Conversation
Signed-off-by: duanjialing.777 <duanjialing.777@bytedance.com>
cocolord
left a comment
There was a problem hiding this comment.
评审提交:2601d5ae4e78611d65ec5a5ad55b0b7e3f9cd6c2。这是 policy-11 whole-PR、exact-head 评审。我检查了全部 14 个变更文件、accepted RFC 与 issue 链、typed effect handlers、Python 持久化 owner、Turn settlement/executor、recreation 与默认 CLI 投影、两份语义 inventory、聚焦测试和远端 checks,并用真实 filesystem registry 做了默认命令的失败路径回读。核心 fence 方向是正向的,但当前有一个可复现的运维语义回归,并且这套 3,013 行 durable protocol 的收益、最小性与单一语义 owner 尚未建立。
动机
这个 PR 想解决真实且高严重度的 lifetime race:Goal A 被 recreate 时,已经被 Turn 接纳、但尚未完成或可证明完成的 provider effect,不能越过 A/B 的 exact GoalRef 边界继续执行或被错误归属。accepted Goal-instance RFC 把这类 M3 工作跟踪在 #4801;若能在 crash/retry 下稳定做到 close A -> drain admitted work -> publish B,会直接降低 stale writeback、quota spend 与 terminal closeout 穿越 Goal lifetime 的风险。
但 PR body 目前写的是 Related to #4447,后者是 semantic-vocabulary convergence tracker,不是这条 recreation race 的需求来源。更重要的是,这次不是小修:14 files、+3013/-238,增加 gate、per-effect admission、tail hold、四个 typed handlers、跨 Python/TypeScript recovery 和新的默认失败状态。作者需要明确受影响的 host/operator、base 上可观察的失败、head 后的收益、发生频率/严重度和为什么这套三层持久机制优于更小的 journal-owned fence;内部测试很多只能证明实现自洽,不能替代需求收益与机制成本的证明。
改动思路
正向路径是:first-party host 在 provider effect 前通过 FirstPartyHostTurnEffectAdmission 把 exact GoalRef、turn key、step kind 和 effect ref 写成 admission;TurnSettlementJournalAdapter 再写 prepared/committed journal,并通过 provider readback 区分 committed、absent 与 unknown。最后一个 provider admission 不在 provider 返回时立即释放,而是通过 tail hold 保留到 scheduler 与 post-settlement 完成。recreate 则先把 A 的 gate 设为 closing,扫描 admissions:可从 journal/readback 证明完成的记录被释放,active/ambiguous/conflicting 记录继续 pending;只有 admissions 归零,typed gate decision 才允许发布 B。
这个 fail-closed 顺序本身合理,focused concurrency、crash-readback、absent reexecution 与 exact-GoalRef mismatch 覆盖也比较扎实。问题在两个边界:第一,drain_required 是已经发生 durable transition 的 public recovery state,却没有被默认 CLI 正确呈现;第二,provider step 与 hold schema 在 writer/reader 两侧重复定义,新的持久协议从一开始就存在多个语义 authority。
具体改动
阻塞问题
-
[P1]
drain_required隐藏了已经发生的 durable gate transition。recreate_goal_instance在 drain 前已经持久化gate.state=closing;有 pending admission 时,_drain_required_result却返回changed: false。默认render_project_command_markdown又只打印ok、registry 和 changed,不打印status、pending_effects、reason 或恢复动作。reviewer 用 exact-head 的真实 filesystem registry 放入一个已 admission、未 checkpoint 的 effect 后执行默认recreate-goal --execute:进程 exit 1,输出只有ok: False/ registry /changed: False,但命令后回读 durable gate 已是closing。这不是纯展示瑕疵:新 effect 会被拒绝,operator 却不知道是 resume Turn、repair tail 还是直接 retry。请把响应建模为 truthful 的 changed/partial transition,在默认 Markdown 中显示drain_required、pending turn/step/reason 和明确的 resume/repair/retry 指令,并用真实 CLI + gate readback 固化回归测试。 -
[P1] 请先证明这套 3k 行 protocol 的具体收益与最小性,并修正需求追踪。 RFC 指向的真实 tracker 是 #4801,PR body 的 #4447 与本问题无关。目前 tests 证明了新 state machine 的分支,但没有给出 base 上 deterministically failing、head 上 passing 的 host/recreation race,没有说明哪些当前 lane/用户会命中、频率或恢复成本,也没有比较更小的“settlement journal 作为 admission/hold owner”方案。对一个新增三类 durable record、锁顺序、mixed-language parser 和长期 migration burden 的 PR,“存在理论 race”不足以直接证明投入产出比。请写清受影响 caller/operator、before/after 可观察结果、验证方式、严重度/频率,以及为什么 gate + admission + tail hold 是最小可维护机制;若无法证明,请把 scope 收窄到可独立验证的最小 fence。
-
[P2] provider-effect vocabulary 和 persisted hold schema 需要单一 owner。
source_session_lifetime.ts新建SourceTurnEffectStep,与既有turn_driver/settlement.ts的PROVIDER_STEP_KINDS重复;PythonSourceTurnEffect.step_kind又使用更宽的SettlementStepKind,能表达 provider protocol 不允许的validation。同时source_session_turn_effects.py的_HOLD_SCHEMA与settlement.py的SOURCE_TURN_EFFECT_HOLD_SCHEMA_VERSION独立保存相同 v1 literal。若未来只改一侧,合法 hold 会被 drain 读成turn_tail_conflict,Goal 可能永久卡在 closing。请 export/generate 一个 provider-step union 与一个 hold-schema owner,收窄 Python type,并增加 writer -> drain reader 的合法/非法 round-trip。
关键代码讲解
decideSourceTurnEffectAdmission/decideSourceTurnEffectGate是新 typed authority:只允许 current exact GoalRef 在 open gate 下 admission,closing gate 且 admission count 非零时拒绝 publish。规则本身 domain-neutral,没有把 advisory 文案伪装成 machine obligation。prepare_source_turn_effect/release_source_turn_effect/drain_releasable_source_turn_effects是 filesystem protocol owner:它们在 guard lock 下写 admission、与 Turn journal 联动,并把executor_active、journal_unreadable、turn_tail_recovery_required等情况投影为 pending。TurnSettlementJournalAdapter把 provider prepared ref、readback 和 source admission 串起来;executor.py把 final admission 延长到 scheduler/post-settlement 后再释放。这解释了为什么只在 provider callback 周围加锁不够,但也让 hold schema 成为必须单一所有权的兼容协议。recreate_goal_instance的 close -> drain -> publish 顺序防止 B 过早可见;_drain_required_result与render_project_command_markdown之间则造成当前可复现的“durable state 已变、用户看到 changed=false”缺口。- 两份 RFC 镜像、goal binding inventory 与 project registry I/O manifest 都登记了新 owner;TS lifetime tests 和 Python executor tests覆盖核心 rule,但没有覆盖默认 CLI receipt 与 persisted gate 的一致性。
对主干的风险
主风险不是 happy path:tests/test_loopx_turn_executor.py 82 个用例、16 个新增聚焦 Turn case、22 个 source-session CLI test、2 个 registry-denial test 和 9 个 typed lifetime test 都通过,control-plane typecheck 也通过。完整 TS suite 的唯一 sqlite capacity 失败,以及远端生成 contract / prompt-upgrade-hook 失败,都在 immutable base 和 exact head 上以相同 signature 复现,因此不归因于本 PR;但远端 pytest / merge-gate 当前仍是红,不能描述为 merge-ready。
真正的新增风险发生在 negative/recovery path:一次未完成 admission 会先把 gate 从 open 持久化为 closing,再返回失败。默认 receipt 不展示状态和恢复义务,调用方容易反复重试、误判 no-op,或在不知道新 effects 已被拒绝的情况下排障。另一个长期风险是 persisted vocabulary 漂移:writer 与 drain reader 对 hold schema/step kind 的任何单边修改都可能把一个可恢复 Turn 变成永久 conflict。修复需要真实命令级 readback test 与跨 owner round-trip,而不是再增加只断言内部 payload 的 unit case。
语义与 CI 对齐
这次新增的是 machine-enforced obligation:gate closing 后不得再 admit,新 Goal 必须等 admissions 归零;它不是“guidance”。typed TS decision 对 open/closing 与 exact GoalRef 的建模方向正确,错误文本也保持 domain-neutral。当前不对齐的是 public projection 与 semantic ownership:CLI 隐藏 obligation,changed=false 与 durable transition 矛盾,provider-step/hold-v1 又被多个 owner 重复声明。请让 typed contract、Python persistence、CLI receipt 和 RFC/issue chain 对齐后再合并。
我的整体评价
结论是 REQUEST_CHANGES。whole diff 的安全目标值得做,核心 close/drain/publish state machine 与多数 recovery tests 也是明显正向;但这并不自动证明当前实现可以 approve。对用户体验,新的失败路径已经可复现地误导 operator;对长期演进,3,013 行机制的 observable benefit、最小性和单一协议 owner 都还没有达到高风险 durable-state change 的门槛。
请优先修复默认 CLI 的 truthful recovery receipt,补真实命令 + state readback 负向测试;修正 #4801 追踪并写清 affected caller、before/after 收益和 mechanism trade-off;同时收敛 provider step/hold schema owner。完成后重跑 focused Python/TS、writer-reader round-trip、typecheck 与 required CI。本次 review 只给出评审结论,不修改 PR,也不授予 merge authority。
English verdict: REQUEST_CHANGES on exact head 2601d5ae4e78611d65ec5a5ad55b0b7e3f9cd6c2. The exact-GoalRef fence and focused recovery coverage are promising, but a reproduced default-CLI path exits 1 after durably changing the gate to closing while reporting only changed=False and hiding drain_required, pending-effect reasons, and the recovery action. The PR also duplicates persisted provider-step/hold vocabulary and points to unrelated #4447 instead of the RFC tracker #4801 without establishing the concrete before/after benefit or why this 3,013-line protocol is the minimum maintainable repair. Fix the recovery receipt, consolidate semantic owners, document the verified ROI and rerun focused plus required checks.
Signed-off-by: duanjialing.777 <duanjialing.777@bytedance.com>
Signed-off-by: duanjialing.777 <duanjialing.777@bytedance.com>
|
Review blockers are addressed on exact head
Local verification: focused Python |
Signed-off-by: duanjialing.777 <duanjialing.777@bytedance.com>
|
CI triage and main sync update:
New exact-head CI is running on |
|
Dependency update for the current exact-head failures:
#5345 passes |
cocolord
left a comment
There was a problem hiding this comment.
评审提交:6689eb01c95b599f3a6ed253191e5565090e68cd。这是 policy-12 whole-PR、exact-head 复审。我检查了全部 20 个变更文件、#4801 与 accepted RFC、typed effect handlers、Python sidecar/lock owner、Turn settlement/executor、recreation 与默认 CLI、两份 inventory、作者新增测试和远端 checks,并用真实 filesystem registry 做了 mixed partial-drain 的命令级回读。上一轮的三个问题中,首次 drain_required 展示、需求/收益说明、provider-step 与 hold-schema 单一 owner 都已实质修复;当前仍有一个新的、可复现的 truthful-receipt blocker。
动机
这个 PR 解决的是一个真实的高影响竞态:Goal A 的 Turn 已通过 GoalRef 检查并进入 provider effect 后,operator 可能 recreate 同名 Goal B;若没有 durable fence,A 的 writeback、quota spend、terminal closeout、scheduler 或 post-settlement callback 可能在 B 成为 current 后继续落地。当前 PR body 已正确关联 #4801,明确受影响入口、base/head 行为、高影响低频、execution_authority: false 的激活边界,以及 gate、per-effect admission、tail hold 各自覆盖的 race window。对这套 3.2k 行机制的收益与最小性说明已比上一版充分。
改动思路
正向路径是:first-party source Turn 在 provider 调用前把 exact GoalRef、turn key、typed provider step 和 effect ref 写入 admission,并与既有 Turn journal 的 prepared/committed/readback 状态绑定;最后一个 provider admission 通过 source_effect_hold 延长到 scheduler 与 post-settlement 结束。recreate 先在 alias lifecycle guard 下把 A 的 gate 写成 closing,再逐个锁 journal:已能证明完成的 admission 被释放,active、prepared、unreadable、identity conflict 或 tail hold 继续投影为 pending;只有 admission 集合为空,typed publish 才允许 registry 退休 A、发布 reserved B 并重新打开 gate。
这次 follow-up 也正确收敛了语义 owner:TurnProviderStepKind / TURN_PROVIDER_STEP_KINDS 由共享 effect_program 提供,TypeScript gate 与 settlement reader 复用同一集合;hold schema 由 drain owner 导出,settlement writer 导入。默认 project Markdown 现在展示 status、changed/replayed、gate、pending turn/step/reason 和 recovery action。
具体改动
整份差异为 20 files、+3252/-267:两份 RFC 补充 Turn-effect candidate 与收益边界;effect_program.py/.ts 和 runtime handlers 扩展 typed provider vocabulary/decision;first_party_host_admission.py、source_session_turn_effects.py 和 source_session_lifetime.ts 实现 gate/admission/hold 及 exact-GoalRef 规则;settlement.py/.ts 与 executor.py 把 admission 贯穿 provider、scheduler 和 post-settlement;source_session_recreation.py 实现 close -> drain -> publish;project.py 输出 actionable recovery;inventory 和四个测试面登记并覆盖新协议。
关键代码讲解
prepare_source_turn_effect在 provider 前持久化 admission 和 prepared journal,使 stale A effect 可枚举、可拒绝。TurnSettlementJournalAdapter复用现有 provider attempt/readback,并让最终 admission 覆盖 settlement tail;合法/损坏 hold round-trip 已有负向验证。drain_releasable_source_turn_effects对每个 admission 做 fail-closed reconciliation,能删除已释放项,但当前只返回 remaining pending。recreate_goal_instance正确保持 A current 直到 drain 为空;问题发生在它聚合 partial-drain 回执时。render_project_command_markdown已修复上一版默认输出缺失,但只能如实渲染底层 payload,无法补救错误的 changed/replayed。
阻塞问题
[P1] 同 operation 的 partial drain 被错误标成 no-change replay。 _drain_required_result 第 252 行 只用 gate_changed 计算 changed,并把其反值当 replayed;但前面的 drain 会在本次调用里真实删除 admission。独立 exact-head 探针创建两个 prepared durable_writeback admissions:第一次 recreate 后两者都 pending,返回 changed=true;随后让其中一个 journal 可释放、另一个仍需 provider readback,再用同 operation retry。回读看到 admissions 从 2 降到 1,但结果仍是 changed=false, replayed=true。提交测试只覆盖“唯一 pending admission 完全没变化”的 retry,所以没有命中 mixed set。
这会让自动化或 operator 把一次实际持久化进展当作纯回放,跳过应有 refresh/audit,也与 PR body 的“recovery receipt matches durable state”直接矛盾。请让 drain 返回 remaining pending 以及 released_count/changed,由 recreation 用 gate_changed || drain_changed 生成 whole-command receipt;只有本次没有任何 durable mutation 时才 replayed=true。回归测试应走真实 CLI + filesystem readback:2 pending -> 1 released/1 pending 时断言 changed=true/replayed=false;紧接着不再变化的 retry 才断言 changed=false/replayed=true。
对主干的风险
核心 safety path 的证据总体扎实:exact head 上 105 个 source-session CLI/Turn executor Python tests、9 个 focused typed lifetime tests、control-plane typecheck 与 changed-Python Ruff 均通过;完整 control-plane run 的 3531 个通过用例之外有一个无关 SQLite timing case,并在隔离重跑立即通过,远端三个 TypeScript shards 也全绿。close-before-drain、new-admission rejection、committed/absent/unknown readback、tail recovery、historical replay 与非 source 路径都有覆盖。
远端当前 27 success、10 failure。Frontstage、chat-bundle-browser、dashboard 在 immutable base 与 exact head 都是同一个 28px assertion;generated-twin 与两个 prompt-upgrade 失败也用同一 pytest 命令在 base ba1e92d860027d2cf209d869249dcf61e6a11651 和 head 得到相同断言,aggregate checks/pytest/merge-gate 随之失败。它们不归因于本 PR,但 required CI 仍是独立 merge-readiness hold。
真正属于本 PR 的风险在 public recovery semantics:gate safety 没有被绕过,B 仍不会过早发布,但 loopx_goal_recreation_v1 无法可靠表达中间 drain 进展。changed/replayed 是机器字段,不是提示性 guidance;在未来开启 execution authority 前必须让它们与 admission side effect 一致。
语义与 CI 对齐
exact GoalRef、gate state、provider step、hold schema 与 pending reason 已放入 typed/shared owner,核心 obligation 保持 domain-neutral,也没有用 substring denylist 或散落 prose 做状态分类。当前唯一不对齐点是 whole-command receipt 仍由 gate 的局部 boolean 推导,漏掉 drain owner 的真实 mutation;PR 文档还把所有同-operation retry 概括成 changed=false/replayed=true。请修正代码、测试与这句文档,再重跑 focused Python/TS、typecheck、Ruff 和 required CI。
我的整体评价
结论是 REQUEST_CHANGES。这个 PR 的问题、收益、机制边界和大部分实现已经建立,上一轮 blocker 的修复也不是表面改文案:首次失败现在可操作,provider vocabulary/hold schema 也有单一 owner。剩余问题范围很小,但它正好落在本轮声称修复的 truthful recovery contract 上,而且已通过真实命令与持久状态回读复现。请在现有 drain/recreation owner 内做有界修复,不需要再增加平行 state machine;修复后我会按新 exact head 只重跑这条 partial-drain 负向路径和相关回归。本次 review 只给出评审结论,不修改 PR,也不授予 merge authority。
English verdict: REQUEST_CHANGES on exact head 6689eb01c95b599f3a6ed253191e5565090e68cd. The prior CLI visibility, motivation/proportionality, and provider-step/hold-schema ownership blockers are materially fixed, and focused source-session validation passes. However, a real two-admission retry removes one durable admission (2 -> 1) while returning changed=false, replayed=true because the receipt considers only the already-closing gate, not drain mutation. Return a structured drain change fact, make replay mean no durable change, add the public CLI/readback regression, and rerun required checks; current unrelated red checks still hold merge readiness.
Signed-off-by: duanjialing.777 <duanjialing.777@bytedance.com>
|
Addressed the partial-drain receipt blocker on exact head
A real CLI/filesystem regression now covers Validation on this head: focused Python |
…ence-regression Signed-off-by: duanjialing.777 <duanjialing.777@bytedance.com>
|
Synced the branch with Post-sync validation passes: 108 focused Python tests, 9 typed source-lifetime TypeScript tests, control-plane TypeScript typecheck, Ruff, and @cocolord @huangruiteng please re-review this exact head when available. No merge action was taken. |
|
CI attribution for exact head Both are the same current-main regression, not GoalRef changes. Each test fails 5/5 on the immutable PR base The behavior was introduced by merged #5323 ( |
|
The baseline runtime fingerprint race now has a dedicated fix in #5367 at exact head The regression test forces |
cocolord
left a comment
There was a problem hiding this comment.
动机
本次完整复审针对 c797daf,以 #4801 和本 head 的 Goal-instance RFC §5.5 / M3 source Turn slice 为框架,不把整个 M3 或 source execution 激活当成本 PR 的交付。当前未发现阻塞项。
收益有独立反事实支持:相同输入经过真实 executor、registry 和 journal,base 在 A 的 scheduler 回调内就能发布 B,随后 A 最终 checkpoint 抛出 stale_goal_instance;本 head 在该位置返回 drain_required,A 完成后才发布 B,B 下一次 Turn 也能提交。它解决低频但高影响的生命周期竞争;默认执行权限仍为 false。
改动思路
TypeScript 的 source_session_lifetime 继续决定 exact GoalRef、gate 和 admission 的合法转换;Python 协调短生命周期锁与文件持久化;已有 Turn journal 仍拥有 provider attempt/readback 真相。gate 禁止新的 A effect,admission 索引已进入的 effect,tail hold 覆盖最后一次 provider checkpoint 到同步 scheduler/observer 返回及最终 journal checkpoint 的窗口。
比较过更小方案:最后一次身份检查仍有 check/use 竞争;把生命周期锁跨 provider 或用户回调持有会扩大锁边界;仅靠已有 journal 不能关闭未来 admission,也不能枚举全部在途记录。这三个记录承担不同职责,当前实现没有另建 provider 清理引擎。
具体改动
全量 diff 为 20 文件、+3370/-267:12 个 runtime 文件、4 个测试文件、2 份双语 RFC、2 份 inventory。除新 effect 协调 owner 和 typed lifetime 决策外,改动贯穿 first-party admission、effect runtime dispatch、Python/TS settlement、Turn attempt contract、executor 尾部和默认 project CLI 输出。测试覆盖并发、崩溃、unknown/absent/committed readback、tail recovery 和损坏持久化状态;inventory 与 RFC 保持 partial qualification,不扩大执行授权。
关键代码讲解
prepare_source_turn_effect(source_session_turn_effects.py:194)在 alias guard 下校验 exact GoalRef 和 canonical journal,先写 admission 与 prepared checkpoint,再进入 provider。TurnSettlementJournalAdapter.hold_tail/release_tail(settlement.py:242)保留最后一个 provider admission;executor 不在生命周期锁内执行 scheduler/observer,最终 checkpoint 才释放。drain_releasable_source_turn_effects(source_session_turn_effects.py:445)复用 journal 区分可释放与仍待恢复的 effect;新SourceTurnEffectDrainResult同时返回 pending_effects 和 released_count。recreate_goal_instance(source_session_recreation.py:266)先 closing、后 drain、最后 publish;从 gate_changed 或 drain_result.changed 推导 changed,让 partial-drain 重试不再伪装成 replay。默认 Markdown 保留 gate、pending reason 和 recovery action。
上次 partial-drain blocker 已独立复验修复:两条 admission 中仅释放一条时,目录从 2 变 1,返回 changed=true / replayed=false;随后的无变化重试才返回 false / true。此次不是沿用旧结论。
对主干的风险
本地实测:focused Python 106 passed,typed lifetime 9/9,control-plane typecheck、changed-Python Ruff、registry-denial architecture、docs governance 与 diff check 通过;diff-driven premerge 的 3 项 direct checks 和 19 项选择检查通过,包括 vocabulary drift 与 public-boundary。独立 base/head probe 证明 non-source commit/replay 保持 schema、phases 和一次性 effect;A closing 时在同一 registry 新建的独立 Goal 可以提交,drain 后 B 可以继续,不只是得到阻塞回执。
尾部范围做了额外反证:人为绕过 reward-memory 的 source registry 读取门禁时,pending observer sidecar 不会被同步 tail hold 排空;但保留原生门禁时,它在 provider 前拒绝,实测 provider_calls=0、无 pending sidecar。因此这不是当前可达回归,也不能把本 PR 当成未来异步 observer/provider reconciliation 已获资格的证明。开启该范围前,应在既有 owner 下证明 uncertain effects 阻止退休,或保留明确 activation hold;不建议在本 PR 新增第二个 reconciliation engine。该探针控制配置输入和远端 provider,registry、journal、admission 与重建走真实代码。
语义与 CI 对齐
当前 CI run 36746763361 的两个根失败分别是 runtime fingerprint 文件消失后未重扫,以及持续 source churn 返回 ready 而非 package_invalid。我用同一命令在不可变 base 3b73108 与本 head 复现相同两条断言;effect_runtime.py、runtime/file_reads.py 和对应测试没有 PR diff。test-shard 3/4 导致 pytest 与 merge-gate 聚合失败;独立修复由 #5367 跟进,不应要求本 PR 修无关代码,也不能越过 merge gate。
其余适用 CI 通过。deploy/upload/publish 属 PR 条件跳过;forward Node 仅 push/dispatch;presentation 无对应变更触发;Sonar 是 pytest 依赖下的非阻塞跳过。当前复用 settlement vocabulary 扩展 typed lifetime,没有 substring 分类、领域专用义务文案或把硬门禁称作 guidance 的问题。未执行 live paid provider、warm/mixed-version runtime 或整体 M3 激活验证。
我的整体评价
APPROVE(仅此 exact head 的代码审查,不表示可合并或可激活)。long-horizon 改善体现在 A 最终结果可持久化并让后续 B 继续;用户体验改善体现在 drain 状态可读、恢复动作明确、partial progress 回执真实。non-source 基线兼容与同容器独立工作均经过实际路径验证。
代码量是成本,但本次 gate/index/tail 的边界和收益足以支持完整、可回退的资格审查阶段。bounded simplification 已应用:provider-step/hold schema 共用 owner,drain 返回结构化 mutation facts;未发现应再拆层或添加框架的必要。保留整体 M3、其他 owner 和异步 provider 的 activation hold,以及当前独立 CI 红项。请在独立基线修复后重新满足 required checks;此 review 不授予 merge 或 source execution 权限。
English verdict: APPROVE - c797daf. Partial-drain receipts are fixed. Whole-PR review and real baseline/head lifecycle, scope and legacy-parity probes validate this bounded inactive source Turn slice. Local 106 Python tests, 9 typed lifetime tests, typecheck and 19 selected premerge checks pass. Two unchanged baseline runtime-fingerprint failures still hold merge readiness; asynchronous observer/provider activation is not qualified.
Summary
GoalRefdrain_requiredtransitions and recovery actions in the default CLI outputRequirement and affected paths
This is a follow-up to #4801 and the accepted Goal-instance RFC. The issue requires host bindings and effects from Goal A to lose authority when an operator recreates the same human-readable Goal id as Goal B.
The affected entry points are:
loopx turn run-once --executefor the built-in source-session Turn settlement pathloopx project recreate-goal --executefor the operator who retires Goal A and publishes Goal BNon-source Turn paths retain their existing schemas and behavior.
Before and after
On the base commit,
run-oncecan pass its GoalRef check and begin a provider effect without leaving a source-owned record that recreation can drain. Recreation can then publish Goal B while Goal A's writeback, quota spend, closeout, scheduler action, or post-settlement callback is still unresolved.This branch writes a per-effect admission before each provider call. Recreation first changes Goal A's gate to
closing, rejects new admissions, and inspects the admitted effects through their existing Turn journals and provider readback. It publishes Goal B only after the admission set is empty. The last provider admission remains held until the scheduler and post-settlement work finishes.The recovery receipt now matches the durable state. The first blocked recreation reports
changed=True,gate_state=closing, each pending Turn and step, its reason, and a recovery action. A same-operation_idretry that releases one or more admissions reportschanged=Trueandreplayed=False; only a retry with no durable mutation reportschanged=Falseandreplayed=True.The deterministic regression coverage includes:
Severity and activation boundary
The overlap is expected to be rare, but its impact is high because a stale Goal A effect can write state, spend quota, or close work after Goal B becomes current.
This PR does not activate source-session execution. The RFC and returned records still set
execution_authority: false, and the overall M3 activation hold remains. The default production frequency is therefore zero today. This change closes the Turn settlement gap before that authority can be enabled.Why these records are needed
A final GoalRef check alone leaves a check/use race. Holding the source lifecycle lock across provider calls or scheduler callbacks would also make the lock cover network and user code.
The existing Turn journal remains the owner of provider attempt and readback state, but it cannot close future admissions or tell recreation which in-flight journals belong to Goal A without a source-owned index. The three records have separate jobs:
Recreation uses the existing journal for reconciliation. This PR does not add a second provider cleanup engine. Python and TypeScript now derive provider steps from the existing settlement vocabulary, and the drain reader owns the persisted hold schema.
Validation
uv run --extra test pytest -q tests/cli_commands/test_source_session_lifetime.py tests/test_loopx_turn_executor.pynpm run test:control-planenpm run typecheck:control-planeuv run --extra test python examples/docs-governance-smoke.pyBaseline failures
tests/architecture/test_turn_contract_generation.py::test_new_independent_twin_cannot_hide_behind_generated_pairexpects one generated pair, while currentorigin/main@996bcc027reports two.examples/repository-hygiene-smoke.pyreportstests/test_contract_scan_missing_roots.py:46: private_ipon the same main baseline.No activation authority is granted by this PR.
Follow-up to #4801.