fix: return validator details for bounded retries

This commit is contained in:
冰朔 2026-08-05 11:36:01 +08:00
commit 3ff80fa10b
2 changed files with 43 additions and 1 deletions

View file

@ -81,6 +81,7 @@ function cognitionInstruction() {
"schema必须等于guanghu.zhuyuan-persona-cognition-frame/v1runtime_id必须等于ZY-TCS-BRAIN-RUNTIME-0001。",
"persona_id原样复制runtime_input.persona_idhuman_anchor原样复制runtime_input.human_system_controllerinstance_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;
}
}

View file

@ -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({