Skip to content

Commit 603a976

Browse files
committed
refactor(operations): keep source resolution inside managed admission
Signed-off-by: huangruiteng <huangrt01@163.com>
1 parent ddedae7 commit 603a976

6 files changed

Lines changed: 30 additions & 23 deletions

File tree

‎docs/architecture/rfcs/human-confirmed-domain-operations-v0.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,8 @@ option, `--codex-operation-source-route-json
470470
The selector is registered-audience routing, not authentication, a session
471471
replacement or an execution permit; the model cannot retarget it. Managed duplicate
472472
bindings collapse. No registered audience retains the historical null route;
473-
non-managed adapters retain their previous count-based implicit routing behavior. Store
473+
non-managed adapters retain their previous no-source-route projection and do not
474+
invoke this managed resolver. Store
474475
preparation freezes the selected audience in the original confirmation digest.
475476

476477
Source-audience selection and the preparation prompt form a bounded backend

‎docs/architecture/rfcs/human-confirmed-domain-operations-v0.zh-CN.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,8 +365,8 @@ Core PR 仍须 owner review,不在合并前自行安装。
365365
受众时,必须由宿主显式传入 `--codex-operation-source-route-json
366366
'{"host_surface":"codex-app","thread_id":"REGISTERED_THREAD"}'`。
367367
这只是已登记的回传受众,不是认证、session 替换或执行许可;模型不能改投。
368-
受管重复绑定会去重;无登记受众保持历史 null 路由,非受管 adapter 保留既有计数式隐式
369-
路由行为。准备落盘后,返回受众进入原确认摘要,不可修改。
368+
受管重复绑定会去重;无登记受众保持历史 null 路由,非受管 adapter 保留既有
369+
无 source-route 投影,不调用这条受管解析。准备落盘后,返回受众进入原确认摘要,不可修改。
370370

371371
源受众选择与准备提示构成有界后台切片,不是完整产品交付。下面的个人工作台
372372
配套改动仍在同一计划内单独完成首屏评审后交付。工作台在可见时读取规范

‎loopx/chat_action_normalization.py‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -237,15 +237,16 @@ def _normalize(
237237
managed_source = executor.get("kind") == "managed_turn"
238238
if not managed_source and "source_route" in values:
239239
raise ValueError("source route selection requires a managed executor")
240-
from .control_plane.effect_runtime import effect_runtime_result
240+
source_route = None
241+
if managed_source:
242+
from .control_plane.effect_runtime import effect_runtime_result
241243

242-
source_route = effect_runtime_result(
243-
"operation.source_route.resolve",
244-
{"goal_id": goal_id, "agent_id": agent_id,
245-
"bindings": (goal.get("coordination") or {}).get("thread_agent_bindings", []),
246-
"selected_route": values.get("source_route"),
247-
"ambiguity_policy": "reject" if managed_source else "legacy_null"},
248-
)["source_route"]
244+
source_route = effect_runtime_result(
245+
"operation.source_route.resolve",
246+
{"goal_id": goal_id, "agent_id": agent_id,
247+
"bindings": (goal.get("coordination") or {}).get("thread_agent_bindings", []),
248+
"selected_route": values.get("source_route")},
249+
)["source_route"]
249250
expires_at = parse_timestamp(
250251
_text(values.get("expires_at"), field="expires_at", limit=80)
251252
)

‎loopx/control_plane/work_items/operation_agent_handoff.ts‎

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,12 @@ export function resolveOperationSourceRoute(input: JsonObject): JsonObject {
3434
const agent = id(input.agent_id, "agent_id");
3535
const bindings = Array.isArray(input.bindings) ? input.bindings : [];
3636
const routes = new Map<string, JsonObject>();
37-
let registeredCount = 0;
3837
for (const raw of bindings) {
3938
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) continue;
4039
const route = raw as JsonObject;
4140
if (route.agent_id !== agent || typeof route.host_surface !== "string"
4241
|| typeof route.thread_id !== "string" || !ID.test(route.host_surface) || !ID.test(route.thread_id)) continue;
4342
const audience = {goal_id: goal, agent_id: agent, host_surface: route.host_surface, thread_id: route.thread_id};
44-
registeredCount += 1;
4543
routes.set(JSON.stringify(audience), audience);
4644
}
4745
if (input.selected_route != null) {
@@ -55,11 +53,6 @@ export function resolveOperationSourceRoute(input: JsonObject): JsonObject {
5553
requireThat(routes.has(JSON.stringify(audience)), "operation source route is not registered for this Goal and Agent");
5654
return {source_route: audience};
5755
}
58-
if (input.ambiguity_policy === "legacy_null") {
59-
// Preserve the old non-managed count-based behavior, including duplicated
60-
// registry rows. Distinct-audience de-duplication is managed opt-in only.
61-
return {source_route: registeredCount === 1 ? routes.values().next().value : null};
62-
}
6356
if (routes.size > 1) throw new EffectRuntimeRequestError(
6457
"operation source route is ambiguous; select a registered return audience in the host invocation",
6558
"operation_source_route_ambiguous");

‎tests/control_plane_ts/operation_agent_handoff.test.ts‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@ test("registered return audience is explicit under ambiguity and never executor
1212
assert.equal(resolveOperationSourceRoute({...value, bindings: []}).source_route, null);
1313
const multiple = {...value, bindings: [route, {...route, thread_id: "historical"}]};
1414
assert.throws(() => resolveOperationSourceRoute(multiple), {code: "operation_source_route_ambiguous"});
15-
// Unchanged non-managed callers retain their historical null-on-ambiguity
16-
// routing behavior; the normalizer never lets a model choose this policy.
17-
assert.equal(resolveOperationSourceRoute({...multiple, ambiguity_policy: "legacy_null"}).source_route, null);
18-
assert.equal(resolveOperationSourceRoute({...value, bindings: [route, route], ambiguity_policy: "legacy_null"}).source_route, null);
19-
assert.deepEqual(resolveOperationSourceRoute({...value, ambiguity_policy: "legacy_null"}), resolveOperationSourceRoute(value));
2015
const selector = {host_surface: route.host_surface, thread_id: route.thread_id};
2116
assert.deepEqual(resolveOperationSourceRoute({...multiple, selected_route: selector}), resolveOperationSourceRoute(value));
2217
for (const selected_route of ["original", {...selector, thread_id: "unregistered"},

‎tests/test_chat_operation_actions.py‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,23 @@ def test_non_managed_prepare_rejects_source_selector_even_when_null(tmp_path: Pa
192192
assert store.list() == []
193193

194194

195+
def test_non_managed_prepare_does_not_mount_managed_source_resolution(tmp_path: Path, monkeypatch) -> None:
196+
from loopx.control_plane import effect_runtime
197+
198+
original = effect_runtime.effect_runtime_result
199+
calls = []
200+
201+
def record(method, *args, **kwargs):
202+
calls.append(method)
203+
return original(method, *args, **kwargs)
204+
205+
monkeypatch.setattr(effect_runtime, "effect_runtime_result", record)
206+
service, store = _service(tmp_path)
207+
proposal = service.preview(_request())
208+
assert "source_route" not in store.load(proposal["proposal_id"])["normalized_parameters"]
209+
assert "operation.source_route.resolve" not in calls
210+
211+
195212
def test_managed_prepare_selects_registered_return_audience_without_rebinding_executor(tmp_path: Path) -> None:
196213
from loopx.control_plane.turn_driver.codex_operation_host import operation_tool_handler
197214

0 commit comments

Comments
 (0)