Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
47247ad
fix(benchmark): budget complete controller capture preparation
loopx-agent Oct 16, 2026
0721781
docs(benchmark): explain bounded full checkpoint capture window
loopx-agent Oct 16, 2026
da01f99
fix(planning): preserve initial quality in shared delivery guidance
songoow Oct 16, 2026
a9dfe8c
fix(turn): reuse the canonical frozen-request digest matcher
loopx-agent Oct 16, 2026
34f1c99
fix(benchmark): keep a refused checkpoint handoff from killing the so…
songoow Oct 16, 2026
05de5f7
Merge PR #6621: preserve initial quality in shared delivery guidance
songoow Oct 16, 2026
c1eb303
Merge PR #6618: preserve full checkpoint preparation within trial phase
songoow Oct 16, 2026
311126b
test(planning): qualify bounded first-delivery guidance growth
songoow Oct 16, 2026
fae4371
fix(research): bind stride outcomes to complete source evidence
songoow Oct 16, 2026
cae3257
docs(research): clarify outcome evidence replay and planning guidance
songoow Oct 16, 2026
0f9663a
Merge PR #6623 into the EdgeBench experiment branch
songoow Oct 16, 2026
f7756ff
fix(testing): recognize planning guidance without dynamic regex
songoow Oct 16, 2026
fb6f060
fix(testing): keep the CLI output probe importable against a base che…
songoow Oct 16, 2026
a0fa279
fix(benchmark): keep the uploaded native prompt readable under any la…
songoow Oct 16, 2026
b7c47c9
fix(benchmark): retry a dropped planning wake within the trial budget
songoow Oct 16, 2026
495ceae
refactor(planning): keep quality message rendering with its owner
songoow Oct 16, 2026
aa12459
test(architecture): reconcile the retired governance module ratchet
songoow Oct 16, 2026
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
16 changes: 16 additions & 0 deletions benchmark/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,16 @@ the source continues from the original packet and deadline. Optional capture
failure records a missed point, never a later replacement or a new model/eval budget.
A barrier that cannot be released remains an explicit controller recovery problem.

The explicitly enabled full controller capture hooks now share a 90-second window for complete
native/research preparation, stability checks and live-source capture (previously
30 seconds). Both hooks cap it by the original trial phase; the controller keeps
that same absolute deadline. After releasing the source, it may spend up to 30
more seconds sealing its immutable exports, also within the original phase. This
allowance changes neither the trial budget nor cooldown, completeness or retry
rules. A missed point remains missed; direct controller commands retain their
caller-supplied timeout, and other capture declarations keep their 30-second
window. Capture remains off unless explicitly enabled.

A real native CLI/worker plus isolated Docker/native-HTTP qualification covers the
mid-wake path, complete canonical context, conserved evaluator opportunity and
original source continuation. It uses a deterministic local model and synthetic
Expand Down Expand Up @@ -897,6 +907,12 @@ worker 保留原 guard,controller 暂停全部容器写入者,核对仍有
文件 witness,再封存原生评测机会、incumbent 及剩余预算。源变化、过期或失去
交接会拒绝;可恢复的捕获失败不取消原研究,也不得改用稍后的状态。解除暂停后,
源会话继续使用原 packet 和 deadline。
显式开启的两处完整 controller 捕获 hook 共用 90 秒窗口(原为 30 秒),覆盖完整原生/研究上下文
准备、一致性复核与 live-source 捕获,并受原试验 phase 截止时间约束。controller
沿用同一个绝对截止时间;解除暂停后,只能在原 phase 内再用至多 30 秒封存静态副本。
这不增加试验预算、冷却或机会,不改变完整性及首次尝试规则;丢失的点仍不能补记,
直接 controller 命令仍受调用者给定的 timeout 约束,其他捕获声明保持 30 秒窗口。
未显式开启时仍不捕获。
自动 supervisor 的准备截止时间仍约束暂停、文件导出及原生捕获。成功解除暂停后,
controller 只对自己持有的静态导出副本继续校验、封存,使用已捕获的原 Host phase
截止时间;该截止时间同时传给类型化产物 owner,过期不能发布。源可以继续工作,
Expand Down
4 changes: 3 additions & 1 deletion benchmark/runtime/checkpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from loopx.hosts.research_checkpoint import (
_participant_id, native_binding_participant, research_participants, verify_research_participants,
)
from .checkpoint_handoff import CHECKPOINT_CAPTURE_SECONDS


def normalize_checkpoint_request_path(value: str) -> str:
Expand Down Expand Up @@ -246,7 +247,8 @@ def capture_after_wake(env: dict[str, str], receipt: dict[str, Any], *,
started = time.monotonic()
try:
phase_deadline = env.get("LOOPX_PHASE_DEADLINE_EPOCH")
deadline = time.time() + 30
window = CHECKPOINT_CAPTURE_SECONDS if supplied_request and supplied_request[0].get("controller_handoff") else 30
deadline = time.time() + window
if phase_deadline is not None:
original_deadline = float(phase_deadline)
if not math.isfinite(original_deadline):
Expand Down
5 changes: 5 additions & 0 deletions benchmark/runtime/checkpoint_handoff.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
import uuid


# One shared budget for explicitly enabled full controller capture, including complete
# native/research preparation and the controller's live-source export. Mature
# histories can exhaust 30 seconds before the writer barrier even starts. The
# original phase still caps this clock; handoff/retries cannot renew it.
CHECKPOINT_CAPTURE_SECONDS = 90
CONTROLLER_SEAL_SECONDS = 30


Expand Down
56 changes: 36 additions & 20 deletions benchmark/runtime/checkpoint_wake.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from loopx.control_plane.effect_runtime import effect_runtime_result
from .checkpoint import _mark_replan_attempt, _participant_id, held_quota_decision, read_checkpoint_request
from .checkpoint_handoff import CHECKPOINT_CAPTURE_SECONDS


class CheckpointWake:
Expand Down Expand Up @@ -103,7 +104,8 @@ def _capture(self, held, deadline):
"decision_sha256": held["sha256"],
"capture_order": "after_native_interruption",
"writer_barrier": "owned_native_process_group_only"}
window = min(time.time() + 30, time.time() + deadline - time.monotonic())
capture_seconds = CHECKPOINT_CAPTURE_SECONDS if self.request.get("controller_handoff") else 30
window = min(time.time() + capture_seconds, time.time() + deadline - time.monotonic())
if window <= time.time():
raise TimeoutError("original wake budget expired before capture")
components = list(self.request["components"])
Expand Down Expand Up @@ -200,25 +202,39 @@ def run(self, execution, body, wake, session_wake, stdout, stderr, timeout):
raise subprocess.TimeoutExpired(command, timeout)
if not select.select([self.listener], [], [], min(.1, remaining))[0]:
continue
with held_quota_decision(self.listener, self.capture_root,
goal_id=self.env["LOOPX_GOAL_ID"], agent_id=self.env["LOOPX_AGENT_ID"],
turn_instance_id=None, native_process=process,
interrupt_timeout=min(10, remaining), interrupt_when=self._first_replan,
deadline=deadline) as held:
if "native_process_return_code" not in held:
continue
stdout.flush()
session_wake.observe(wake / "stdout.jsonl")
event = {"obligation_id": held["intent"]["obligation_id"],
"decision_path": str(held["path"]), "decision_sha256": held["sha256"],
"captured": False, "research_point_qualified": False}
self.receipt.setdefault("checkpoint_handoffs", []).append(event)
try:
event.update(self._capture(held, deadline), captured=True)
except Exception as error:
# A missed optional point cannot discard the original
# committed guard or turn a later retry into its capture.
event.update(error_kind=type(error).__name__, error=str(error)[:500])
entered = False
try:
with held_quota_decision(self.listener, self.capture_root,
goal_id=self.env["LOOPX_GOAL_ID"], agent_id=self.env["LOOPX_AGENT_ID"],
turn_instance_id=None, native_process=process,
interrupt_timeout=min(10, remaining), interrupt_when=self._first_replan,
deadline=deadline) as held:
entered = True
if "native_process_return_code" not in held:
continue
stdout.flush()
session_wake.observe(wake / "stdout.jsonl")
event = {"obligation_id": held["intent"]["obligation_id"],
"decision_path": str(held["path"]), "decision_sha256": held["sha256"],
"captured": False, "research_point_qualified": False}
self.receipt.setdefault("checkpoint_handoffs", []).append(event)
try:
event.update(self._capture(held, deadline), captured=True)
except Exception as error:
# A missed optional point cannot discard the original
# committed guard or turn a later retry into its capture.
event.update(error_kind=type(error).__name__, error=str(error)[:500])
except Exception as error:
# A refused handoff (unparseable, outside this wake's capture
# root, or rejected by the typed selector) is neither released
# nor captured; its caller already reports a typed transport
# failure. The uninterrupted original wake keeps running.
if entered or process.poll() is not None:
raise
self.receipt.setdefault("checkpoint_handoffs", []).append({
"captured": False, "research_point_qualified": False, "released": False,
"error_kind": type(error).__name__, "error": str(error)[:500]})
continue

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 拒绝交接前 capture root 缺失时会在同一连接上忙循环。在当前 wake 的物理 capture root 边界准备并验证目录,或先 accept 并关闭被拒连接再继续;不能对同一个未消费的可读连接直接 continue。保留同 wake、物理路径和原阶段预算约束。 独立观察:当前准确头的真实 Unix socket/拥有子进程反例中,1.010秒内接受连接0次、记录25217个拒绝事件,客户端已经发送但一直等回复,原来源未继续;这不是恢复后的新 wake,也不是已消费的一次拒绝。 验证要求:扩展 benchmark/tests/test_checkpoint_handoff.py:只创建当前 wake 父目录、保留 decisions 缺失,发送旧 wake packet;断言一次有界拒绝后原子进程能发送新的当前 packet 并继续、旧 packet 不变、无跨 wake 捕获或释放。再运行 uv run --extra test python -m pytest -q benchmark/tests/test_checkpoint_handoff.py。

continuation = held.get("continuation_body")
if continuation is not None:
break
Expand Down
4 changes: 4 additions & 0 deletions benchmark/runtime/sforge.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,10 @@ def format_run_cmd(self, prompt_path, *, model=None, cwd="", internet=True, resu
with tempfile.TemporaryDirectory(prefix="benchmark-prompt-") as directory:
prompt = Path(directory) / "task.md"
prompt.write_text(self.feedback_prompt)
# The native copy preserves the host mode and lands root-owned.
# The container's agent user must read the prompt whatever
# umask the launcher runs under.
prompt.chmod(0o644)
asyncio.run(self.environment.upload_file(prompt, prompt_path))
# Native run_agent saved the pre-override wrapper. Keep its visible
# prompt artifact aligned with what all five workers actually read.
Expand Down
23 changes: 22 additions & 1 deletion benchmark/runtime/sforge_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@
from .planning import task_plan_packet, validate_plan_readback
from .worker import PHASE_WORK_RESERVE_SECONDS, loopx_command, run_once

# One planning wake whose native process exits non-zero without a harness
# error or a validated plan (a dropped model stream, a failed turn) must not
# strand the whole trial before execution. Retry within the same phase budget.
PLANNING_WAKE_ATTEMPTS = 3


def _retryable_planning_wake(receipt: dict) -> bool:
return (receipt.get("ok") is not True and receipt.get("timed_out") is not True
and "error_kind" not in receipt and "planning" not in receipt
and receipt.get("return_code") not in (None, 0))


def prepare_entry(env: dict[str, str]) -> bool:
"""Reuse a qualified initial plan; execution still requires ordinary quota."""
Expand Down Expand Up @@ -48,7 +59,17 @@ def prepare_entry(env: dict[str, str]) -> bool:
# Native SForge owns one absolute trial deadline for planning and execution.
# Do not inherit a separate planning cap from the shared worker environment.
planning_env.pop("LOOPX_PLANNING_TIMEOUT_SEC", None)
receipt = run_once(planning_env)
attempts = []
for attempt in range(1, PLANNING_WAKE_ATTEMPTS + 1):
receipt = run_once(planning_env)
attempts.append({"attempt": attempt, "ok": receipt.get("ok") is True,
"timed_out": receipt.get("timed_out") is True,
"return_code": receipt.get("return_code"),
**({"error_kind": receipt["error_kind"]} if "error_kind" in receipt else {})})
remaining = float(env["LOOPX_PHASE_DEADLINE_EPOCH"]) - time.time()
if not _retryable_planning_wake(receipt) or remaining <= PHASE_WORK_RESERVE_SECONDS:
break
path.with_name("planning-attempts.json").write_text(json.dumps(attempts))
if receipt.get("ok") is not True:
raise RuntimeError("Planning failed; execution was not started")
entry = receipt.get("planning") or {}
Expand Down
9 changes: 7 additions & 2 deletions benchmark/runtime/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,11 @@ class _InertCheckpointWake:
listener = None


def _error_tail(exc: BaseException) -> dict[str, str]:
"""Private wake evidence: the class plus a bounded message, never a traceback."""
return {"error_kind": type(exc).__name__, "error": " ".join(str(exc).split())[:500]}


def run_once(env: dict[str, str]) -> dict:
if env.get("LOOPX_TURN_ENVELOPE", "0") not in {"0", "1"}:
raise ValueError("LOOPX_TURN_ENVELOPE must be 0 or 1")
Expand Down Expand Up @@ -505,15 +510,15 @@ def run_once(env: dict[str, str]) -> dict:
receipt["timed_out"] = True
except BaseException as exc:
receipt["ok"] = False
receipt["error_kind"] = type(exc).__name__
receipt.update(_error_tail(exc))
raise
finally:
checkpoint_context.close()
try:
if session_wake is not None:
session_wake.observe(wake / "stdout.jsonl")
except BaseException as exc:
receipt.update(ok=False, error_kind=type(exc).__name__)
receipt.update(ok=False, **_error_tail(exc))
raise
finally:
if env.get("LOOPX_CHECKPOINT_REQUEST"):
Expand Down
Loading
Loading