Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Replan consumer checkpoint and default-path estimate

The replan history decision family now shares a TS owner across legacy and
canonical consumers. See the [TS checkpoint](../typescript-control-plane-migration-v0/2026-09-22-replan-history-policy.md)
for the four semantic fixes and isolated real-source readback. This advances T3;
it does not establish a new provider durability, capture, or promotion claim.

The planning range remains **5–8 cohesive delivery packages**, conditional on
acceptance rather than line counts: remaining caller/executor fences (1–2),
consumer/projection recovery (1), SQLite durability and qualification (1–2),
whole-Goal capture/migration (1–2), then default onboarding and bounded retirement
(1). These categories can overlap in a complete package; their maxima are not
independent additive promises. The current history slice does not retire an
entire package. The existing SQLite long-running qualification owner (#4224)
and the required elapsed soak remain dependencies.

A complete capture attempt exposed a still-open archive dependency role/class
hold. Active read-model parity is useful but cannot discharge this hold. Existing
Goal migration must keep its exact-source qualification, old-writer fence and
rollback acceptance; no active Goal was promoted for this validation. PostgreSQL
remains separately qualified and opt-in; this PR changes no store transaction,
connection, selection, schema or migration contract.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Replan 消费侧检查点与默认路径估算

历史 replan 决策族已由旧路径与 canonical 消费侧共享 TS 规则;四项语义修复与
隔离真实读后核对见 [TS 检查点](../typescript-control-plane-migration-v0/2026-09-22-replan-history-policy.zh-CN.md)。
这推进 T3,不新增 provider 持久性、完整捕获或晋升资格声明。

仍估计需要 **5–8 个完整交付包**:剩余 caller/executor fence 1–2、消费侧和投影
恢复 1、SQLite 持久性资格 1–2、整 Goal 捕获及迁移 1–2、默认 onboarding 和受控
删除 1。相关类别可以在一个完整包中重叠,不能把各自最大值当作独立承诺相加。
本次历史触发 slice 没有独自清空其中一个完整包;SQLite 长程资格原有负责人
(#4224)及实际 soak 时间仍是依赖,不能用 PR 数量抵扣。

完整捕获还遇到归档依赖 role/class 不完整的门禁。活动读模型一致不能消除这个
阻塞。既有 Goal 迁移仍需精确源资格、旧 writer fence 及回滚验收;本次没有晋升
活动 Goal。PostgreSQL 继续独立资格化并显式选择,本次不修改其事务、连接、
选择、数据库 schema 或迁移合同。
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Replan history decision owner

Goal/source: overall roadmap #4574, typed control-plane T3 consumer ownership.
The observed gap was independent Python history scans with different retry,
ACK, neutral-accounting and lane semantics. The delivered boundary is one
`work_item.replan_history.project` request, with in-process reuse of the existing
Todo resume planner. Python keeps legacy codecs and public obligation rendering;
its replaced historical trigger scans and duplicated neutral vocabulary are removed.

Four independently specified regression cases failed on baseline `709734cd6`:
periodic retry overcount, Monitor retry overcount, accepted ACK not resetting
progress repetition, and neutral accounting breaking equivalent progress. The
new owner corrects them while retaining thresholds, precedence and obligation
identity. Typed negative cases and the 280-row multi-agent interleaving fixture
cover scope-before-ACK, retries, missing identities, invalid input and source
immutability. Real File/SQLite consumer tests delete the display before reading
status and invoking quota, rather than substituting an in-memory store.

A read-only local-source rehearsal covered 345 active Todos, 600 history rows
and five agent lanes: all five historical projections matched baseline; isolated
File/SQLite readback and public quota CLI passed without changing the source.
This was an active read-model snapshot, not whole-Goal promotion. Complete source
capture independently rejected an archived dependency with missing/incompatible
role/task-class facts. That migration hold remains and was not bypassed or repaired
in active state.

This closes the history-trigger decision family, not all T3 or default adoption.
Progress fingerprint codecs, obligation assembly, frontier settlement and broader
capture/provider qualification retain their owners. No new model observer,
provider selection, frontend configuration or optional capability is introduced.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Replan 历史决策归属

目标来源为总纲 #4574 与 T3 消费侧 TS 迁移。原有 Python 历史扫描分别处理重试、
ACK、中性记账及 agent 归属,规则已经分叉。本次统一到一次
`work_item.replan_history.project` 请求,并在 TS 内直接复用 Todo resume planner。
Python 保留历史解码、指纹及 obligation 呈现;删除被替代的触发扫描和重复词表。

基线 `709734cd6` 上独立编写的四个反例失败:周期复盘重复累计重试、监控重复
累计重试、ACK 未截断进展停滞、记账打断同质进展。新实现修复这些问题,保持
阈值、优先级和 obligation 标识。280 行多 agent 交错 fixture 与边界负例验证
先归属后 ACK、重试、无效身份、输入拒绝及不修改源数据。File/SQLite 消费侧测试
使用真实持久化 provider,删除展示文件后读取状态并执行 quota CLI。

本机只读演练覆盖 345 条活动 Todo、600 条历史记录和五个 agent:五个历史投影
与基线一致,隔离 File/SQLite 读后核对及公开 quota CLI 通过,活动源未修改。
该证据仅限活动读模型,不证明完整 Goal 晋升。独立的完整捕获因归档依赖缺少或
不兼容的 role/task-class 事实而拒绝;这个迁移阻塞保留,没有绕过门禁或改写活动状态。

本次闭合历史触发决策族,不代表全部 T3 或默认切换。进展指纹解码、obligation
组装、frontier 结算及完整捕获资格仍有各自归属。没有加入模型观察器、provider
切换、前端配置项或可选 capability。
49 changes: 49 additions & 0 deletions docs/reference/protocols/goal-vision-replan-contract-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -730,3 +730,52 @@ A change satisfies this contract only when:
- auto-research remains a thin preset over the reusable kernel; and
- public docs and smokes cover the budget, state machine, and `quota.py`
boundary without private material.

## History-trigger ownership and retry semantics

The built-in `work_items/replan_history.ts` decision owns historical progress
repetition, blocked-successor repetition, repeated executed Monitor polls,
periodic review, and the persisted unchanged-Monitor streak. The Python codec
preserves historical observation fingerprints and timestamp parsing, then sends
one bounded fact request. Obligation rendering and identity serialization retain
the existing public contract. This is deterministic policy; no observer model,
new capability, provider selection, or additional permission is introduced.

History is newest first. Agent scoping precedes an accepted replan ACK cutoff;
a peer ACK cannot clear another lane. The three existing neutral accounting
classifications are transparent. A valid logical turn id is counted once per
agent, including an id carried by settlement identity. Missing, malformed, or
conflicting historical ids remain separate rows; the reader does not invent an
identity. Unknown material work still breaks an established progress streak.

The default thresholds remain two equivalent typed observations, two blocked
successor waits, six executed unchanged Monitor turns, twenty material turns
for periodic review, and five persisted unchanged polls for a Monitor-only
lane. Trigger precedence remains progress, Monitor, then periodic review.
Accepted ACKs reset the historical window; clearing another frontier obligation
still requires its existing typed semantic outcome and revision rules. A future
blocking Monitor suppresses premature wait replanning only while its schedule
and expiry are valid; the decision reuses the Todo resume planner.

These are enforced replan conditions, not advisory hints. Relative to the older
reader, retry records no longer accelerate periodic/Monitor thresholds, accepted
ACKs now stop typed-progress repetition, and neutral accounting no longer hides
repetition. These changes apply to legacy, File, and SQLite status/quota callers
without an opt-in. Frontend and Lark consume the existing obligation shape and
need no new setting or editor. Read back with `loopx status --goal-id <id>` and
`loopx quota should-run --goal-id <id> --agent-id <agent>`.

### 中文:历史触发与重试语义

历史触发规则由现有 work_items 的 TypeScript 边界统一维护,Python 负责旧数据
解码及原有 obligation 呈现。没有新增模型、capability、provider 选择或权限。
先按 agent 筛选,再遇到已接受的 replan ACK 截断窗口;其他 agent 的 ACK 不能
清空当前窗口。中性额度记账不计数、不打断停滞;同一 agent 的有效 Turn ID
只计一次。无效、缺失或相互矛盾的历史 ID 不被猜测性合并。

阈值仍为:2 次相同 typed progress、2 次 successor 等待、6 次已执行监控、
20 次实质工作轮次、5 次持久化监控无变化。优先级及 obligation 标识保持原样。
修复的是计数单位、ACK 截断和记账透明性,适用于旧路径及 File/SQLite;这些是
机器执行的 replan 条件。尚未到期且在到期时仍有效的关联监控继续抑制提前重规划。
这不替代其他 frontier 的语义验收、版本检查或权限。前端与 Lark 继续使用原有
返回结构;可用上面的 status/quota 命令核对。
2 changes: 2 additions & 0 deletions loopx/control_plane/effect_runtime_handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ import {
} from "./turn_driver/delivery_continuity.ts";
import { reduceTurnSettlementTransaction } from "./turn_driver/settlement.ts";
import { evaluateHostTodoCompletion } from "./turn_driver/host_todo_completion.ts";
import { projectReplanHistory } from "./work_items/replan_history.ts";
import { projectReplanSemantics } from "./work_items/replan_semantics.ts";
import {
projectReplanSettlementContract,
Expand Down Expand Up @@ -787,6 +788,7 @@ export function createEffectRuntimeHandlers(
["turn.host_todo_completion.evaluate", evaluateHostTodoCompletion],
["work_item.replan_settlement.project", projectReplanSettlementContract],
["work_item.replan_semantics.project", projectReplanSemantics],
["work_item.replan_history.project", projectReplanHistory],
[
"work_item.replan_settlement.reentry",
projectTodoLifecycleSettlementReentry,
Expand Down
10 changes: 5 additions & 5 deletions loopx/control_plane/status/autonomous_replan_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

from __future__ import annotations


from typing import Any

from ..work_items.replan_history_codec import (
REPLAN_HISTORY_NEUTRAL_CLASSIFICATIONS as AUTONOMOUS_RUN_HISTORY_NEUTRAL_CLASSIFICATIONS,
)

from ..runtime.public_safety import public_safe_compact_text
from ..work_items.autonomous_replan_ack import (
AUTONOMOUS_REPLAN_ACK_MATERIAL_RUN_WINDOW,
Expand All @@ -23,11 +28,6 @@
DEAD_MONITOR_REPEAT_SCHEMA_VERSION = "dead_monitor_repeat_v0"
AUTONOMOUS_REPLAN_SCHEMA_VERSION = "autonomous_replan_obligation_v0"
AUTONOMOUS_REPLAN_PERIODIC_RUN_THRESHOLD = AUTONOMOUS_REPLAN_ACK_MATERIAL_RUN_WINDOW
AUTONOMOUS_RUN_HISTORY_NEUTRAL_CLASSIFICATIONS = {
"quota_slot_spent",
"quota_slot_voided",
"delivery_completion_spend_accounted_v0",
}


def build_autonomous_replan_obligation(
Expand Down
21 changes: 6 additions & 15 deletions loopx/control_plane/work_items/autonomous_replan_ack.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,16 +122,10 @@ def compact_autonomous_replan_ack(run: dict[str, Any] | None) -> dict[str, Any]
isinstance(outcomes, list)
and "fresh_vision_path_outcome" in outcomes
):
agent_vision = (
run.get("agent_vision")
if isinstance(run.get("agent_vision"), dict)
else {}
)
path_delta = (
agent_vision.get("path_delta")
if isinstance(agent_vision.get("path_delta"), dict)
else {}
)
raw_vision = run.get("agent_vision")
agent_vision = raw_vision if isinstance(raw_vision, dict) else {}
raw_path = agent_vision.get("path_delta")
path_delta = raw_path if isinstance(raw_path, dict) else {}
path_disposition = str(path_delta.get("outcome") or "").strip()
if path_disposition in FRESH_VISION_PATH_DISPOSITIONS:
result["path_disposition"] = path_disposition
Expand Down Expand Up @@ -211,11 +205,8 @@ def _latest_monitor_replan_frontier_identity(
for run in latest_runs or []:
if not isinstance(run, dict):
continue
target = (
run.get("monitor_target")
if isinstance(run.get("monitor_target"), dict)
else {}
)
raw_target = run.get("monitor_target")
target = raw_target if isinstance(raw_target, dict) else {}
if normalized_agent_id:
run_agent_id = str(run.get("agent_id") or "").strip()
target_agent_id = str(target.get("agent_id") or "").strip()
Expand Down
Loading
Loading