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
Expand Up @@ -56,9 +56,9 @@ export function parseEditableCapabilityJson(
export function replanCadenceEditorValue(value: unknown): unknown {
const record = configurationObject(value);
if (Object.hasOwn(record, "completed_todos") && !Object.hasOwn(record, "count_unit")) {
return { count_unit: "completed_todos", count: record.completed_todos };
return { count_unit: "completed_todos", count: record.completed_todos, stride_strategy: "off" };
}
return value;
return { ...record, stride_strategy: record.stride_strategy ?? "off" };
}

/** A v0 storage selector did not enable creation; opening Settings must not. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const capabilityCopy: Record<WorkspaceLocale, Record<string, LocalizedCopy>> = {
displayName: "Model and executor",
description: "Guides the steward executor, model, and selection boundary for this machine. A pinned route blocks substitution; a flexible pool permits only authorized fallback.",
},
todo_replan_cadence: { displayName: "Goal review cadence", description: "Configures the Goal review cadence." },
todo_replan_cadence: { displayName: "Goal review and stride", description: "Set review frequency and optionally choose a versioned stride strategy for work scope and route changes." },
change_quality_qualification: {
displayName: "Change quality qualification",
description: "Prepares a provider-neutral review packet, allows at most one policy-authorized safe-fix pass, and can require an exact-diff receipt.",
Expand Down Expand Up @@ -89,7 +89,7 @@ const capabilityCopy: Record<WorkspaceLocale, Record<string, LocalizedCopy>> = {
displayName: "模型与执行器",
description: "配置本机管家的执行器、模型与选择边界;锁定路径禁止替代,灵活池只允许在已授权范围内回退。",
},
todo_replan_cadence: { displayName: "Goal 复核周期", description: "配置 Goal 的复核周期。" },
todo_replan_cadence: { displayName: "Goal 复盘与步幅", description: "设置复盘周期,可选用带版本的步幅策略,调节工作大小与路线变化。" },
change_quality_qualification: {
displayName: "变更质量验证",
description: "生成与 Provider 无关的审阅包,最多允许一次策略授权的安全修复,并可要求精确 diff 回执。",
Expand Down Expand Up @@ -159,6 +159,7 @@ const fieldCopy: Record<WorkspaceLocale, FieldCopy> = {
executor_model: { label: "Model", description: "Optional model for the selected executor. Leave blank to keep the executor's own default." },
executor_reasoning_effort: { label: "Reasoning effort", description: "Optional reasoning effort for the selected executor. Leave blank to keep the executor's own default." },
count_unit: { label: "Review after", description: "Work Turns require accepted settlement; polls and retries do not count.", options: { completed_todos: "Completed Todos", effective_turns: "Settled work Turns" } },
stride_strategy: { label: "Stride strategy", description: "Structured work scope, route and validation proposals are required for enabled fresh Vision reviews. Does not grant execution authority.", options: { off: "Off", evidence_conditioned_v1: "Evidence-conditioned · v1" } },
count: { label: "Number between reviews", description: "Settled work Turns: 1–6; completed Todos: 1–5. Goal overrides take precedence over device defaults." },
allowed_domains: { label: "Allowed responsibility domains", description: "Enter one bounded, public-safe domain per line." },
coordinator_agent_id: { label: "Coordinator Agent", description: "Use an already registered Agent id; leave blank to disable coordination." },
Expand Down Expand Up @@ -191,6 +192,7 @@ const fieldCopy: Record<WorkspaceLocale, FieldCopy> = {
executor_model: { label: "模型", description: "所选执行器使用的模型,可留空;留空表示沿用执行器自身的默认模型。" },
executor_reasoning_effort: { label: "推理档位", description: "所选执行器使用的推理档位,可留空;留空表示沿用执行器自身的默认档位。" },
count_unit: { label: "复核计数依据", description: "有效工作 Turn 须完成结算;轮询和重复重试不计数。", options: { completed_todos: "已完成 Todo", effective_turns: "已结算工作 Turn" } },
stride_strategy: { label: "步幅策略", description: "启用后,新的 Vision 路线复盘须提交工作大小、路线及验证范围的结构化提案,不授予执行权限。", options: { off: "关闭", evidence_conditioned_v1: "依据证据调整 · v1" } },
count: { label: "两次复核间的数量", description: "已结算工作 Turn 为 1–6,已完成 Todo 为 1–5;Goal 显式设置优先于设备默认值。" },
allowed_domains: { label: "允许的职责域", description: "每行填写一个有边界、可公开的职责域。" },
coordinator_agent_id: { label: "协调 Agent", description: "填写一个已经注册的 Agent ID;留空表示关闭协调。" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1064,7 +1064,7 @@ const en = {
"machine.periodicReportActivationDescription": "This is not a weekly timer. When LoopX validates a Goal stage completion, an Agent prepares and freezes the report, then automatically delivers it through the configured Goal Channel. The enabled subscription is standing delivery authority; failures and route drift stop closed for repair.",
"machine.changeQualityActivation": "Qualification is a quality gate, not new authority",
"machine.changeQualityActivationDescription": "Goals that inherit this policy qualify their exact final diff. safe_fix allows at most one bounded fix pass inside existing write authority; this setting never grants file, permission, or merge authority.",
"machine.replanCadenceActivation": "Review cadence changes timing, not execution authority",
"machine.replanCadenceActivation": "Review timing and stride policy do not grant execution authority",
"machine.replanCadenceActivationDescription": "Goals without an explicit override read this threshold before the next review decision. It does not create Turns, spend quota, or grant authority.",
"machine.preview": "Review exact machine change",
"machine.previewChanges": "Preview changes",
Expand Down Expand Up @@ -2467,7 +2467,7 @@ const zhCN: Record<WorkspaceMessageKey, string> = {
"machine.periodicReportActivationDescription": "这不是每周定时器。当 LoopX 验证 Goal 已完成一个阶段时,Agent 会生成并冻结报告,随后通过配置的 Goal Channel 自动发送。启用此订阅即授予持续投递权;发送失败或路由漂移会 fail closed 并进入修复。",
"machine.changeQualityActivation": "质量验证是质量门禁,不是新增授权",
"machine.changeQualityActivationDescription": "继承该策略的 Goal 会验证最终精确 diff。safe_fix 只允许在既有写入权限内执行至多一次有界修复;此设置不会授予文件、权限或合并权。",
"machine.replanCadenceActivation": "复核周期只改变时机,不改变执行权限",
"machine.replanCadenceActivation": "复盘周期与步幅策略,不扩大执行权限",
"machine.replanCadenceActivationDescription": "没有显式覆盖的 Goal 会在下一次复核决策前读取此阈值;它不会创建 Turn、消耗配额或授予权限。",
"machine.preview": "审阅机器配置变更",
"machine.previewChanges": "预览变更",
Expand Down
19 changes: 19 additions & 0 deletions benchmark/edgebench/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,25 @@ other replan reasons may trigger sooner. Both runtime and worker-profile receipt
record the resolved cadence. Only newly launched attempts use the new default;
keep existing runs and archived settings pinned.

### Optional structured stride strategy

Add `--stride-strategy evidence_conditioned_v1` to a new `heartbeat-resume`
or `heartbeat-explore` trial to opt into the existing Goal review strategy.
The runner writes the option together with the resolved cadence and checks
the real Goal configuration before planning or execution. The install,
worker-profile and runtime receipts record an explicitly selected version.
Omitting the option preserves ordinary off commands and receipt shapes;
`--stride-strategy off` explicitly selects the disabled treatment. Plain and
native-goal profiles reject this option before creating a trial. Unknown
versions also fail before registration or execution.

The [Goal review contract](../../docs/reference/protocols/goal-vision-replan-contract-v0.md)
defines structured proposals and the three independently controlled axes.
Strategy configuration grants no new execution authority, evaluator feedback,
budget or submissions. Freeze it with exact sources under a new run ID; do not
change live workers. Receipts qualify requested configuration, while actual
review, proposal adoption and task gains require trajectory evidence.

## Optional short-context treatment

Add `--turn-envelope` to an otherwise identical `heartbeat-resume` or
Expand Down
8 changes: 8 additions & 0 deletions benchmark/edgebench/export_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ def export(runs_root: Path, selection: list, output: Path, *, observed_at: str):
if row.get("runner_revision") != receipt["runner_commit"]:
raise ValueError("Board and runner revision disagree")
_validate_feedback_payload(receipt, profile)
if receipt.get("stride_strategy") != profile.get("stride_strategy"):
raise ValueError("Profile disagrees with runtime: stride_strategy")
if "stride_strategy" in receipt:
from loopx.control_plane.work_items.stride_policy_codec import normalize_stride_strategy
normalize_stride_strategy(receipt["stride_strategy"])
if number(final["best_score"]) != number(receipt["best_score"]):
raise ValueError("Final and runtime scores disagree")
score_entries = [
Expand Down Expand Up @@ -273,6 +278,9 @@ def export(runs_root: Path, selection: list, output: Path, *, observed_at: str):
if "feedback_payload" in receipt:
config["runner"]["feedback_payload"] = receipt["feedback_payload"]
config["worker_profile"]["feedback_payload"] = profile["feedback_payload"]
if "stride_strategy" in receipt:
config["runner"]["stride_strategy"] = receipt["stride_strategy"]
config["worker_profile"]["stride_strategy"] = profile["stride_strategy"]
row["status"], row["observed_at"] = "completed", observed_at
row["metrics"]["best_score"] = {
"value": final["best_score"],
Expand Down
11 changes: 10 additions & 1 deletion benchmark/edgebench/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from sforge.harness.task_spec import make_task_spec

from benchmark.runtime.codex import TASK_ENTRIES
from loopx.control_plane.work_items.stride_policy_codec import stride_strategy_options
from benchmark.runtime.sforge import (
DEFAULT_TIMEOUT_SECONDS, PROFILES, SForgeWorker,
)
Expand Down Expand Up @@ -116,6 +117,8 @@ def main(argv=None):
help="Heartbeat default: 6 settled effective work Turns")
cadence.add_argument("--replan-after-todos", type=int, choices=range(1, 6),
help="Explicit completed-Todo cadence ablation for heartbeat profiles")
parser.add_argument("--stride-strategy", choices=stride_strategy_options(),
help="Explicit heartbeat Goal-review strategy; omitted keeps the ordinary off treatment")
parser.add_argument("--eval-interval", type=int,
help="Native default: 0 (no periodic evaluation); other modes use task defaults; explicit 0 disables")
parser.add_argument("--submission-cooldown", type=int,
Expand All @@ -137,6 +140,8 @@ def main(argv=None):
if (args.replan_after_turns is not None or args.replan_after_todos is not None
) and not args.worker.startswith("heartbeat-"):
parser.error("Replan cadence requires a heartbeat worker")
if args.stride_strategy is not None and not args.worker.startswith("heartbeat-"):
parser.error("--stride-strategy requires a heartbeat worker")
# One directory is one attempt: never reuse native registration or overwrite
# source/profile evidence after an ambiguous launch.
trial = args.log_dir / "runs" / args.run_id / args.task
Expand Down Expand Up @@ -189,7 +194,9 @@ def main(argv=None):
task_entry=args.task_entry,
turn_envelope=args.turn_envelope,
replan_after_turns=args.replan_after_turns,
replan_after_todos=args.replan_after_todos)
replan_after_todos=args.replan_after_todos,
**({"stride_strategy": args.stride_strategy}
if args.stride_strategy is not None else {}))
if args.api_proxy_url:
agent.default_api_base_url = args.api_proxy_url
logger = logging.getLogger("edgebench-runtime")
Expand Down Expand Up @@ -233,6 +240,8 @@ def main(argv=None):
if agent.replan_after_turns is not None else {}),
**({"replan_after_completed_todos": agent.replan_after_todos}
if agent.replan_after_todos is not None else {}),
**({"stride_strategy": args.stride_strategy}
if args.stride_strategy is not None else {}),
}
receipt_path = trial / "runtime-receipt.json"
receipt_path.write_text(json.dumps(receipt, indent=2))
Expand Down
21 changes: 19 additions & 2 deletions benchmark/runtime/harbor.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
from harbor.models.trajectories import FinalMetrics, Trajectory
from harbor.utils.trajectory_utils import format_trajectory_json

from loopx.control_plane.work_items.stride_policy_codec import normalize_stride_strategy

from .codex_offline import CodexOffline, stage_portable_python
from .codex import DEFAULT_REPLAN_AFTER_TURNS, Execution

Expand Down Expand Up @@ -56,6 +58,7 @@ def __init__(
scheduler_timeout_sec=5080,
replan_after_todos=None,
replan_after_turns=None,
stride_strategy=None,
task_entry=None,
planning_timeout_sec=300,
turn_envelope=False,
Expand Down Expand Up @@ -95,6 +98,10 @@ def __init__(
self.replan_after_todos = int(3 if replan_after_todos is None else replan_after_todos)
if not 1 <= self.replan_after_todos <= 5:
raise ValueError("replan_after_todos must be between 1 and 5")
if stride_strategy is not None and not self.execution.uses_loopx:
raise ValueError("stride_strategy requires a LoopX execution mode")
self.stride_strategy = (normalize_stride_strategy(stride_strategy)
if stride_strategy is not None else None)
self._task_path = "/usr/local/bin:/usr/bin:/bin"
self._phase_number = 0
self._seeded_todo_id: str | None = None
Expand All @@ -113,9 +120,10 @@ def _replan_configuration(self) -> tuple[str, str, int]:
return ("replan_after_completed_todos", "--execution-replan-after-todos",
self.replan_after_todos)

def _replan_receipt(self) -> dict[str, int]:
def _replan_receipt(self) -> dict[str, int | str]:
key, _, value = self._replan_configuration()
return {key: value}
return {key: value, **({"stride_strategy": self.stride_strategy}
if self.stride_strategy is not None else {})}

@staticmethod
def name() -> str:
Expand Down Expand Up @@ -362,6 +370,8 @@ async def _prepare_phase(
self, environment: BaseEnvironment, instruction: str, *, cwd: str
) -> None:
key, option, expected = self._replan_configuration()
stride_args = (["--execution-stride-strategy", self.stride_strategy]
if self.stride_strategy is not None else [])
pending = await environment.exec(
command=f"test -e {_LOOPX_RUNTIME}/benchmark-pending-turn.json"
)
Expand Down Expand Up @@ -407,6 +417,7 @@ async def _prepare_phase(
"trial-workspace",
option,
str(expected),
*stride_args,
"--agent-work-mode",
f"{_AGENT_ID}=active",
"--execute",
Expand All @@ -423,6 +434,7 @@ async def _prepare_phase(
_GOAL_ID,
option,
str(expected),
*stride_args,
"--execute",
],
cwd=cwd,
Expand All @@ -440,6 +452,11 @@ async def _prepare_phase(
raise RuntimeError(
f"replan cadence readback mismatch for {key}: expected {expected}, got {configured!r}"
)
strategy = configured_state.get("execution_profile", {}).get("stride_strategy", "off")
if strategy != (self.stride_strategy or "off"):
raise RuntimeError(
f"stride strategy readback mismatch: expected {self.stride_strategy or 'off'}, got {strategy!r}"
)

async def _seed_phase(self, environment: BaseEnvironment, *, cwd: str) -> None:
text = (
Expand Down
10 changes: 9 additions & 1 deletion benchmark/runtime/sforge.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ def __init__(self, config, *, profile: str, cwd: str,
task_entry: str | None = None,
turn_envelope: bool = False,
replan_after_turns: int | None = None,
replan_after_todos: int | None = None):
replan_after_todos: int | None = None,
stride_strategy: str | None = None):
super().__init__(config)
if profile not in PROFILES:
raise ValueError("Unknown benchmark worker profile")
Expand Down Expand Up @@ -129,6 +130,12 @@ def __init__(self, config, *, profile: str, cwd: str,
replan_after_turns = DEFAULT_REPLAN_AFTER_TURNS
self.replan_after_turns = replan_after_turns
self.replan_after_todos = replan_after_todos
if stride_strategy is not None and not profile.startswith("heartbeat-"):
raise ValueError("stride_strategy requires a heartbeat profile")
if stride_strategy is not None:
from loopx.control_plane.work_items.stride_policy_codec import normalize_stride_strategy
stride_strategy = normalize_stride_strategy(stride_strategy)
self.stride_strategy = stride_strategy
self.profile, self.cwd = profile, cwd
if feedback not in FEEDBACK_MODES:
raise ValueError("Unknown feedback mode")
Expand Down Expand Up @@ -214,6 +221,7 @@ def _install_worker(self, backend, handle, log_dir, logger):
turn_envelope=self.turn_envelope,
replan_after_turns=self.replan_after_turns,
replan_after_todos=self.replan_after_todos,
stride_strategy=self.stride_strategy,
task_objective=self.task_objective,
)
asyncio.run(self.runtime.install(self.environment))
Expand Down
36 changes: 36 additions & 0 deletions benchmark/tests/test_edgebench_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,42 @@
from benchmark.edgebench.export_report import digest, export, verify, write


@pytest.mark.parametrize("strategy", [None, "off", "evidence_conditioned_v1"])
def test_report_preserves_observed_stride_version_and_absence(trial, strategy):
runs, selection, output, task = trial
task = runs / "run" / "case"
for name in ("runtime-receipt", "worker-profile"):
path = task / (name + ".json")
value = json.loads(path.read_text())
if strategy is not None:
value["stride_strategy"] = strategy
value["worker" if name == "runtime-receipt" else "profile"] = "heartbeat-resume"
write(path, value)
export(runs, selection, output, observed_at="2026-01-02T00:00:00+00:00")
config = json.loads((output / "settings/run.json").read_text())
for field in ("runner", "worker_profile"):
assert config[field].get("stride_strategy") == strategy
assert ("stride_strategy" in config[field]) is (strategy is not None)


@pytest.mark.parametrize("runner,profile", [
("evidence_conditioned_v1", "off"),
("evidence_conditioned_v1", None),
("future_policy", "future_policy"),
])
def test_report_rejects_unbound_or_unknown_stride_version(trial, runner, profile):
runs, selection, output, task = trial
task = runs / "run" / "case"
for name, strategy in (("runtime-receipt", runner), ("worker-profile", profile)):
path = task / (name + ".json")
value = json.loads(path.read_text())
if strategy is not None:
value["stride_strategy"] = strategy
write(path, value)
with pytest.raises(ValueError, match="stride_strategy"):
export(runs, selection, output, observed_at="2026-01-02T00:00:00+00:00")


@pytest.fixture
def trial(tmp_path):
row = dict(
Expand Down
Loading
Loading