mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-03 04:07:04 +00:00
`delegate_shared._fail` rendered `{"status":"refused", ...}` as a plain string,
and the registry's legacy text adapter classified it as OK: it only understands
a top-level `ok:false` or a first-line `⚠️ IDENTIFIER` marker. So a refused
`delegate_wait`/`delegate_cancel` — daemon unreachable, run not owned, a
containment fault, a cancel the daemon refused — was recorded as a SUCCESSFUL
tool call on the outcome axis, in the acceptance packet and in the supervising
task's own reasoning.
Owner decision Q8A: the fix is a native structured result INSIDE the family,
not a repo-wide ABI migration. `_fail` now returns a `ToolResult` whose text is
the same JSON the callers emitted, plus two ADDITIVE envelope keys — `ok:false`
and `host_code` — written beside the domain payload. The domain `reason` is
never renamed into `ToolResult.code`, and the domain extras
(`definitely_unrun`, `pending_invocation_id`, `run_id`, `reset_at`, the custody
facts) keep their places. One exact, closed table maps a reason to its class:
substrate refusals (the daemon, the engine, custody or the run said no) are
`TOOL_REPORTED_FAILURE`, recorded and never degrading; malformed or
self-contradictory calls are `TOOL_ARG_ERROR`, which degrades and feeds
reflection. Neither is a timeout or the generic tool error, and an unclassified
reason defaults to the substrate class — the safe direction.
The second literal refusal author, `supervised_wait`'s checkpoint argument
check, folds into `_fail`. `_delegate_cancel`'s own outcomes join it: `failed`
and `containment_fault_run_may_still_be_live` report a run that may still be
live and mutating, so they publish as failures, while `confirmed` and
`requested` stay successful observations of the control surface.
Publication happens once, at the four REGISTERED entries, after every
decoration and immediately before the string is returned: `subagent_runtime`
mutates the start payload after `_delegate_start`, and an earlier publish fails
the registry's equality gate silently. `exact_start` now reads the native
payload, adds the actor identity and the work-order source, and returns
`_replace_tool_result`; its `json.loads → TypeError → return result` bypass,
which dropped that decoration without saying so, is deleted.
`_mark_actor_physical_start` still reads the DOMAIN `started` /
`started_uncustodied` status, never the host class.
The consumers migrate in the same change as the type they consume: the
configured-session bootstrap, the recovery handoff, the unknown-provider hold
and the pending-wake replay all read the producer's own payload rather than a
stringified result — without this, every leaf wake would have failed its
acknowledgement and taken the no-resend terminal. The wake envelope carries
`ok`/`host_code` through both fitted-spill shapes, so a refusal too large to
inline cannot read as a successful wait. `wait_once` deliberately keeps its
`str` tick contract, and `integrate_delegated_patch` keeps its own string ABI
at the one helper the two families share.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
103 lines
3.5 KiB
Python
103 lines
3.5 KiB
Python
"""Version-negotiated Claudexor execution-workspace wire contract."""
|
|
|
|
from types import SimpleNamespace
|
|
|
|
from ouroboros.subagents import (
|
|
DelegationRoute,
|
|
delegated_execution_workspace_root,
|
|
delegated_run_shape,
|
|
)
|
|
from ouroboros.tools.delegate import _start_request
|
|
|
|
|
|
def _request(version: str, *, acting: bool) -> dict:
|
|
shape = delegated_run_shape(acting)
|
|
stable_root = "/tmp/stable-project"
|
|
snapshot_root = "/tmp/private-execution-snapshot" if acting else stable_root
|
|
gateway = SimpleNamespace(engine_version=version)
|
|
execution_root = delegated_execution_workspace_root(gateway, shape, snapshot_root)
|
|
scope_root = stable_root if execution_root else snapshot_root
|
|
return _start_request(
|
|
SimpleNamespace(), DelegationRoute("codex"), shape, scope_root,
|
|
"do the work", 300, "host instructions", execution_root,
|
|
)
|
|
|
|
|
|
def test_legacy_strict_schema_keeps_the_byte_compatible_execution_shape():
|
|
request = _request("3.8.0", acting=True)
|
|
assert request["scope"]["root"] == "/tmp/private-execution-snapshot"
|
|
assert request["execution"] == {"isolation": "live", "delegated": True}
|
|
|
|
|
|
def test_new_schema_receives_the_private_snapshot_as_execution_workspace():
|
|
request = _request("3.8.1", acting=True)
|
|
assert request["scope"]["root"] == "/tmp/stable-project"
|
|
assert request["execution"] == {
|
|
"isolation": "live",
|
|
"delegated": True,
|
|
"workspaceRoot": "/tmp/private-execution-snapshot",
|
|
}
|
|
|
|
|
|
def test_readonly_shape_never_sends_a_live_execution_workspace():
|
|
request = _request("99.0.0", acting=False)
|
|
assert request["mode"] == "ask" and "execution" not in request
|
|
|
|
|
|
def test_retry_binding_keeps_snapshot_as_host_execution_root(tmp_path, monkeypatch):
|
|
import ouroboros.subagent_worktrees as worktrees
|
|
import ouroboros.tools.delegate_integration as integration
|
|
|
|
stable_root = tmp_path / "stable-project"
|
|
snapshot_root = tmp_path / "private-snapshot"
|
|
stable_root.mkdir()
|
|
snapshot_root.mkdir()
|
|
request = {
|
|
"primaryHarness": "codex",
|
|
"model": "gpt-5.6-sol",
|
|
"effort": "high",
|
|
"access": "workspace_write",
|
|
"mode": "agent",
|
|
"maxSeconds": 300,
|
|
"scope": {"kind": "project", "root": str(stable_root)},
|
|
"execution": {
|
|
"isolation": "live",
|
|
"delegated": True,
|
|
"workspaceRoot": str(snapshot_root),
|
|
},
|
|
}
|
|
record = {
|
|
"request": request,
|
|
"project_id": "project-stable",
|
|
"project_owned": False,
|
|
"idempotency_key": "stored-key",
|
|
"snapshot_id": "snapshot-1",
|
|
"target_root": str(stable_root),
|
|
"baseline_sha": "a" * 40,
|
|
"authority_source": "acting_constraint",
|
|
}
|
|
monkeypatch.setattr(
|
|
integration, "_validated_invocation",
|
|
lambda *_args, **_kwargs: (record, None),
|
|
)
|
|
monkeypatch.setattr(
|
|
integration, "_retry_binding_refusal", lambda *_args, **_kwargs: None,
|
|
)
|
|
monkeypatch.setattr(
|
|
integration, "_mutation_authority",
|
|
lambda *_args, **_kwargs: ({"target_root": str(stable_root)}, None),
|
|
)
|
|
monkeypatch.setattr(
|
|
worktrees, "find_execution_snapshot",
|
|
lambda _snapshot_id: {"path": str(snapshot_root)},
|
|
)
|
|
|
|
binding, refusal = integration._resolve_retry_invocation(
|
|
SimpleNamespace(task_id="task-a"), tmp_path, "invocation-a", "same prompt",
|
|
)
|
|
|
|
assert refusal is None
|
|
assert binding is not None
|
|
assert binding.request_body is request
|
|
assert binding.root == str(snapshot_root)
|
|
assert binding.target_root == str(stable_root)
|