From 3bf6160326bca577527d24e86bdd75ad581fc486 Mon Sep 17 00:00:00 2001 From: song Date: Sat, 3 Oct 2026 22:53:19 +0800 Subject: [PATCH 1/4] docs(rfc): specify the lease-fenced delegation stop contract Record the design decision for stopping one delegated operation: the execution's own canonical lease release is the only fence, drain is an observation rather than a settlement condition, and hard_lease authority is required. The entry measures why #5308 could not converge (six independently written settlement facts) and what main already proves (#5436, #5466 and the real revocation test), so the implementation PR has an accepted specification to be reviewed against. Co-Authored-By: Claude Fable 5.1 Signed-off-by: song --- docs/architecture/rfcs/STATUS.md | 2 +- docs/architecture/rfcs/STATUS.zh-CN.md | 2 +- .../2026-10-03-delegation-stop-lease-fence.md | 124 ++++++++++++++++++ ...10-03-delegation-stop-lease-fence.zh-CN.md | 97 ++++++++++++++ 4 files changed, 223 insertions(+), 2 deletions(-) create mode 100644 docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.md create mode 100644 docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.zh-CN.md diff --git a/docs/architecture/rfcs/STATUS.md b/docs/architecture/rfcs/STATUS.md index c16dd68b3a..6a3df593d2 100644 --- a/docs/architecture/rfcs/STATUS.md +++ b/docs/architecture/rfcs/STATUS.md @@ -61,7 +61,7 @@ appendix may keep dated history, but no dated log heading may precede it. | [RFC: Research Exploration Control Plane v0](research-exploration-control-plane-v0.md) | Accepted | none | — | | [RFC: Semantic Vocabulary Convergence and Commit-Time Drift Checks (v0)](semantic-vocabulary-convergence-v0.md) | Accepted | none | [5 entries](ledger/semantic-vocabulary-convergence-v0/) | | [RFC: Shared Goal Alignment and Governed Amendment Protocol (v0)](shared-goal-alignment-and-governed-amendment-v0.md) | Accepted | none | [2 entries](ledger/shared-goal-alignment-and-governed-amendment-v0/) | -| [RFC: LoopX Shared Control-Plane Authority and Pluggable State Providers (v0)](shared-goal-authority-state-provider-v0.md) | Accepted | none | [23 entries](ledger/shared-goal-authority-state-provider-v0/) | +| [RFC: LoopX Shared Control-Plane Authority and Pluggable State Providers (v0)](shared-goal-authority-state-provider-v0.md) | Accepted | none | [24 entries](ledger/shared-goal-authority-state-provider-v0/) | | [RFC: Single-Owner Local Daemon (v0)](single-owner-local-daemon-v0.md) | Accepted | none | — | | [RFC: TypeScript Control-Plane Migration Direction v0](typescript-control-plane-migration-v0.md) | Accepted | none | [14 entries](ledger/typescript-control-plane-migration-v0/) | diff --git a/docs/architecture/rfcs/STATUS.zh-CN.md b/docs/architecture/rfcs/STATUS.zh-CN.md index fe9189d5b8..33c158d11e 100644 --- a/docs/architecture/rfcs/STATUS.zh-CN.md +++ b/docs/architecture/rfcs/STATUS.zh-CN.md @@ -58,7 +58,7 @@ | [RFC:研究型探索控制面 v0](research-exploration-control-plane-v0.zh-CN.md) | 已接受 | 无 | — | | [RFC:语义词表收敛与提交期漂移检查(v0)](semantic-vocabulary-convergence-v0.zh-CN.md) | 已接受 | 无 | [5 条](ledger/semantic-vocabulary-convergence-v0/) | | [RFC:共享 Goal 对齐与受治理 Amendment 协议(v0)](shared-goal-alignment-and-governed-amendment-v0.zh-CN.md) | 已接受 | 无 | [2 条](ledger/shared-goal-alignment-and-governed-amendment-v0/) | -| [RFC:LoopX 共享控制面权威与可插拔状态 Provider(v0)](shared-goal-authority-state-provider-v0.zh-CN.md) | 已接受 | 无 | [23 条](ledger/shared-goal-authority-state-provider-v0/) | +| [RFC:LoopX 共享控制面权威与可插拔状态 Provider(v0)](shared-goal-authority-state-provider-v0.zh-CN.md) | 已接受 | 无 | [24 条](ledger/shared-goal-authority-state-provider-v0/) | | [RFC: Single-Owner Local Daemon (v0)](single-owner-local-daemon-v0.md) | 已接受 | none | — | | [RFC:LoopX 控制面 TypeScript 渐进迁移方向 v0](typescript-control-plane-migration-v0.zh-CN.md) | 已接受 | 无 | [14 条](ledger/typescript-control-plane-migration-v0/) | diff --git a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.md b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.md new file mode 100644 index 0000000000..fb936fa90d --- /dev/null +++ b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.md @@ -0,0 +1,124 @@ +# Delegated operation stop: the canonical lease is the only fence + +- Baseline: `e55489c77`, measured October 3, 2026. +- Outcome: overall roadmap S4 ("restart/cancel/drain/stop retain work and + fence old executors") and the R2 bounded single-operation stop; the + revocation half of delivery 2 in the + [September 27 host-supervision plan](2026-09-27-host-supervision.md) + ("cancellation on expiry/reclaim/revocation"). No provider, capability, + configuration surface or lease vocabulary is introduced. +- This entry is the specification a follow-up implementation PR is built + against. It records a design decision and the measurements behind it; it + does not claim the stop surface has shipped. +- [中文](2026-10-03-delegation-stop-lease-fence.zh-CN.md). + +## What was measured + +The closed [#5308](https://github.com/loopx-project/loopx/pull/5308) tried to +ship the same user outcome and received sixteen maintainer reviews in four +days, fifteen of them `REQUEST_CHANGES`. Nine of its blocking findings were +instances of one structural property: the receipt's terminal `settled` was a +read-side conjunction of facts written by six independent writers (stop +sidecar acknowledgement, operation lock probe, Turn lane holder record, inner +Host process-group record, outer CLI process-group record, canonical lease), +and each pair of writers has an interleaving window. Each repair added another +fact or another lock; each review found another pair. The branch merged +`main` fifteen times while the same lease owner changed twelve times on +`main`. Its final head passed 147 selected real-process tests and was closed +under an unresolvable historical-attribution hold. + +`main` already carries the fence that PR was emulating with file locks: + +- `Delegations._complete_delegated_todo` refuses to commit without the + execution's acquired lease and completes through the canonical lease CAS + ([#5466](https://github.com/loopx-project/loopx/pull/5466)). +- `runLeasedHostProcess` re-proves the original owner/key/epoch at + `min(30 s, remaining/2)` and cancels the delegated CLI, including its nested + Host, when current proof is lost; the forced group termination follows a + six-second grace ([#5436](https://github.com/loopx-project/loopx/pull/5436)). +- `tests/test_delegation_lease_lifetime.py::test_real_revocation_or_new_execution_stops_nested_host_without_acceptance` + proves on real File and SQLite authority that releasing that lease stops the + nested Host and its descendants before the worker returns, leaves the Todo + open, and that retrying the operation neither reacquires the old execution + nor launches the Host again. + +A replay of the retired acquire receipt is the only way the old execution key +can reach the authority again. The acquire receipt identity is deterministic +in `(goal_id, todo_id, owner, idempotency_key)`, and replay requires current +proof, so a released lease retires its key permanently without a new status. + +## Contract + +1. **Scope.** One authorized, bound delegated operation on the local host + authority. Not a team or Goal stop, not coordinator pause, not cross-host + signalling, not a frontend control beyond a recorded-state label. +2. **Intent.** `stop` is the explicit intent of the binding's requester for + one operation. It is persisted beside the operation record before any + fence write, carries the requester identity and one stable `stop_id`, and + cannot be inferred from a signal, a timeout or progress. Repeated stops + return the same receipt. +3. **Fence.** The execution's own canonical hard lease + (`owner`, `idempotency_key`, `lease_epoch`) is the only fence. The + Delegations host releases it through the existing canonical lifecycle with + a CAS on the current version, retrying only a version-mismatch race. This + is the same trust the host already exercises when it claims, renews and + completes on the member's behalf. After release the authority rejects + every renewal, completion CAS and acquire replay from that execution; late + Todo completion and result acceptance are impossible by construction. No + file lock, worker acknowledgement, lane probe or process-group record is + part of the guarantee. +4. **Receipt.** The typed TypeScript owner derives one phase from current + facts on every read; no phase is persisted. + - `requested`: intent persisted, the execution has not yet exposed a lease + to release. Read again; the worker observes the intent before it + launches a Host. + - `revoked`: the release committed, or the execution is already fenced by + another epoch or by expiry. Safe to continue the Todo with a new + operation; the old execution cannot commit any canonical effect. + - `drained`: additionally, the operation recorded its `stopped` + observation with `host_supervision` of `returned` (the leased supervisor + returned after proof loss) or `not_launched` (no Host was launched). + - `noop`: the operation was `accepted` or `rejected` before the fence took + effect. Its prior conclusion stands and nothing is written. + Drain is an observation, never a settlement condition. A dead worker + leaves `revoked` with `host_supervision: unobserved`; later green reads do + not upgrade it. +5. **Worker observation.** The worker checks the intent before acquiring a + lease and again before launching a Host, releases its own lease on either + checkpoint, and records `stopped` after any supervised execution returns + while the intent exists. Those checkpoints avoid wasted work; the fence, + not the checkpoint, is the guarantee. +6. **Authority mode.** Stop requires the Goal's canonical `hard_lease` mode. + On `legacy` or `soft_claim` authority there is no execution lease and + therefore no fence; `stop --execute` is refused before any write with a + reason naming the mode. Promoting the Goal is the enabling step. +7. **Lifecycle.** A stopped operation refuses `resume`; continuing requires a + new operation, which acquires a new lease epoch. Stop never completes the + Todo, settles the Goal or changes an accepted result. +8. **Drain latency.** Bounded by the supervisor's renewal cadence plus its + grace: at most about thirty-six seconds after the release commits, under + the existing supervisor. No signal accelerator is added in this slice. + Nested Host cleanup after a forced group kill remains the existing + supervisor boundary from #5436 and is not re-proven here. +9. **Surfaces.** CLI `delegation stop --execute` and MCP `stop_delegation` + share `Delegations.stop`; `read`, `wait` and the inventory expose the + receipt and the `stopped` observation. The dashboard shows a recorded + stop, not a claim that execution resources were released. + +## Decisions taken here + +- **D1, hard-lease only.** The alternative is a second linearization + mechanism for unleased routes, which is the design that failed above. +- **D2, release instead of a new `revoked` lease status.** A new status would + extend a vocabulary consumed by lifecycle, proof, retirement, migration and + recovery owners; release already retires the key, as measured. +- **D3, drain reported, not required.** Requiring it recreated every + process-attribution window in #5308. + +## What this entry does not establish + +The stop surface is not implemented by this entry. Windows native drain, +PostgreSQL re-qualification, cross-host stop, Lark controls, whole-team stop +and installed-product acceptance are outside the slice. Lease records are +opaque JSON to the File, SQLite and PostgreSQL providers, so no provider +change is expected, but that is a reviewed claim of the implementation PR. diff --git a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.zh-CN.md b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.zh-CN.md new file mode 100644 index 0000000000..a4da1e3c5f --- /dev/null +++ b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.zh-CN.md @@ -0,0 +1,97 @@ +# 停止委派操作:canonical lease 是唯一的 fence + +- 基线:`e55489c77`,2026 年 10 月 3 日测量。 +- 结果:总体 roadmap S4("restart/cancel/drain/stop 保留工作并 fence 旧执行者") + 与 R2 的有界单操作停止;对应 + [9 月 27 日 host-supervision 计划](2026-09-27-host-supervision.zh-CN.md) + 中交付 2 的撤销部分("到期/回收/撤销时取消")。不引入 provider、 + capability、配置面或 lease 词表。 +- 本条目是后续实现 PR 所依据的规格。它记录一个设计决定及其背后的测量, + 不声称停止能力已经交付。 +- [English](2026-10-03-delegation-stop-lease-fence.md)。 + +## 测量到什么 + +已关闭的 [#5308](https://github.com/loopx-project/loopx/pull/5308) 试图交付同一 +用户结果,四天内收到十六份维护者评审,其中十五份为 `REQUEST_CHANGES`。 +它的九个阻塞发现属于同一个结构性性质:回执的终态 `settled` 是对六个独立写者 +(stop sidecar 的 ACK、operation lock 探测、Turn lane holder 记录、内层 Host +进程组记录、外层 CLI 进程组记录、canonical lease)所写事实的读侧合取,而任意 +两个写者之间都存在交错窗口。每次修复再加一个事实或一把锁,每轮评审再找到一对。 +该分支合并了十五次 `main`,同期 `main` 上同一 lease owner 改动了十二次。最终 +head 通过了 147 项选定的真实进程测试,并在一个无法闭合的"历史归因"hold 下关闭。 + +`main` 上已经存在那个 PR 用文件锁模拟的 fence: + +- `Delegations._complete_delegated_todo` 没有本次执行已取得的 lease 就拒绝提交, + 并通过 canonical lease CAS 完成 + ([#5466](https://github.com/loopx-project/loopx/pull/5466))。 +- `runLeasedHostProcess` 以 `min(30 s, remaining/2)` 的节奏重新证明原 + owner/key/epoch,证明丢失时取消委派 CLI 及其嵌套 Host;强制进程组终止前有 + 六秒 grace([#5436](https://github.com/loopx-project/loopx/pull/5436))。 +- `tests/test_delegation_lease_lifetime.py::test_real_revocation_or_new_execution_stops_nested_host_without_acceptance` + 在真实 File 与 SQLite authority 上证明:释放该 lease 后,嵌套 Host 及其子进程 + 在 worker 返回前停止,Todo 保持未完成,重试该操作既不会重新取得旧执行也不会 + 再次启动 Host。 + +旧执行 key 再次到达 authority 的唯一途径是重放已退役的 acquire 回执。acquire +回执身份由 `(goal_id, todo_id, owner, idempotency_key)` 确定性生成,而重放要求 +当前证明,因此 lease 一旦 released,其 key 就永久退役,不需要新状态。 + +## 契约 + +1. **范围。** 本机 authority 上一个经授权、有 binding 的委派操作。不是团队或 + Goal 停止,不是协调者暂停,不是跨宿主信号,前端只有一个记录状态标签。 +2. **意图。** `stop` 是 binding 的 requester 对一个操作的明确意图。它在任何 + fence 写入之前持久化在操作记录旁,携带 requester 身份和一个稳定的 + `stop_id`,不能由信号、超时或进度推导。重复 stop 返回同一回执。 +3. **Fence。** 该执行自己的 canonical hard lease(`owner`、`idempotency_key`、 + `lease_epoch`)是唯一的 fence。Delegations host 通过既有 canonical lifecycle + 以当前 version 的 CAS 释放它,只重试 version 不匹配这一种竞争。这与 host + 代表成员 claim、renew、complete 时行使的是同一份信任。释放之后,authority + 拒绝该执行的一切续期、完成 CAS 与 acquire 重放;迟到的 Todo 完成与结果 + 验收在构造上不可能。文件锁、worker ACK、lane 探测、进程组记录都不是保证的 + 一部分。 +4. **回执。** 类型化的 TypeScript owner 在每次读取时由当前事实推导一个 + phase;不持久化 phase。 + - `requested`:意图已持久化,执行尚未暴露可释放的 lease。再次读取;worker + 会在启动 Host 前观察到该意图。 + - `revoked`:释放已提交,或该执行已被另一个 epoch 或到期 fence。可以用新 + 操作安全继续该 Todo;旧执行无法提交任何 canonical 效果。 + - `drained`:在此之上,操作记录了 `stopped` 观察,且 `host_supervision` 为 + `returned`(leased supervisor 在证明丢失后返回)或 `not_launched`(未 + 启动 Host)。 + - `noop`:操作在 fence 生效前已经 `accepted` 或 `rejected`。原结论保留, + 不写任何内容。 + Drain 是观察,绝不是结算条件。worker 已死时停留在 `revoked` 且 + `host_supervision: unobserved`;之后的绿色读取不会升级它。 +5. **Worker 观察。** worker 在取得 lease 前和启动 Host 前各检查一次意图,任一 + 检查点命中时释放自己的 lease;在意图存在时任何被监督执行返回后记录 + `stopped`。这些检查点避免浪费工作;保证来自 fence,不来自检查点。 +6. **Authority 模式。** stop 要求 Goal 的 canonical `hard_lease` 模式。在 + `legacy` 或 `soft_claim` authority 上没有执行 lease,因此没有 fence; + `stop --execute` 在任何写入前被拒绝,原因中写明模式。提升 Goal 是启用步骤。 +7. **生命周期。** 已停止的操作拒绝 `resume`;继续需要新操作,它会取得新的 + lease epoch。stop 永不完成 Todo、不结算 Goal、不改变已验收结果。 +8. **Drain 延迟。** 由 supervisor 的续期节奏加 grace 界定:在既有 supervisor + 下,释放提交后最多约三十六秒。本切片不增加信号加速路径。强制进程组终止后 + 的嵌套 Host 清理仍是 #5436 的既有 supervisor 边界,此处不重新证明。 +9. **入口。** CLI `delegation stop --execute` 与 MCP `stop_delegation` 共用 + `Delegations.stop`;`read`、`wait` 与 inventory 暴露回执与 `stopped` 观察。 + dashboard 展示"停止已登记",不声称执行资源已释放。 + +## 此处作出的决定 + +- **D1,仅限 hard lease。** 替代方案是为无 lease 路由再建一套线性化机制, + 正是上面失败的设计。 +- **D2,用 release 而非新增 `revoked` lease 状态。** 新状态会扩展被 + lifecycle、proof、retirement、migration 与 recovery 多个 owner 消费的词表; + 如测量所示,release 已经使 key 退役。 +- **D3,drain 只报告,不要求。** 要求它会重现 #5308 中的每一个进程归属窗口。 + +## 本条目不建立什么 + +停止能力不由本条目实现。Windows 原生 drain、PostgreSQL 重新资格化、跨宿主 +停止、Lark 控件、整团队停止与安装态验收都在切片之外。lease 记录对 File、 +SQLite、PostgreSQL provider 是不透明 JSON,预计不需要 provider 改动,但这是 +实现 PR 需要评审的声明。 From a91b3d1b85b04560d0c1eb8cb9a30ab992ee6693 Mon Sep 17 00:00:00 2001 From: song Date: Sun, 4 Oct 2026 07:25:47 +0800 Subject: [PATCH 2/4] docs(rfc): bound stop drain by the proven expiry, not a fixed interval Contract 8 promised drain within about thirty-six seconds of the release. The existing supervisor meets that only on its nominal path: a renewal in flight finishes on its own clock (each lease command may run 60 seconds and a lost reply is retried once), so slow or lost authority replies delay cancellation until the last proven expiry, at most one lease TTL after the release. State that boundary in both languages, keep revoked and drained distinct, and list the real-process qualification the implementation PR owes: the healthy revocation control, a release during an in-flight renewal with a long TTL and a delayed reply, and lost authority replies. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: song --- .../2026-10-03-delegation-stop-lease-fence.md | 56 ++++++++++++++++--- ...10-03-delegation-stop-lease-fence.zh-CN.md | 39 +++++++++++-- 2 files changed, 83 insertions(+), 12 deletions(-) diff --git a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.md b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.md index fb936fa90d..863ac5d66c 100644 --- a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.md +++ b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.md @@ -34,8 +34,14 @@ under an unresolvable historical-attribution hold. ([#5466](https://github.com/loopx-project/loopx/pull/5466)). - `runLeasedHostProcess` re-proves the original owner/key/epoch at `min(30 s, remaining/2)` and cancels the delegated CLI, including its nested - Host, when current proof is lost; the forced group termination follows a - six-second grace ([#5436](https://github.com/loopx-project/loopx/pull/5436)). + Host, when a renewal is rejected, current proof is lost or the last proven + `expires_at` passes; the forced group termination follows a six-second + grace ([#5436](https://github.com/loopx-project/loopx/pull/5436)). Each + lease command may run for 60 seconds and a lost reply is retried once with + the same intent, while the proven expiry stays armed throughout: + `tests/control_plane/test_leased_host_process.py::test_real_renewal_faults_keep_original_deadline_and_identity` + shows on real File and SQLite authority that a hung renewal does not keep + the Host running past that expiry. - `tests/test_delegation_lease_lifetime.py::test_real_revocation_or_new_execution_stops_nested_host_without_acceptance` proves on real File and SQLite authority that releasing that lease stops the nested Host and its descendants before the worker returns, leaves the Todo @@ -95,11 +101,25 @@ proof, so a released lease retires its key permanently without a new status. 7. **Lifecycle.** A stopped operation refuses `resume`; continuing requires a new operation, which acquires a new lease epoch. Stop never completes the Todo, settles the Goal or changes an accepted result. -8. **Drain latency.** Bounded by the supervisor's renewal cadence plus its - grace: at most about thirty-six seconds after the release commits, under - the existing supervisor. No signal accelerator is added in this slice. - Nested Host cleanup after a forced group kill remains the existing - supervisor boundary from #5436 and is not re-proven here. +8. **Drain latency.** Revocation and drain run on different clocks. The + fence holds from the moment the release commits. Physical drain follows + when the existing supervisor cancels the delegated CLI, on the first of a + rejected renewal, a renewal whose command fails twice, a current-proof + read that fails or no longer proves the execution, or the last proven + `expires_at`. Only that expiry bounds drain unconditionally: it is at most + one lease TTL after the release commits, because nothing renews after the + release, and the six-second grace and forced group kill follow it. About + thirty-six seconds (a renewal interval of at most 30 seconds plus the + grace, plus one lease command) is the nominal path only: no renewal is in + flight when the release commits and the authority answers promptly. A + renewal already in flight finishes on its own clock, so slow or lost + authority replies move drain toward the expiry bound. `revoked` therefore + never implies drain; treat execution resources as released only on + `drained`. This slice adds no release-to-drain deadline and no signal + accelerator; a tighter unconditional bound would need a supervisor-owned + cancellation deadline with its own real-process qualification. Nested + Host cleanup after a forced group kill remains the existing supervisor + boundary from #5436 and is not re-proven here. 9. **Surfaces.** CLI `delegation stop --execute` and MCP `stop_delegation` share `Delegations.stop`; `read`, `wait` and the inventory expose the receipt and the `stopped` observation. The dashboard shows a recorded @@ -115,6 +135,28 @@ proof, so a released lease retires its key permanently without a new status. - **D3, drain reported, not required.** Requiring it recreated every process-attribution window in #5308. +## Qualification the implementation owes + +The implementation PR shows each of these on real processes against File and +SQLite authority, records the observed release-to-drain durations, and never +asserts the nominal thirty-six seconds: + +- **Healthy revocation, the positive control.** + `test_real_revocation_or_new_execution_stops_nested_host_without_acceptance` + keeps passing: releasing the lease stops the nested Host and its + descendants before the worker returns, and the Todo stays open. +- **Release while a renewal is in flight.** With a long TTL (for example 180 + seconds), the stop's release commits after a renewal has started and while + that renewal's authority reply is delayed. The receipt is `revoked` once + the release commits and does not report `drained` while the nested Host is + still running. No renewal, Todo completion or acceptance from the old + execution commits, and drain arrives after the delayed rejection, no later + than the last proven expiry plus the grace. +- **Lost authority replies.** When the renewal command fails twice or never + answers within its timeout, the same receipt and fence properties hold, + and the supervisor cancels no later than the last proven expiry plus the + grace. + ## What this entry does not establish The stop surface is not implemented by this entry. Windows native drain, diff --git a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.zh-CN.md b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.zh-CN.md index a4da1e3c5f..2762dcd48b 100644 --- a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.zh-CN.md +++ b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.zh-CN.md @@ -27,8 +27,12 @@ head 通过了 147 项选定的真实进程测试,并在一个无法闭合的" 并通过 canonical lease CAS 完成 ([#5466](https://github.com/loopx-project/loopx/pull/5466))。 - `runLeasedHostProcess` 以 `min(30 s, remaining/2)` 的节奏重新证明原 - owner/key/epoch,证明丢失时取消委派 CLI 及其嵌套 Host;强制进程组终止前有 - 六秒 grace([#5436](https://github.com/loopx-project/loopx/pull/5436))。 + owner/key/epoch,在续期被拒绝、当前证明丢失或最后已证明的 `expires_at` 到达时 + 取消委派 CLI 及其嵌套 Host;强制进程组终止前有六秒 grace + ([#5436](https://github.com/loopx-project/loopx/pull/5436))。每个 lease 命令 + 最长运行 60 秒,回复丢失时以同一意图重试一次,而已证明的到期计时始终有效: + `tests/control_plane/test_leased_host_process.py::test_real_renewal_faults_keep_original_deadline_and_identity` + 在真实 File 与 SQLite authority 上证明,续期挂起不会让 Host 运行到该到期之后。 - `tests/test_delegation_lease_lifetime.py::test_real_revocation_or_new_execution_stops_nested_host_without_acceptance` 在真实 File 与 SQLite authority 上证明:释放该 lease 后,嵌套 Host 及其子进程 在 worker 返回前停止,Todo 保持未完成,重试该操作既不会重新取得旧执行也不会 @@ -73,9 +77,18 @@ head 通过了 147 项选定的真实进程测试,并在一个无法闭合的" `stop --execute` 在任何写入前被拒绝,原因中写明模式。提升 Goal 是启用步骤。 7. **生命周期。** 已停止的操作拒绝 `resume`;继续需要新操作,它会取得新的 lease epoch。stop 永不完成 Todo、不结算 Goal、不改变已验收结果。 -8. **Drain 延迟。** 由 supervisor 的续期节奏加 grace 界定:在既有 supervisor - 下,释放提交后最多约三十六秒。本切片不增加信号加速路径。强制进程组终止后 - 的嵌套 Host 清理仍是 #5436 的既有 supervisor 边界,此处不重新证明。 +8. **Drain 延迟。** 撤销与 drain 走不同的时钟。release 提交的那一刻 fence 即 + 生效;物理 drain 发生在既有 supervisor 取消委派 CLI 时,取以下最早者:续期被 + 拒绝、续期命令两次失败、当前证明读取失败或不再证明该执行、最后已证明的 + `expires_at` 到达。只有这个到期时刻能无条件地界定 drain:release 之后不会再有 + 续期,所以它最多在 release 提交后一个 lease TTL 到达,随后是六秒 grace 与强制 + 进程组终止。约三十六秒(不超过 30 秒的续期间隔加 grace,再加一次 lease 命令) + 只是名义路径:release 提交时没有正在进行的续期,且 authority 及时响应。已在 + 进行的续期按自己的时钟结束,因此 authority 响应慢或丢失时,drain 会推向到期 + 上界。所以 `revoked` 从不意味着 drain;只有 `drained` 才能视为执行资源已释放。 + 本切片不增加从 release 到 drain 的期限,也不增加信号加速路径;更紧的无条件 + 上界需要由 supervisor 自己拥有的取消期限及其真实进程验收。强制进程组终止后的 + 嵌套 Host 清理仍是 #5436 的既有 supervisor 边界,此处不重新证明。 9. **入口。** CLI `delegation stop --execute` 与 MCP `stop_delegation` 共用 `Delegations.stop`;`read`、`wait` 与 inventory 暴露回执与 `stopped` 观察。 dashboard 展示"停止已登记",不声称执行资源已释放。 @@ -89,6 +102,22 @@ head 通过了 147 项选定的真实进程测试,并在一个无法闭合的" 如测量所示,release 已经使 key 退役。 - **D3,drain 只报告,不要求。** 要求它会重现 #5308 中的每一个进程归属窗口。 +## 实现 PR 必须给出的验收 + +实现 PR 需在 File 与 SQLite authority 上以真实进程逐项证明以下各点,记录观测到的 +release 到 drain 耗时,且从不断言名义上的三十六秒: + +- **健康撤销,作为正向对照。** + `test_real_revocation_or_new_execution_stops_nested_host_without_acceptance` + 继续通过:释放 lease 后,嵌套 Host 及其子进程在 worker 返回前停止,Todo 保持 + 未完成。 +- **续期进行中时 release。** 使用长 TTL(例如 180 秒),在一次续期已开始、且其 + authority 回复被延迟时提交 stop 的 release。release 提交后回执即为 `revoked`, + 嵌套 Host 仍在运行时不报告 `drained`。旧执行的续期、Todo 完成与验收都不能提交; + drain 在延迟的拒绝到达后发生,且不晚于最后已证明的到期加 grace。 +- **authority 回复丢失。** 续期命令两次失败或在超时内始终无回复时,回执与 fence + 的性质不变,supervisor 不晚于最后已证明的到期加 grace 取消执行。 + ## 本条目不建立什么 停止能力不由本条目实现。Windows 原生 drain、PostgreSQL 重新资格化、跨宿主 From dbdeb9895afb323f0a5bd4d98e49ce6bc8185631 Mon Sep 17 00:00:00 2001 From: song Date: Sun, 4 Oct 2026 07:43:45 +0800 Subject: [PATCH 3/4] docs(rfc): distinguish stop proposal from current review contract Signed-off-by: song --- .../2026-10-03-delegation-stop-lease-fence.md | 63 ++++++++++++------- ...10-03-delegation-stop-lease-fence.zh-CN.md | 47 +++++++++----- 2 files changed, 69 insertions(+), 41 deletions(-) diff --git a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.md b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.md index 863ac5d66c..02eb48843d 100644 --- a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.md +++ b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.md @@ -1,4 +1,4 @@ -# Delegated operation stop: the canonical lease is the only fence +# Proposed delegated operation stop: canonical lease revocation - Baseline: `e55489c77`, measured October 3, 2026. - Outcome: overall roadmap S4 ("restart/cancel/drain/stop retain work and @@ -7,27 +7,40 @@ [September 27 host-supervision plan](2026-09-27-host-supervision.md) ("cancellation on expiry/reclaim/revocation"). No provider, capability, configuration surface or lease vocabulary is introduced. -- This entry is the specification a follow-up implementation PR is built - against. It records a design decision and the measurements behind it; it - does not claim the stop surface has shipped. +- Status: proposed alternative, pending an explicit maintainer decision. + This entry neither replaces the stop contract under review in #5308 nor + claims that the stop surface has shipped. Its implementation qualification + applies only if this alternative is selected. - [中文](2026-10-03-delegation-stop-lease-fence.zh-CN.md). ## What was measured -The closed [#5308](https://github.com/loopx-project/loopx/pull/5308) tried to -ship the same user outcome and received sixteen maintainer reviews in four -days, fifteen of them `REQUEST_CHANGES`. Nine of its blocking findings were -instances of one structural property: the receipt's terminal `settled` was a -read-side conjunction of facts written by six independent writers (stop -sidecar acknowledgement, operation lock probe, Turn lane holder record, inner -Host process-group record, outer CLI process-group record, canonical lease), -and each pair of writers has an interleaving window. Each repair added another -fact or another lock; each review found another pair. The branch merged -`main` fifteen times while the same lease owner changed twelve times on -`main`. Its final head passed 147 selected real-process tests and was closed -under an unresolvable historical-attribution hold. - -`main` already carries the fence that PR was emulating with file locks: +[#5308](https://github.com/loopx-project/loopx/pull/5308) is open again. Its +[October 3 review](https://github.com/loopx-project/loopx/pull/5308#pullrequestreview-5401677786) +requires canonical lease-obligation readback (R1), complete execution drain +observation in the existing Host boundary (R2), and a typed separation between +next actions and final receipts (R3). That review explicitly removes historical +failure-by-failure attribution as a merge prerequisite. Earlier failures remain +historical evidence, not proof that its current design cannot be repaired. + +The two proposals address the same caller outcome with different guarantees: + +| Boundary | #5308 under review | This proposed alternative | +| --- | --- | --- | +| Ordering | Prove the original execution drained before releasing its lease | Revoke the lease first; observe drain separately | +| Completion feedback | `settled` requires ACK, released holders, Host drain and resolved lease obligation | `revoked` proves loss of commit authority; only `drained` reports execution exit | +| Authority modes | Includes existing unleased routes with the dispatch fence | Requires canonical `hard_lease`; refuses unleased routes | + +These are alternative public contracts, not interchangeable phase names. Do +not implement both under the same `delegation stop` / `stop_delegation` entry +points. The current #5308 repair follows R1–R3. Selecting this alternative would +require an explicit supersession decision, the CLI/MCP/readback/docs companions, +and the implementation evidence below; merging a design note alone does not +change the runtime contract. Neither approach establishes whole-team or Goal +completion, and neither makes revocation proof of physical drain. + +At the measured baseline, `main` already provides reusable lease fencing and +supervision: - `Delegations._complete_delegated_todo` refuses to commit without the execution's acquired lease and completes through the canonical lease CAS @@ -53,7 +66,7 @@ can reach the authority again. The acquire receipt identity is deterministic in `(goal_id, todo_id, owner, idempotency_key)`, and replay requires current proof, so a released lease retires its key permanently without a new status. -## Contract +## Proposed contract 1. **Scope.** One authorized, bound delegated operation on the local host authority. Not a team or Goal stop, not coordinator pause, not cross-host @@ -125,15 +138,17 @@ proof, so a released lease retires its key permanently without a new status. receipt and the `stopped` observation. The dashboard shows a recorded stop, not a claim that execution resources were released. -## Decisions taken here +## Decisions proposed here -- **D1, hard-lease only.** The alternative is a second linearization - mechanism for unleased routes, which is the design that failed above. +- **D1, hard-lease only.** This reduces the stop guarantee to the canonical + lease fence, at the cost of refusing existing unleased routes. #5308 instead + retains their dispatch-fence path; that tradeoff needs a maintainer decision. - **D2, release instead of a new `revoked` lease status.** A new status would extend a vocabulary consumed by lifecycle, proof, retirement, migration and recovery owners; release already retires the key, as measured. -- **D3, drain reported, not required.** Requiring it recreated every - process-attribution window in #5308. +- **D3, drain reported, not required for revocation.** This makes authority + loss observable while processes may still be running. It does not satisfy + #5308's `settled` promise or qualify immediate resource handoff. ## Qualification the implementation owes diff --git a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.zh-CN.md b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.zh-CN.md index 2762dcd48b..83788212a4 100644 --- a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.zh-CN.md +++ b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.zh-CN.md @@ -1,4 +1,4 @@ -# 停止委派操作:canonical lease 是唯一的 fence +# 委派停止替代提案:canonical lease 撤销 - 基线:`e55489c77`,2026 年 10 月 3 日测量。 - 结果:总体 roadmap S4("restart/cancel/drain/stop 保留工作并 fence 旧执行者") @@ -6,22 +6,34 @@ [9 月 27 日 host-supervision 计划](2026-09-27-host-supervision.zh-CN.md) 中交付 2 的撤销部分("到期/回收/撤销时取消")。不引入 provider、 capability、配置面或 lease 词表。 -- 本条目是后续实现 PR 所依据的规格。它记录一个设计决定及其背后的测量, - 不声称停止能力已经交付。 +- 状态:替代提案,待维护者明确决定。本条目不替换 #5308 正在评审的停止契约, + 也不声称停止能力已交付。只有选定本替代方案后,下述实现验收才适用。 - [English](2026-10-03-delegation-stop-lease-fence.md)。 ## 测量到什么 -已关闭的 [#5308](https://github.com/loopx-project/loopx/pull/5308) 试图交付同一 -用户结果,四天内收到十六份维护者评审,其中十五份为 `REQUEST_CHANGES`。 -它的九个阻塞发现属于同一个结构性性质:回执的终态 `settled` 是对六个独立写者 -(stop sidecar 的 ACK、operation lock 探测、Turn lane holder 记录、内层 Host -进程组记录、外层 CLI 进程组记录、canonical lease)所写事实的读侧合取,而任意 -两个写者之间都存在交错窗口。每次修复再加一个事实或一把锁,每轮评审再找到一对。 -该分支合并了十五次 `main`,同期 `main` 上同一 lease owner 改动了十二次。最终 -head 通过了 147 项选定的真实进程测试,并在一个无法闭合的"历史归因"hold 下关闭。 +[#5308](https://github.com/loopx-project/loopx/pull/5308) 已重新打开。 +[10 月 3 日最新评审](https://github.com/loopx-project/loopx/pull/5308#pullrequestreview-5401677786) +要求从 canonical authority 读回租约义务(R1)、由既有 Host 边界提供完整执行的 +退出观察(R2),以及在类型化 owner 中区分下一步动作和最终回执(R3)。该评审 +已明确取消逐次追查历史失败原因这一合入前置条件。旧失败仍是历史证据,不能据此 +断言当前设计无法修复。 -`main` 上已经存在那个 PR 用文件锁模拟的 fence: +两份方案服务于同一调用者结果,但保证不同: + +| 边界 | #5308 正在评审的实现 | 本替代提案 | +| --- | --- | --- | +| 顺序 | 先证明原执行退出,再释放其租约 | 先撤销租约,单独观察进程退出 | +| 完成反馈 | `settled` 要求 ACK、holder 释放、Host 退出和租约义务已解析 | `revoked` 证明提交权限失效;只有 `drained` 报告执行退出 | +| authority 模式 | 通过 dispatch fence 保留既有无租约路径 | 要求 canonical `hard_lease`,拒绝无租约路径 | + +这是两份替代的公共契约,不是可以互换的 phase 名称,不能同时实现在同一个 +`delegation stop` / `stop_delegation` 入口下。当前 #5308 的修复遵循 R1–R3。 +选择本替代方案需要明确的替代决定、CLI/MCP/读回/文档的配套修改,以及下述实现 +验收;仅合入设计文档不会改变运行时契约。两者均不代表团队或 Goal 已完成, +也不能用撤销权限证明物理进程已退出。 + +在测量基线上,`main` 已提供可复用的租约 fence 和 supervision: - `Delegations._complete_delegated_todo` 没有本次执行已取得的 lease 就拒绝提交, 并通过 canonical lease CAS 完成 @@ -42,7 +54,7 @@ head 通过了 147 项选定的真实进程测试,并在一个无法闭合的" 回执身份由 `(goal_id, todo_id, owner, idempotency_key)` 确定性生成,而重放要求 当前证明,因此 lease 一旦 released,其 key 就永久退役,不需要新状态。 -## 契约 +## 提议的契约 1. **范围。** 本机 authority 上一个经授权、有 binding 的委派操作。不是团队或 Goal 停止,不是协调者暂停,不是跨宿主信号,前端只有一个记录状态标签。 @@ -93,14 +105,15 @@ head 通过了 147 项选定的真实进程测试,并在一个无法闭合的" `Delegations.stop`;`read`、`wait` 与 inventory 暴露回执与 `stopped` 观察。 dashboard 展示"停止已登记",不声称执行资源已释放。 -## 此处作出的决定 +## 本条目提议的决定 -- **D1,仅限 hard lease。** 替代方案是为无 lease 路由再建一套线性化机制, - 正是上面失败的设计。 +- **D1,仅限 hard lease。** 把停止保证限定在 canonical lease fence,代价是 + 拒绝既有无租约路径。#5308 保留这些路径的 dispatch fence,取舍需由维护者决定。 - **D2,用 release 而非新增 `revoked` lease 状态。** 新状态会扩展被 lifecycle、proof、retirement、migration 与 recovery 多个 owner 消费的词表; 如测量所示,release 已经使 key 退役。 -- **D3,drain 只报告,不要求。** 要求它会重现 #5308 中的每一个进程归属窗口。 +- **D3,drain 单独报告,不作为撤销的条件。** 允许在进程仍运行时报告提交权限 + 已失效;这不满足 #5308 的 `settled` 承诺,也不证明可以立即交接执行资源。 ## 实现 PR 必须给出的验收 From bd37a9c7ca85dbcb7982bcd1b1d166f2742fab97 Mon Sep 17 00:00:00 2001 From: song Date: Sun, 4 Oct 2026 10:06:16 +0800 Subject: [PATCH 4/4] docs(delegation): require host evidence before claiming drain Signed-off-by: song --- .../2026-10-03-delegation-stop-lease-fence.md | 87 ++++++++++--------- ...10-03-delegation-stop-lease-fence.zh-CN.md | 58 +++++++------ 2 files changed, 80 insertions(+), 65 deletions(-) diff --git a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.md b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.md index 02eb48843d..132a8af72e 100644 --- a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.md +++ b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.md @@ -46,18 +46,19 @@ supervision: execution's acquired lease and completes through the canonical lease CAS ([#5466](https://github.com/loopx-project/loopx/pull/5466)). - `runLeasedHostProcess` re-proves the original owner/key/epoch at - `min(30 s, remaining/2)` and cancels the delegated CLI, including its nested - Host, when a renewal is rejected, current proof is lost or the last proven - `expires_at` passes; the forced group termination follows a six-second + `min(30 s, remaining/2)` and requests cancellation when a renewal is + rejected, current proof is lost or the last proven `expires_at` passes; + the forced group termination follows a six-second grace ([#5436](https://github.com/loopx-project/loopx/pull/5436)). Each lease command may run for 60 seconds and a lost reply is retried once with the same intent, while the proven expiry stays armed throughout: `tests/control_plane/test_leased_host_process.py::test_real_renewal_faults_keep_original_deadline_and_identity` - shows on real File and SQLite authority that a hung renewal does not keep - the Host running past that expiry. + shows on real File and SQLite authority that a hung renewal does not + disarm expiry-driven cancellation in the tested supervisor topology. - `tests/test_delegation_lease_lifetime.py::test_real_revocation_or_new_execution_stops_nested_host_without_acceptance` - proves on real File and SQLite authority that releasing that lease stops the - nested Host and its descendants before the worker returns, leaves the Todo + proves on real File and SQLite authority, with functioning nested + supervision, that releasing that lease stops the nested Host and its + descendants before the worker returns, leaves the Todo open, and that retrying the operation neither reacquires the old execution nor launches the Host again. @@ -85,28 +86,33 @@ proof, so a released lease retires its key permanently without a new status. every renewal, completion CAS and acquire replay from that execution; late Todo completion and result acceptance are impossible by construction. No file lock, worker acknowledgement, lane probe or process-group record is - part of the guarantee. + part of this canonical-write guarantee. It does not undo shell commands, + network requests or other external effects already launched by the Host. 4. **Receipt.** The typed TypeScript owner derives one phase from current facts on every read; no phase is persisted. - `requested`: intent persisted, the execution has not yet exposed a lease to release. Read again; the worker observes the intent before it launches a Host. - `revoked`: the release committed, or the execution is already fenced by - another epoch or by expiry. Safe to continue the Todo with a new - operation; the old execution cannot commit any canonical effect. - - `drained`: additionally, the operation recorded its `stopped` - observation with `host_supervision` of `returned` (the leased supervisor - returned after proof loss) or `not_launched` (no Host was launched). + another epoch or by expiry. The old execution cannot commit effects + guarded by canonical authority. This alone does not qualify overlapping + external work or a resource handoff. + - `drained`: additionally, the existing Host owner proves that the original + execution and every attributed process group have exited, or proves + that no Host was launched and no launch remains possible. A returned + leased supervisor, a `stopped` operation or elapsed grace is insufficient. - `noop`: the operation was `accepted` or `rejected` before the fence took effect. Its prior conclusion stands and nothing is written. Drain is an observation, never a settlement condition. A dead worker - leaves `revoked` with `host_supervision: unobserved`; later green reads do - not upgrade it. + leaves `revoked` with `host_supervision: unobserved`; only complete Host + evidence tied to the original execution can establish drain. An unavailable + or interrupted inner supervisor leaves drain unproven even after outer return. 5. **Worker observation.** The worker checks the intent before acquiring a lease and again before launching a Host, releases its own lease on either checkpoint, and records `stopped` after any supervised execution returns - while the intent exists. Those checkpoints avoid wasted work; the fence, - not the checkpoint, is the guarantee. + while the intent exists. That observation says the worker handled stop; it + does not prove complete drain. Those checkpoints avoid wasted work; the + canonical-write guarantee comes from the lease fence. 6. **Authority mode.** Stop requires the Goal's canonical `hard_lease` mode. On `legacy` or `soft_claim` authority there is no execution lease and therefore no fence; `stop --execute` is refused before any write with a @@ -114,25 +120,19 @@ proof, so a released lease retires its key permanently without a new status. 7. **Lifecycle.** A stopped operation refuses `resume`; continuing requires a new operation, which acquires a new lease epoch. Stop never completes the Todo, settles the Goal or changes an accepted result. -8. **Drain latency.** Revocation and drain run on different clocks. The - fence holds from the moment the release commits. Physical drain follows - when the existing supervisor cancels the delegated CLI, on the first of a - rejected renewal, a renewal whose command fails twice, a current-proof - read that fails or no longer proves the execution, or the last proven - `expires_at`. Only that expiry bounds drain unconditionally: it is at most - one lease TTL after the release commits, because nothing renews after the - release, and the six-second grace and forced group kill follow it. About - thirty-six seconds (a renewal interval of at most 30 seconds plus the - grace, plus one lease command) is the nominal path only: no renewal is in - flight when the release commits and the authority answers promptly. A - renewal already in flight finishes on its own clock, so slow or lost - authority replies move drain toward the expiry bound. `revoked` therefore - never implies drain; treat execution resources as released only on - `drained`. This slice adds no release-to-drain deadline and no signal - accelerator; a tighter unconditional bound would need a supervisor-owned - cancellation deadline with its own real-process qualification. Nested - Host cleanup after a forced group kill remains the existing supervisor - boundary from #5436 and is not re-proven here. +8. **Drain latency.** Revocation and resource exit are separate facts. The + fence holds once release commits. The existing leased supervisor requests + cancellation on rejected renewal, failed current proof or its last proven + expiry; that expiry remains armed while authority replies are in flight. + These are cancellation triggers, not an unconditional deadline for every + nested process to exit. Outer supervisor return and expiry plus six-second + grace do not prove inner drain if a nested supervisor is interrupted or its + cleanup cannot be observed. The roughly thirty-six-second healthy path is + nominal only, requiring promptly answered authority requests, no in-flight + renewal at release and functioning supervision. Slow/lost replies or failed + cleanup must remain visible as `revoked` with unproven drain. Report actual + Host evidence before `drained`; this proposal adds no hard drain deadline, + new cleanup service or second process-lifecycle owner. 9. **Surfaces.** CLI `delegation stop --execute` and MCP `stop_delegation` share `Delegations.stop`; `read`, `wait` and the inventory expose the receipt and the `stopped` observation. The dashboard shows a recorded @@ -165,12 +165,19 @@ asserts the nominal thirty-six seconds: that renewal's authority reply is delayed. The receipt is `revoked` once the release commits and does not report `drained` while the nested Host is still running. No renewal, Todo completion or acceptance from the old - execution commits, and drain arrives after the delayed rejection, no later - than the last proven expiry plus the grace. + execution commits. Observe the existing cancellation trigger separately + from complete Host exit; retain `revoked` whenever drain cannot be proved. - **Lost authority replies.** When the renewal command fails twice or never answers within its timeout, the same receipt and fence properties hold, - and the supervisor cancels no later than the last proven expiry plus the - grace. + and the last proven expiry remains armed for cancellation. A cancellation + observation is not complete nested-process drain. +- **Interrupted nested supervisor.** Pause the inner supervisor after the + actual Host starts, release the original canonical lease, and wait for the + outer call to return. If an independently observed descendant still runs, + including after expiry plus grace, the receipt must remain `revoked` with + unproven drain. Only subsequent complete, original-execution Host evidence + may report `drained`. Retain the healthy-supervisor control and ensure the + fixture cleans its own groups even when the assertion fails. ## What this entry does not establish diff --git a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.zh-CN.md b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.zh-CN.md index 83788212a4..0a4424e120 100644 --- a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.zh-CN.md +++ b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-10-03-delegation-stop-lease-fence.zh-CN.md @@ -40,13 +40,15 @@ ([#5466](https://github.com/loopx-project/loopx/pull/5466))。 - `runLeasedHostProcess` 以 `min(30 s, remaining/2)` 的节奏重新证明原 owner/key/epoch,在续期被拒绝、当前证明丢失或最后已证明的 `expires_at` 到达时 - 取消委派 CLI 及其嵌套 Host;强制进程组终止前有六秒 grace + 请求取消;强制进程组终止前有六秒 grace ([#5436](https://github.com/loopx-project/loopx/pull/5436))。每个 lease 命令 最长运行 60 秒,回复丢失时以同一意图重试一次,而已证明的到期计时始终有效: `tests/control_plane/test_leased_host_process.py::test_real_renewal_faults_keep_original_deadline_and_identity` - 在真实 File 与 SQLite authority 上证明,续期挂起不会让 Host 运行到该到期之后。 + 在真实 File 与 SQLite authority 上证明,在该测试的监督结构内,续期挂起不会 + 撤销由到期时刻触发的取消。 - `tests/test_delegation_lease_lifetime.py::test_real_revocation_or_new_execution_stops_nested_host_without_acceptance` - 在真实 File 与 SQLite authority 上证明:释放该 lease 后,嵌套 Host 及其子进程 + 在真实 File 与 SQLite authority 上证明:嵌套监督正常运行时,释放该 lease 后, + 嵌套 Host 及其子进程 在 worker 返回前停止,Todo 保持未完成,重试该操作既不会重新取得旧执行也不会 再次启动 Host。 @@ -67,40 +69,40 @@ 代表成员 claim、renew、complete 时行使的是同一份信任。释放之后,authority 拒绝该执行的一切续期、完成 CAS 与 acquire 重放;迟到的 Todo 完成与结果 验收在构造上不可能。文件锁、worker ACK、lane 探测、进程组记录都不是保证的 - 一部分。 + 一部分。该保证只约束经过 canonical authority 校验的写入,不能撤回 Host 已经 + 发出的 shell 命令、网络请求或其他外部副作用。 4. **回执。** 类型化的 TypeScript owner 在每次读取时由当前事实推导一个 phase;不持久化 phase。 - `requested`:意图已持久化,执行尚未暴露可释放的 lease。再次读取;worker 会在启动 Host 前观察到该意图。 - - `revoked`:释放已提交,或该执行已被另一个 epoch 或到期 fence。可以用新 - 操作安全继续该 Todo;旧执行无法提交任何 canonical 效果。 - - `drained`:在此之上,操作记录了 `stopped` 观察,且 `host_supervision` 为 - `returned`(leased supervisor 在证明丢失后返回)或 `not_launched`(未 - 启动 Host)。 + - `revoked`:释放已提交,或该执行已被另一个 epoch 或到期 fence。旧执行不能 + 提交受 canonical authority 校验的效果;这本身不证明可以重叠执行外部工作 + 或交接资源。 + - `drained`:在此之上,既有 Host owner 证明原执行及全部归属进程组已经退出, + 或证明 Host 从未启动且已不存在继续启动的可能。leased supervisor 返回、 + 操作记录为 `stopped` 或 grace 已经过期,都不足以证明这一点。 - `noop`:操作在 fence 生效前已经 `accepted` 或 `rejected`。原结论保留, 不写任何内容。 Drain 是观察,绝不是结算条件。worker 已死时停留在 `revoked` 且 - `host_supervision: unobserved`;之后的绿色读取不会升级它。 + `host_supervision: unobserved`;只有绑定原执行的完整 Host 证据才能建立 drain。 + 内层 supervisor 不可用或被中断时,即使外层已经返回,drain 仍未获证明。 5. **Worker 观察。** worker 在取得 lease 前和启动 Host 前各检查一次意图,任一 检查点命中时释放自己的 lease;在意图存在时任何被监督执行返回后记录 - `stopped`。这些检查点避免浪费工作;保证来自 fence,不来自检查点。 + `stopped`。这只说明 worker 处理过停止,不能证明完整 drain。检查点用于避免 + 浪费工作;canonical 写入保证来自 lease fence。 6. **Authority 模式。** stop 要求 Goal 的 canonical `hard_lease` 模式。在 `legacy` 或 `soft_claim` authority 上没有执行 lease,因此没有 fence; `stop --execute` 在任何写入前被拒绝,原因中写明模式。提升 Goal 是启用步骤。 7. **生命周期。** 已停止的操作拒绝 `resume`;继续需要新操作,它会取得新的 lease epoch。stop 永不完成 Todo、不结算 Goal、不改变已验收结果。 -8. **Drain 延迟。** 撤销与 drain 走不同的时钟。release 提交的那一刻 fence 即 - 生效;物理 drain 发生在既有 supervisor 取消委派 CLI 时,取以下最早者:续期被 - 拒绝、续期命令两次失败、当前证明读取失败或不再证明该执行、最后已证明的 - `expires_at` 到达。只有这个到期时刻能无条件地界定 drain:release 之后不会再有 - 续期,所以它最多在 release 提交后一个 lease TTL 到达,随后是六秒 grace 与强制 - 进程组终止。约三十六秒(不超过 30 秒的续期间隔加 grace,再加一次 lease 命令) - 只是名义路径:release 提交时没有正在进行的续期,且 authority 及时响应。已在 - 进行的续期按自己的时钟结束,因此 authority 响应慢或丢失时,drain 会推向到期 - 上界。所以 `revoked` 从不意味着 drain;只有 `drained` 才能视为执行资源已释放。 - 本切片不增加从 release 到 drain 的期限,也不增加信号加速路径;更紧的无条件 - 上界需要由 supervisor 自己拥有的取消期限及其真实进程验收。强制进程组终止后的 - 嵌套 Host 清理仍是 #5436 的既有 supervisor 边界,此处不重新证明。 +8. **Drain 延迟。** 撤销与资源退出是两个事实。release 提交后 fence 生效;既有 + leased supervisor 在续期被拒绝、当前证明失败或最后已证明的到期时刻请求取消, + authority 回复在途时也保留这个到期计时器。这些是取消触发条件,不是所有嵌套 + 进程退出的无条件期限。内层 supervisor 被中断或无法观察其清理时,外层返回和 + 到期加六秒 grace 都不能证明内层 drain。约三十六秒的健康路径只是名义值,要求 + authority 及时响应、release 时没有在途续期且监督正常运行。慢响应、回复丢失或 + 清理失败时,保留 `revoked` 与 drain 未证明的事实。只有真实完整的 Host 证据才 + 能得到 `drained`;本提案不新增 drain 硬期限、清理服务或第二个进程生命周期 owner。 9. **入口。** CLI `delegation stop --execute` 与 MCP `stop_delegation` 共用 `Delegations.stop`;`read`、`wait` 与 inventory 暴露回执与 `stopped` 观察。 dashboard 展示"停止已登记",不声称执行资源已释放。 @@ -127,9 +129,15 @@ release 到 drain 耗时,且从不断言名义上的三十六秒: - **续期进行中时 release。** 使用长 TTL(例如 180 秒),在一次续期已开始、且其 authority 回复被延迟时提交 stop 的 release。release 提交后回执即为 `revoked`, 嵌套 Host 仍在运行时不报告 `drained`。旧执行的续期、Todo 完成与验收都不能提交; - drain 在延迟的拒绝到达后发生,且不晚于最后已证明的到期加 grace。 + 分别观察既有取消触发和完整 Host 退出;不能证明 drain 时保留 `revoked`。 - **authority 回复丢失。** 续期命令两次失败或在超时内始终无回复时,回执与 fence - 的性质不变,supervisor 不晚于最后已证明的到期加 grace 取消执行。 + 的性质不变,最后已证明的到期计时器仍负责触发取消。取消观察不等于完整嵌套 + 进程 drain。 +- **内层 supervisor 中断。** 实际 Host 启动后暂停内层 supervisor,释放原 canonical + lease,等待外层调用返回。若独立观察到后代仍在运行,包括到期加 grace 之后, + 回执必须保持 `revoked` 且 drain 未证明。只有后续绑定原执行的完整 Host 证据才能 + 报告 `drained`。保留 supervisor 正常运行的正控,并保证断言失败时 fixture 仍清理 + 自己的进程组。 ## 本条目不建立什么