From 3ff80fa10b5a89d7e5e15e07e7c86513c7824f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B0=E6=9C=94?= <565183519@qq.com> Date: Wed, 5 Aug 2026 11:36:01 +0800 Subject: [PATCH] fix: return validator details for bounded retries --- .../controller-engine.mjs | 8 ++++- .../controller-engine.test.mjs | 36 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/server-tools/bingshuo-tcs-controller/controller-engine.mjs b/server-tools/bingshuo-tcs-controller/controller-engine.mjs index d80685a..19d70ef 100644 --- a/server-tools/bingshuo-tcs-controller/controller-engine.mjs +++ b/server-tools/bingshuo-tcs-controller/controller-engine.mjs @@ -81,6 +81,7 @@ function cognitionInstruction() { "schema必须等于guanghu.zhuyuan-persona-cognition-frame/v1;runtime_id必须等于ZY-TCS-BRAIN-RUNTIME-0001。", "persona_id原样复制runtime_input.persona_id;human_anchor原样复制runtime_input.human_system_controller;instance_id原样复制runtime_input.current_instance.instance_id。", "faculties必须完整包含B1至B9及每个required_faculties列出的字段;列表字段使用字符串数组。", + "字符串数组字段精确为B5.confirmed_facts、B5.unknowns、B5.prior_wrong_route、B6.stopped_routes、B7.memory_queries、B7.memory_writebacks、B8.reality_proposals、B8.permission_boundaries、B8.unverified_states;其中memory_writebacks和reality_proposals允许空数组,其余不得为空。", "typed_outputs必须包含ui_projections,navigation_actions,capability_calls,receipts四个数组;没有现实动作时全部为空。", "protocol_effects必须是对象数组;逐一覆盖activated_protocols里的每个id,每项精确包含protocol_id和effect,不能添加未唤醒协议。", "guard_agent_projection.schema必须等于guanghu.zhuyuan-guard-agent-projection/v1,并含reminders,auto_triggers,hard_boundaries三个数组。", @@ -193,7 +194,12 @@ export class ControllerEngine { try { return validate(candidate); } catch (error) { - validatorError = String(error.message || error).slice(0, 240); + const details = + error && error.details ? `:${JSON.stringify(error.details)}` : ""; + validatorError = `${String(error.message || error)}${details}`.slice( + 0, + 240, + ); if (attempt === this.maxModelAttempts) throw error; } } diff --git a/server-tools/bingshuo-tcs-controller/controller-engine.test.mjs b/server-tools/bingshuo-tcs-controller/controller-engine.test.mjs index cb4040b..9594cd8 100644 --- a/server-tools/bingshuo-tcs-controller/controller-engine.test.mjs +++ b/server-tools/bingshuo-tcs-controller/controller-engine.test.mjs @@ -213,6 +213,10 @@ test("orientation prompt carries the exact runtime contract required by the vali observedInstructions.zhuyuan_cognition, /protocol_effects必须是对象数组/, ); + assert.match( + observedInstructions.zhuyuan_cognition, + /B5\.prior_wrong_route/, + ); assert.match( observedInstructions.zhuyuan_cognition, /guard_agent_projection\.schema必须等于/, @@ -227,6 +231,38 @@ test("orientation prompt carries the exact runtime contract required by the vali ); }); +test("validator details are returned to the model for a bounded retry", async () => { + const stateRoot = fs.mkdtempSync(path.join(os.tmpdir(), "bs-tcs-retry-")); + const fixture = new FixtureModelClient(); + let orientationAttempts = 0; + let retryError = null; + const modelClient = { + async generate(request) { + if (request.role !== "tcs_event_orientation") { + return fixture.generate(request); + } + orientationAttempts += 1; + if (orientationAttempts === 1) { + const malformed = await fixture.generate(request); + malformed.signals[0] = { why: malformed.signals[0].why }; + return malformed; + } + retryError = request.validatorError; + return fixture.generate(request); + }, + }; + const engine = new ControllerEngine({ + stateRoot, + modelClient, + modelName: "fixture-model", + }); + engine.initialize(); + const receipt = await engine.runEvent(bootEvent()); + assert.equal(receipt.outcome, "PASS"); + assert.equal(orientationAttempts, 2); + assert.match(retryError, /orientation\.signal\.id/); +}); + test("an interrupted technical cycle is preserved and restarted in a new state epoch", () => { const stateRoot = fs.mkdtempSync(path.join(os.tmpdir(), "bs-tcs-recovery-")); const first = new ControllerEngine({