diff --git a/docs/architecture/rfcs/monorepo-distribution-split-v0.md b/docs/architecture/rfcs/monorepo-distribution-split-v0.md index c7c151b8c4..0ccbab1029 100644 --- a/docs/architecture/rfcs/monorepo-distribution-split-v0.md +++ b/docs/architecture/rfcs/monorepo-distribution-split-v0.md @@ -2567,6 +2567,50 @@ R0/V10/R1, D2/D5/D6/D3, context/model/split-install, PostgreSQL host/wheel and full-cost/adoption requirements remain open. No main merge or host upgrade is included. +### 2026-11-08 — Synthetic Host UTF-8 recovery qualification + +The Windows CPython 3.11 CI arm of #8360 fails six existing original-task +revalidation cases. Its synthetic Python Host inherits a locale decoder for a +pipe, while the existing generic Host transport sends UTF-8 JSON. A disposable +File-provider counterprobe changes only that Host decoder: CP1252 produces +`host_failure` without `task_failure`; UTF-8 reaches the independent artifact +validator and produces `validation_failed` at `task_postcondition`, with +`revalidate_cached_result`. The source macOS arm independently passes all 12 +existing revalidation cases. These observations locate a fixture mismatch; +they do not justify widening production failure classification. + +Fixture commit `a4ce613585fdb329ba3600f0ede768864214ed84`, over main +`4e949f405528fd8087f0623526c62f890c1ce982`, explicitly configures its stdin as +UTF-8 before reading the request. One new File/SQLite regression first sets +only the generated Host's decoder to CP1252, sends a Unicode brief and requires +the rejected artifact to reach task validation. After repairing the artifact, +revalidation must accept it with the original turn key and exactly one Host +invocation. The same regression fails with the immutable baseline Host on the +same CPython 3.13.5/Node 22.22.3 environment, and passes with the correction. +No transport, production owner, persisted state or protocol vocabulary changes. + +On macOS arm64 with Node 22.22.3, CPython 3.11.15 source and a newly installed, +isolated monolithic wheel each pass the new two provider cases plus all 12 +existing original-task recovery cases. Wheel imports are verified outside the +checkout before loading the source-owned fixtures; all 1,920 present packaged +tracked runtime entries match the source bytes. The same 17 source-only +exclusions remain: canary baseline, host plugin/config, fault-test helper and +documentation/media. This qualifies the existing File/SQLite +synthetic Host recovery path, including retained MCP disconnection and lock +timeout recovery cases, not model adoption, split installation or native +Windows. The failed original Windows arm remains historical evidence until a +new exact-head Windows run passes. The independent Frontstage manifest request +still returns HTTP 504 after its bounded retry. + +The original task stays open. Preserve I3 for at least one minor release, +M1/#8011, full R0/V10/R1, D2/D5/D6/D3, context/model/split-install, +PostgreSQL host/wheel, whole-path/full marginal-cost/adoption requirements and +all historical and frozen failures. No cost campaign is repeated or relabeled, +and this fixture qualification establishes no performance benefit. The report +loading proposal #8360 retains its independent review and CI gates; the next +step is exact-head review of this correction and native Windows readback. No +main merge or installed-host upgrade is included. + ## Appendix B: Decision log | Date | Decision | Owner / approval | Alternatives | Normative sections changed | diff --git a/docs/architecture/rfcs/monorepo-distribution-split-v0.zh-CN.md b/docs/architecture/rfcs/monorepo-distribution-split-v0.zh-CN.md index fac481ef09..60c2ee7943 100644 --- a/docs/architecture/rfcs/monorepo-distribution-split-v0.zh-CN.md +++ b/docs/architecture/rfcs/monorepo-distribution-split-v0.zh-CN.md @@ -1993,6 +1993,41 @@ caller 的必需依赖闭包,再选一个完整可回退的加载改动并验 context/model/split-install、PostgreSQL host/wheel、full-cost/adoption 要求 继续开放;本轮不合入 main、不升级 host。 +### 2026-11-08 — 合成 Host 的 UTF-8 恢复资格 + +#8360 的 Windows CPython 3.11 CI arm 在六个既有原任务重验证用例中失败。 +合成 Python Host 对 pipe 继承 locale decoder,而原 generic Host transport +发送 UTF-8 JSON。Disposable File-provider counterprobe 只改变该 Host decoder: +CP1252 产生 `host_failure`,没有 `task_failure`;UTF-8 到达独立产物 validator, +在 `task_postcondition` 产生 `validation_failed`,并提供 +`revalidate_cached_result`。源码 macOS arm 独立通过全部12个既有重验证用例。 +这些观察定位了 fixture mismatch,不支持扩大生产 failure classification。 + +Fixture commit `a4ce613585fdb329ba3600f0ede768864214ed84` 基于 main +`4e949f405528fd8087f0623526c62f890c1ce982`,在读取请求前显式将 stdin 配为 +UTF-8。新增 File/SQLite regression 先只把生成 Host 的 decoder 设为 CP1252, +发送 Unicode brief,要求被拒绝的产物到达 task validation。修复产物后,重验证 +必须沿用原 turn key 并接受结果,Host invocation 始终为一次。同一 regression +在相同 CPython3.13.5/Node22.22.3 环境中对不可变 baseline Host 失败,修正后 +通过。不改变 transport、生产 owner、持久状态或协议词汇。 + +macOS arm64/Node22.22.3 下,CPython3.11.15 source 与新安装的隔离单体 wheel +各通过新增两个 provider 用例及全部12个既有原任务恢复用例。先在 checkout 外 +验证 wheel import 归属,再加载 source-owned fixture;1920个实际 packaged +tracked runtime entry 均与源码字节相同。原17个 source-only exclusion 保留: +canary baseline、host plugin/config、fault-test helper 和 documentation/media。 +这验收既有 File/SQLite 合成 Host 恢复路径,包括保留的 MCP +断连及 lock-timeout 恢复用例,不验收模型采纳、split installation 或原生 Windows。 +原失败 Windows arm 保留为历史证据,直到新 exact-head Windows run 通过。 +独立 Frontstage manifest 请求在有界重试后仍为 HTTP504。 + +原任务保持开放。保留 I3 至少一个 minor release、M1/#8011、完整 R0/V10/R1、 +D2/D5/D6/D3、context/model/split-install、PostgreSQL host/wheel、whole-path/ +full marginal-cost/adoption 验收及全部历史/冻结失败。不重跑或重标签成本 +campaign,本 fixture 资格不证明性能收益。报告按需加载提案 #8360 保留独立 +review/CI gate;下一步为本修正的 exact-head 评审与原生 Windows 读回。不包含 +合入 main 或 installed-host 升级。 + ## 附录 B:决策日志 | 日期 | 决策 | 负责人 / 批准 | 备选 | 变更的规范章节 | diff --git a/tests/test_local_delegation.py b/tests/test_local_delegation.py index d76b1ae942..e23b5dd6ff 100644 --- a/tests/test_local_delegation.py +++ b/tests/test_local_delegation.py @@ -38,6 +38,9 @@ from loopx.control_plane.quota.turn_envelope import turn_host_lineage from loopx.control_plane.collaboration.inbox import acknowledge from loopx.control_plane.collaboration.peers import return_result +# The generic Host protocol sends UTF-8 over a pipe, whose Python 3.11 +# Windows default is a locale codec rather than the console's UTF-8 mode. +sys.stdin.reconfigure(encoding='utf-8') request = json.load(sys.stdin) workspace = Path.cwd() root = Path(sys.argv[1]) @@ -98,6 +101,35 @@ def brief(): "return_requirement": "Return the independently checked artifact"} +def test_fixture_host_revalidates_utf8_input_under_a_non_utf8_pipe(service): + """The fixture must reach task validation with the original UTF-8 identity.""" + root, runner = service + host = root / "fixture-host.py" + # Model the Windows Python 3.11 pipe default without changing the parent + # transport or requiring a platform-dependent locale on the test runner. + host.write_text("import sys; sys.stdin.reconfigure(encoding='cp1252')\n" + + host.read_text(encoding="utf-8"), encoding="utf-8") + output = root / "analyst/initial/output.json" + saved = output.read_bytes() + output.write_text("{}", encoding="utf-8") + request = {**brief(), "context": "Read the synthetic résumé: 报告 € → evidence."} + runner.start("analysis", "analysis-1", request) + rejected = wait(runner) + assert rejected["status"] == "rejected", rejected + assert "task_failure" in rejected, rejected + assert rejected["task_failure"]["resume_mode"] == "revalidate_cached_result" + turn_key = rejected["task_failure"]["turn_key"] + assert (root / "analyst/initial/host-invocations").read_text() == "1" + + output.write_bytes(saved) + runner.revalidate("analysis-1") + accepted = wait(runner) + assert accepted["status"] == "accepted", accepted + assert "task_failure" not in accepted + assert json.loads(runner.path("analysis-1").read_text())["turn_key"] == turn_key + assert (root / "analyst/initial/host-invocations").read_text() == "1" + + @pytest.mark.parametrize("provider", ["file", "sqlite"]) def test_delegation_fixture_isolates_cached_and_child_runtime_routes(tmp_path, monkeypatch, provider): """A warmed parent must use the same private Effect server as its CLI."""