Skip to content

Commit f3cd2bd

Browse files
committed
test(cli-output): declare the bounded managed executor readback budget
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
1 parent cbc08e3 commit f3cd2bd

4 files changed

Lines changed: 115 additions & 0 deletions

File tree

‎examples/control_plane/cli-output-probe-runner.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ def _receipt_row(
108108
"reward_memory_outcome_prompt_revision": (
109109
semantics.reward_memory_outcome_prompt_revision(text)
110110
),
111+
"managed_executor_binding_revision": (
112+
semantics.managed_executor_binding_revision(text)
113+
),
111114
"guided_todo_delta_schema_versions": (
112115
semantics.guided_todo_delta_schema_versions(payload)
113116
if isinstance(payload, dict)

‎loopx/control_plane/testing/cli_output_differential.py‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,45 @@ class GrowthAllowance:
177177
"compact_payload_chars": 640,
178178
}
179179

180+
# The Turn plan readback adds one bounded managed-executor binding so a caller
181+
# sees which executor a planned Turn would use and whether it can launch here,
182+
# instead of inferring it from the host id. The allowance is bound to the
183+
# declared none-to-v0 binding transition and to the Turn surfaces that quote
184+
# it; quota, status, and every other agent-facing surface keep the ordinary
185+
# budget, and once v0 is the baseline a v0-to-v0 change receives no allowance.
186+
_MANAGED_EXECUTOR_BINDING_V0_MIGRATION_GROWTH_ALLOWANCE: dict[Metric, int] = {
187+
"chars": 512,
188+
"utf8_bytes": 512,
189+
"lines": 12,
190+
"compact_payload_chars": 448,
191+
}
192+
193+
_MANAGED_EXECUTOR_BINDING_SURFACES = frozenset(
194+
{
195+
"loopx_turn_plan",
196+
"loopx_turn_plan_transaction_detail",
197+
"loopx_turn_run_once_preview",
198+
}
199+
)
200+
201+
202+
def _managed_executor_binding_allowance(
203+
row_id: str,
204+
base: Mapping[str, Any],
205+
candidate: Mapping[str, Any],
206+
metric: Metric,
207+
) -> int:
208+
surface = row_id.partition("/")[2].partition("/")[0]
209+
if (
210+
row_id.startswith(("surface/", "variant/"))
211+
and surface in _MANAGED_EXECUTOR_BINDING_SURFACES
212+
and base.get("managed_executor_binding_revision") is None
213+
and candidate.get("managed_executor_binding_revision")
214+
== "managed_executor_binding_v0"
215+
):
216+
return _MANAGED_EXECUTOR_BINDING_V0_MIGRATION_GROWTH_ALLOWANCE[metric]
217+
return 0
218+
180219

181220
def _reward_memory_outcome_prompt_allowance(
182221
row_id: str,
@@ -545,6 +584,12 @@ def _compare_row(base: dict[str, Any], candidate: dict[str, Any]) -> dict[str, A
545584
candidate,
546585
metric,
547586
),
587+
_managed_executor_binding_allowance(
588+
row_id,
589+
base,
590+
candidate,
591+
metric,
592+
),
548593
)
549594
# Thin installed prompts contain bilingual lifecycle instructions. A
550595
# small character-level clarification can cost three bytes per CJK

‎loopx/control_plane/testing/cli_output_semantics.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,28 @@ def reward_memory_outcome_prompt_revision(text: str) -> str | None:
4141
else None
4242
)
4343

44+
45+
def managed_executor_binding_revision(text: str) -> str | None:
46+
"""Attribute the managed-executor binding readback on a Turn surface.
47+
48+
This is qualification evidence for the exact projection, never a runtime
49+
classifier: the binding key alone would match prose, so the revision also
50+
requires the executor identity, its launchability claim, and the typed
51+
reason slot that only this readback renders.
52+
"""
53+
54+
required = (
55+
'"managed_executor"',
56+
'"executor_kind"',
57+
'"available"',
58+
'"unavailable_reason"',
59+
)
60+
return (
61+
"managed_executor_binding_v0"
62+
if all(fragment in text for fragment in required)
63+
else None
64+
)
65+
4466
_MARKDOWN_HEADING = re.compile(r"^#{1,6}\s+.+$")
4567
_RUNTIME_ROOT_COMMAND_ROUTE = re.compile(
4668
r"(?m)(?:^|[\"'`])[^\r\n\S]*loopx\s+--runtime-root\s+"

‎tests/control_plane/test_cli_output_differential.py‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,51 @@ def test_reward_memory_outcome_prompt_budget_is_one_time_bounded_and_prompt_only
146146
]
147147

148148

149+
def test_managed_executor_binding_budget_is_one_time_bounded_and_turn_only() -> None:
150+
from loopx.control_plane.testing.cli_output_differential import _compare_row
151+
from loopx.control_plane.testing.cli_output_semantics import (
152+
managed_executor_binding_revision,
153+
)
154+
155+
projection = (
156+
'{\n "managed_executor": {\n'
157+
' "executor_kind": "managed",\n'
158+
' "available": true,\n'
159+
' "unavailable_reason": null\n }\n}'
160+
)
161+
assert (
162+
managed_executor_binding_revision(projection)
163+
== "managed_executor_binding_v0"
164+
)
165+
assert (
166+
managed_executor_binding_revision(
167+
projection.replace('"unavailable_reason"', '"reason"')
168+
)
169+
is None
170+
)
171+
172+
base = _row(row_id="variant/loopx_turn_run_once_preview/small/json")
173+
current = {
174+
**base,
175+
"chars": base["chars"] + 254,
176+
"utf8_bytes": base["utf8_bytes"] + 254,
177+
"lines": base["lines"] + 9,
178+
"compact_payload_chars": base["compact_payload_chars"] + 205,
179+
"managed_executor_binding_revision": "managed_executor_binding_v0",
180+
}
181+
assert not _compare_row(base, current)["failures"]
182+
assert _compare_row(base, {**current, "chars": base["chars"] + 513})[
183+
"failures"
184+
]
185+
assert _compare_row(current, {**current, "chars": current["chars"] + 254})[
186+
"failures"
187+
]
188+
other = {**base, "row_id": "surface/status/small/json"}
189+
assert _compare_row(other, {**current, "row_id": other["row_id"]})[
190+
"failures"
191+
]
192+
193+
149194
def test_regular_integration_pr_keeps_requested_cli_output_base() -> None:
150195
ancestors = {
151196
("origin/main", "HEAD"),

0 commit comments

Comments
 (0)