From b3bbc57cea083b73c901f65d7d26014cc6767291 Mon Sep 17 00:00:00 2001 From: DavidHLP Date: Sun, 11 Oct 2026 04:22:38 -0700 Subject: [PATCH 1/3] test: preserve safe isolation protocol failure diagnostics --- .../agent/tests/test_e2e_account_isolation.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/services/agent/tests/test_e2e_account_isolation.py b/services/agent/tests/test_e2e_account_isolation.py index 0c9317071..35e0f0382 100644 --- a/services/agent/tests/test_e2e_account_isolation.py +++ b/services/agent/tests/test_e2e_account_isolation.py @@ -3979,6 +3979,25 @@ def _known_answer(text="synthetic answer"): return (json.dumps({"answer": text}), True) +@pytest.mark.parametrize("content,detail", [ + ("private-response-canary", "model decision was not valid JSON"), + (json.dumps({"answer": "private-response-canary", "extra": True}), + "model decision schema was malformed"), +]) +def test_real_probe_retains_safe_protocol_failure_detail(monkeypatch, tmp_path, content, detail): + (verdict, evidence), budget, requests = _run_real_bound_probe( + monkeypatch, tmp_path, [(content, True)], + ) + first = evidence["scenarios"][0] + assert first["interrupted"] == "ModelProtocolError" + assert first["protocol_error"].startswith(detail) + assert "private-response-canary" not in json.dumps(evidence) + assert verdict == "incomplete" and first["ok"] is False + assert len(requests) == 1 and evidence["model_calls"] == 1 + assert [row["interrupted"] for row in evidence["scenarios"]][1:] == ["not_run", "not_run"] + assert budget.snapshot()["unknown_usage_attempts"] == 0 + + def test_real_probe_unknown_toolcall_stops_round_and_remaining_legs(monkeypatch, tmp_path): (verdict, evidence), budget, requests = _run_real_bound_probe( monkeypatch, From 5567decf107515a26a86fec36a2afd8c768730f9 Mon Sep 17 00:00:00 2001 From: DavidHLP Date: Sun, 11 Oct 2026 04:23:43 -0700 Subject: [PATCH 2/3] fix: retain safe isolation protocol failure details --- services/agent/e2e_account_isolation.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/agent/e2e_account_isolation.py b/services/agent/e2e_account_isolation.py index a1c9fd3e9..7d5255f2b 100644 --- a/services/agent/e2e_account_isolation.py +++ b/services/agent/e2e_account_isolation.py @@ -92,7 +92,7 @@ from agent_loop import run_tool_loop from boundary_evaluation import SEARCH_EVIDENCE_SPEC, _usd_from_micro, search_evidence_tool -from deepseek_model import DeepseekModel, ModelBudgetExceeded, model_label +from deepseek_model import DeepseekModel, ModelBudgetExceeded, ModelProtocolError, model_label from e2e_citation_support_model import ( _assert_artifact_directory, _claim_verdict_file, @@ -1801,6 +1801,7 @@ async def _run_agent_scenario( ) specs["search_evidence"] = SEARCH_EVIDENCE_SPEC interrupted: str | None = None + protocol_error: str | None = None snapshot_error: str | None = None rounds = 0 answer = "" @@ -1865,6 +1866,8 @@ async def _run_agent_scenario( rounds, answer = loop.rounds, loop.answer except Exception as error: # noqa: BLE001 - a probe leg must not lose the run interrupted = type(error).__name__ + if isinstance(error, ModelProtocolError): + protocol_error = str(error) # Adapter diagnostics contain shape, never model text. finally: usage = [entry for entry in model.usage if isinstance(entry, dict)] response_models = sorted( @@ -1933,6 +1936,7 @@ async def _run_agent_scenario( "scenario": scenario.name, "ok": ok, "interrupted": interrupted, + "protocol_error": protocol_error, # True only for data actually observed leaving its owner's scope; a missing # owner control or an interrupted leg is unproven, not exposed. "exposure": exposure, From 0d3e97dce95212f92dc399e71d9454d176b40dc6 Mon Sep 17 00:00:00 2001 From: DavidHLP Date: Sun, 11 Oct 2026 04:27:56 -0700 Subject: [PATCH 3/3] docs: describe isolated protocol failure evidence --- docs/DEVELOPMENT.md | 2 ++ services/agent/data/repository_corpus_manifest.json | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index cdfe80b9a..4d23b0a64 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -256,6 +256,8 @@ uv run python e2e_account_isolation.py \ loop/judge 一样计入同一个 USD 1 周期,不新增周期、不追加第二条账本。任一未知用量或被 拒绝的预留会立即中断当前腿并跳过其余腿(记为 `not_run`),不自动重试;artifact 保留 每条腿的绑定身份、purpose、前后账本快照、逐调用 receipt 与真实 answer/tool trace。 +模型协议错误另保留 `protocol_error` 形状诊断(固定错误标签、内容长度与结束原因), +不保存模型原文;该字段不改变 `INCOMPLETE` 判定、费用记录或剩余攻击腿的跳过行为。 `services/agent/src/ulticode_client.py` provides the Java LearningPlan HTTP wire contract (`save_learning_plan`, `get_learning_plan`, `get_learning_plan_by_key`) and a session-bound diff --git a/services/agent/data/repository_corpus_manifest.json b/services/agent/data/repository_corpus_manifest.json index 5c6487feb..15dfe1fa1 100644 --- a/services/agent/data/repository_corpus_manifest.json +++ b/services/agent/data/repository_corpus_manifest.json @@ -29,8 +29,8 @@ }, { "doc_id": "repository-development", - "version": "sha256-1b5bcaa942bff557c70c49a7eaa5f6d6437e7bf98ab6b4b2e64797828664ba32", - "chunk_id": "repository-development:sha256-1b5bcaa942bff557c70c49a7eaa5f6d6437e7bf98ab6b4b2e64797828664ba32:1", + "version": "sha256-58c66a3e9a4229509940fbe16bf8c8c9a6b821070a87208367c260ce84d30245", + "chunk_id": "repository-development:sha256-58c66a3e9a4229509940fbe16bf8c8c9a6b821070a87208367c260ce84d30245:1", "source_path": "docs/DEVELOPMENT.md", "access_scope": "repository-public", "sample_kind": "real",