Skip to content
Draft
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,99 @@
# Goal-route hints in the existing planning batch

## Boundary and compatibility

This slice moves the complete read-only Goal-route projection from Python into
`work_items/goal_route_hint.ts`, consumed by the existing
`work_item.action_portfolio.project` batch. It retires the Python route builder
and task-override composer together with the production caller switch. The
existing `quota.run_decision` owner still produces task-orchestration overrides;
this slice consumes that result rather than implementing its policy again.

The internal `quota_planning_packet_request_v1` carries independently nullable
planning and route contexts. Its result is `quota_planning_packet_v1`. The
coordination contract JSON registers both identities, and the existing generator
owns Python/TypeScript constants. Existing v0 requirements and result shape keep
their meaning. The external nested `goal_route_hint_v0` does not change.

Python captures route facts at the former builder position, after Agent lane,
frontier and recommendation selection. The existing planning call projects the
hint before monitor-only, unadmitted-selection, settlement and cadence cleanup.
There is no later scheduler/serialization refill. Source list absence remains
separate from an authoritative empty list; an empty frontier object remains a
frontier. Candidate order, selection-before-display truncation, local-source
counts, current-action priority, canonical frontier action priority and task
patch omission semantics retain their previous contracts.

Python retains existing Todo/text codecs and the shared
`actions_are_projection_aligned` owner as named compatibility facts. It does not
select route candidates, deduplicate, count or decide routes. Retire these
compatibility facts only after their shared source owners and final Python
quota assembly callers have native replacements. No provider reads, synthetic
inventory or Goal document scanning are introduced for route-only requests.

The hint remains advice nested inside the existing quota read model and its
`projection_envelope`, not a new authoritative read API. It grants no execution,
claim, lease or write permission, preserves the durable Goal Next Action and
introduces no persisted-state migration. No leaf RPC or Python fallback owner is
added. Rollback restores the preceding code/artifact and fingerprint; it does
not rewrite Goal state.

## Costs and qualification

Initial implementation used `d39662d79784e7afa9dbc844f61452a64cb92c1c`; the
earlier review base was `2c24d5d102dc35c417b3ce49e25096aa37e1642b`. Final
matched CLI evidence uses integration base
`2ce6a5dcd8a5396f5d60312f37958e1b05d529ca` and candidate
`db7f364385bb4418ea223623f0d1ffbd9745a1bd`.

| Local planning/route segment | Before | Candidate design |
| --- | --- | --- |
| Planning already required | One RPC plus Python route rules | One existing batch RPC |
| Route only | No planning RPC plus Python route rules | One route-only batch RPC |
| Neither required | No RPC | No RPC |

This is an ownership consolidation, not a claimed latency improvement. Measure
both execution and route-only wait/read branches. Keep the existing warm
non-persistent p95 target below 2 ms and complete-CLI owner-review gates for
p95 regression over 5% or unexplained added 25 ms. Preserve wire, coverage,
fixture and performance budgets; use the complete-snapshot transport rather
than truncating source rows. Do not retain a production dual-owner fallback if
performance fails: optimize the batch or hold/revert the cutover.

### Verified results and remaining holds

- **Passed:** generated-contract freshness, 29 contract/catalog tests, both
native-path canary selections, 214 focused Python tests, and all 12 matched
real CLI parity cases across SQLite/File at the revisions above.
- **Passed, focused semantic evidence:** 1,200 deterministic synthetic
Python-baseline versus codec/TS cases; all three independent mutations were
killed (nonempty-list fallback for executable candidates, current/frontier
priority reversal, and nonempty-list fallback for handoff gates). This is not
additional CLI, backend or performance evidence.
- **Passed:** premerge, with an inherited complexity advisory. The full native
suite recorded 6,105 passed, 30 failed and 77 skipped. All 30 failures passed
individually with isolation or a larger temporary filesystem after
runtime/filesystem failures; this does not recast the original aggregate run
as a clean pass.

The 20-sample full-command measurements retain these threshold exceedances:

| Case | Base p95 | Candidate p95 | Change |
| --- | --- | --- | --- |
| SQLite current JSON | 879.85 ms | 975.16 ms | +10.83%, +95.31 ms |
| SQLite current Markdown | 851.66 ms | 877.88 ms | +26.23 ms |
| File durable Markdown | 583.29 ms | 618.80 ms | +6.09%, +35.51 ms |

The other nine cases improved. A separate instrumented 20-pair SQLite current
JSON cohort measured full-command p95 723.73→694.55 ms and planning-RPC p95
1.294→1.797 ms. These distinct observations do not cancel the original
threshold exceedances or prove their cause. **Performance qualification remains
held for owner review**, not passed; no speedup is claimed.

Cold-start qualification, clean wheel/sdist installation, PostgreSQL and Host
coverage remain **not_yet_proven**. Keep full-command p50/p95, logical IPC,
wire/output bytes and persisted-state readback distinct from projection timing.
Record Python rules removed, compatibility code added and net production changes
separately from tests, generated files and docs. Later source changes need fresh
exact-head verification. These results grant neither merge nor deployment
authority.
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Goal 路由提示并入既有规划批处理

## 边界与兼容性

本切片将完整只读 Goal 路由投影从 Python 移入
`work_items/goal_route_hint.ts`,由既有
`work_item.action_portfolio.project` 批处理消费。Python 路由 builder、task
override 合并器与生产调用者在同一切片切换并退役。既有 `quota.run_decision`
owner 仍产生 task-orchestration override;本切片消费其结果,不重复实现该策略。

内部 `quota_planning_packet_request_v1` 包含各自可空的 planning 和 route
context,结果为 `quota_planning_packet_v1`。coordination 合同 JSON 登记两个身份,
既有生成器负责 Python/TypeScript 常量。原有 v0 的要求与结果形态保持原意;
外部嵌套的 `goal_route_hint_v0` 不变。

Python 在原 builder 位置、Agent lane、frontier 与 recommendation 选择之后捕获
route facts。既有 planning 调用在 monitor-only、未准入选择、结算及 cadence 清理
之前投影 hint;scheduler/序列化阶段不再回填。来源列表缺失与权威空列表保持区别;
空 frontier 对象仍命中 frontier 分支。候选顺序、先选择后截断展示、局部来源计数、
current action 优先级、canonical frontier action 优先级及 task patch 的省略语义
保留既有合同。

Python 保留既有 Todo/文本 codec,并将共享 `actions_are_projection_aligned`
owner 的结果作为命名兼容事实传递,不选择路由候选、不去重、不计数、不决定路由。
只有共享来源 owner 与最后的 Python quota 组装调用者均有原生替代后,才能退役这些
兼容事实。route-only 请求不引入 provider 读取、虚构 inventory 或 Goal 文档扫描。

hint 仍是既有 quota 读模型及其 `projection_envelope` 内的建议,不是新增权威读 API。
它不授予执行、claim、lease 或写权限,保留持久 Goal Next Action,不引入持久化格式
迁移。不新增 leaf RPC 或 Python 回退 owner。回滚恢复之前的代码/发行物与 fingerprint,
不改写 Goal 状态。

## 成本与资格

初始实现基线为 `d39662d79784e7afa9dbc844f61452a64cb92c1c`;早期审阅基线为
`2c24d5d102dc35c417b3ce49e25096aa37e1642b`。最终 CLI 配对证据使用集成基线
`2ce6a5dcd8a5396f5d60312f37958e1b05d529ca` 与候选
`db7f364385bb4418ea223623f0d1ffbd9745a1bd`。

| 局部 planning/route 段 | 修改前 | 候选设计 |
| --- | --- | --- |
| 已需要 planning | 一次 RPC 加 Python 路由规则 | 一次既有 batch RPC |
| 仅需 route | 零次 planning RPC 加 Python 路由规则 | 一次 route-only batch RPC |
| 两者都不需要 | 零次 RPC | 零次 RPC |

这是归属收敛,不宣称降低延迟。执行分支与 route-only 等待/只读分支须分别测量。
保留 warm 非持久化转换 p95 小于 2 ms 的既有目标;完整 CLI p95 回退超过 5% 或
存在无法解释的 25 ms 增量时,进入 owner review。不得放宽 wire、coverage、fixture
或性能预算;使用完整快照传输,不截断来源行。性能失败不得保留生产双 owner 回退:
应优化批处理,或暂停/回滚切换。

### 已验证结果与剩余门禁

- **Passed:**生成合同新鲜度、29 项合同/catalog 测试、两个原生路径 canary
选择、214 项聚焦 Python 测试,以及上述 revision 的 SQLite/File 共 12 项
真实 CLI 配对一致性用例全部通过。
- **Passed,聚焦语义证据:**1,200 项确定性合成 Python baseline 与 codec/TS
对照通过;三个独立 mutation 均被测试击败(可执行候选按列表非空而非存在回退、
current/frontier 优先级颠倒、handoff gate 按列表非空而非存在回退)。这些不构成
额外的 CLI、后端或性能证据。
- **Passed:**premerge 通过,保留继承的复杂度 advisory。完整原生测试运行记录为
6,105 passed、30 failed、77 skipped。运行时/临时文件系统失败后,30 项失败用例
均在隔离或扩大临时文件系统后单独通过;这不将原始整套运行改记为全部通过。

20 样本完整命令测量保留以下超阈值结果:

| 场景 | 基线 p95 | 候选 p95 | 变化 |
| --- | --- | --- | --- |
| SQLite current JSON | 879.85 ms | 975.16 ms | +10.83%,+95.31 ms |
| SQLite current Markdown | 851.66 ms | 877.88 ms | +26.23 ms |
| File durable Markdown | 583.29 ms | 618.80 ms | +6.09%,+35.51 ms |

其余九项场景改善。另一组带 instrumentation 的 20 对 SQLite current JSON 测量中,
完整命令 p95 为 723.73→694.55 ms,planning RPC p95 为 1.294→1.797 ms。
这些独立观察不能抵消原始超阈值结果,也不能证明其原因。**性能资格仍待 owner review,
不是 passed**,不宣称性能提升。

冷启动资格、干净 wheel/sdist 安装、PostgreSQL 与 Host 覆盖仍为
**not_yet_proven**。完整命令 p50/p95、逻辑 IPC、wire/输出字节及持久状态 readback
须与投影耗时分开记录。删除的 Python 规则、新增兼容代码、生产代码净变化与测试、
生成物及文档分别统计。后续源码变更须重新执行 exact-head 验证。这些结果不授予
合并或部署权限。
Original file line number Diff line number Diff line change
Expand Up @@ -1765,10 +1765,13 @@ The task-scoped `goal_route_hint` override now comes back in that same
`quota.run_decision` response. Python projects the lane count from the existing
task-orchestration source fact. TypeScript owns the ready-state check, route
decision and coordination wording. Python's duplicate `task_goal_route_hint` decision is
deleted, leaving only the bounded merge into the assembled hint. This adds no
request, provider read or authority. Other Goal-route and primary-action
builders remain separate migration work; this does not complete the quota read
transaction or claim a performance gain.
deleted. The complete Goal-route projection and override merge now move to the
existing planning batch, with a route-only internal v1 request when planning
views are absent. This can add a planning RPC on route-only paths; it adds no
provider read or authority. Primary-action builders remain separate work. See
the [Goal-route checkpoint](ledger/typescript-control-plane-migration-v0/2026-11-05-goal-route-planning-batch.md)
for compatibility, qualification and cost gates; neither the complete quota
read transaction nor a performance gain is claimed.

**Long-history transport boundary.** Replan history still has one TS decision
owner. Small requests retain the inline codec; larger complete fact snapshots
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1263,9 +1263,12 @@ TS 生产者验证器通过共享 guard fixture 执行真实 TS 分类器,并
Task-scoped `goal_route_hint` override 现在由同一个
`quota.run_decision` 响应返回。Python 从既有 task-orchestration source fact 投影
lane 数。TypeScript 持有 ready 状态检查、route 判定和协调说明。Python 重复实现的
`task_goal_route_hint` 判定已删除,只保留将有界补丁合并进已组装 hint 的步骤。
本改动不增加请求、provider 读取或 authority。其他 Goal route 与 primary-action
builder 仍是独立迁移工作;这不代表 quota read transaction 已完成,也不宣称性能提升。
`task_goal_route_hint` 判定已删除。完整 Goal route 投影与 override 合并现迁入既有
planning batch;没有 planning view 时使用 route-only 内部 v1 请求。这可能为
route-only 路径增加一次 planning RPC,但不增加 provider 读取或 authority。
primary-action builder 仍是独立工作。兼容性、资格及成本门见
[Goal route 检查点](ledger/typescript-control-plane-migration-v0/2026-11-05-goal-route-planning-batch.zh-CN.md);
不宣称完整 quota read transaction 已完成或性能提升。

**长历史传输边界。** Replan 历史仍由一个 TS owner 决策。小请求保留 inline
codec;较大的完整事实快照通过私有临时文件和摘要绑定的引用传递。同一 reducer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

from __future__ import annotations

import hashlib
import json
import shlex
import subprocess
import sys
import tempfile
from pathlib import Path
Expand Down Expand Up @@ -86,7 +90,7 @@ def write_fixture(root: Path, *, include_review: bool = False) -> tuple[Path, Pa


def should_run(project: Path, runtime: Path, registry_path: Path, agent_id: str) -> dict:
return run_cli(
payload = run_cli(
"quota",
"should-run",
"--goal-id",
Expand All @@ -98,6 +102,39 @@ def should_run(project: Path, runtime: Path, registry_path: Path, agent_id: str)
runtime=runtime,
cwd=project,
)
if payload.get("schema_version") != "context_capture_navigation_v1":
return payload
# Read the original capture, never rerun admission to recover its decision.
# Full traversal here is a semantic test oracle, not a model read requirement.
command = payload["recovery"]["command"]
content = bytearray()
identity = None
while True:
words = shlex.split(command)
quota_index = words.index("quota")
assert words[quota_index + 1] == "context-read", command
result = subprocess.run(
words, cwd=REPO_ROOT, capture_output=True, text=True,
encoding="utf-8", errors="strict", check=False, timeout=30,
)
assert result.returncode == 0, result.stdout + result.stderr
page = json.loads(result.stdout)
assert page["ok"] is True and page["offset"] == len(content), page
page_identity = (page["sha256"], page["total_bytes"])
if identity is None:
identity = page_identity
assert page_identity == identity, page
chunk = page["content"].encode("utf-8")
assert chunk, "context read made no progress"
content.extend(chunk)
if page["eof"] is True:
assert len(content) == identity[1], page
assert hashlib.sha256(content).hexdigest() == identity[0], page
resolved = json.loads(content)
assert isinstance(resolved, dict), resolved
return resolved
assert page["next_offset"] == len(content) < identity[1], page
command = page["next_command"]


def assert_same_peer_continuation() -> None:
Expand Down
2 changes: 1 addition & 1 deletion loopx/canary/planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@
"loopx/control_plane/work_items/interaction_contract.py",
"loopx/control_plane/todos/handoff_gate.py",
"loopx/control_plane/work_items/execution_obligation.py",
"loopx/control_plane/work_items/goal_route_hint.py",
"loopx/control_plane/work_items/goal_route_hint.ts",
"loopx/control_plane/work_items/delivery_history.py",
"loopx/control_plane/work_items/delivery_history.ts",
"loopx/control_plane/work_items/work_lane.py",
Expand Down
2 changes: 1 addition & 1 deletion loopx/canary/qualification_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
"loopx/control_plane/testing/decision_replay.py",
"loopx/control_plane/work_items/interaction_contract.py",
"loopx/control_plane/work_items/execution_obligation.py",
"loopx/control_plane/work_items/goal_route_hint.py",
"loopx/control_plane/work_items/goal_route_hint.ts",
"loopx/control_plane/work_items/delivery_history.py",
"loopx/control_plane/work_items/delivery_history.ts",
"loopx/control_plane/work_items/work_lane.py",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ export const ACTION_PORTFOLIO_SELECTION_REQUEST_SCHEMA = "action_selection_quali
export const ACTION_PORTFOLIO_SELECTION_RESULT_SCHEMA = "action_selection_qualification_v0";
export const ACTION_PORTFOLIO_PLANNING_PACKET_REQUEST_SCHEMA = "quota_planning_packet_request_v0";
export const ACTION_PORTFOLIO_PLANNING_PACKET_RESULT_SCHEMA = "quota_planning_packet_v0";
export const ACTION_PORTFOLIO_PLANNING_PACKET_REQUEST_V1_SCHEMA = "quota_planning_packet_request_v1";
export const ACTION_PORTFOLIO_PLANNING_PACKET_RESULT_V1_SCHEMA = "quota_planning_packet_v1";

export const TODO_RESUME_NORMALIZE_REQUEST_SCHEMA = "todo_resume_normalize_request_v0";
export const TODO_RESUME_EVALUATION_REQUEST_SCHEMA = "todo_resume_evaluation_request_v0";
Expand Down Expand Up @@ -355,7 +357,9 @@ export const COORDINATION_STATE_CONTRACT = deepFreeze({
"selection_request_schema": ACTION_PORTFOLIO_SELECTION_REQUEST_SCHEMA,
"selection_result_schema": ACTION_PORTFOLIO_SELECTION_RESULT_SCHEMA,
"planning_packet_request_schema": ACTION_PORTFOLIO_PLANNING_PACKET_REQUEST_SCHEMA,
"planning_packet_result_schema": ACTION_PORTFOLIO_PLANNING_PACKET_RESULT_SCHEMA
"planning_packet_result_schema": ACTION_PORTFOLIO_PLANNING_PACKET_RESULT_SCHEMA,
"planning_packet_request_v1_schema": ACTION_PORTFOLIO_PLANNING_PACKET_REQUEST_V1_SCHEMA,
"planning_packet_result_v1_schema": ACTION_PORTFOLIO_PLANNING_PACKET_RESULT_V1_SCHEMA
},
"todo_resume_protocol": {
"normalize_request_schema": TODO_RESUME_NORMALIZE_REQUEST_SCHEMA,
Expand Down
Loading
Loading