fix(rectification): keep choice stems visible and align overlay adopt with the engine
Independent Staging Quality Gate / validate (push) Successful in 7m44s
Independent Staging Quality Gate / publish (push) Successful in 1m48s

Walkthrough polish: fail-closed empty D9 prompts, stop asserting the next question is on screen, vary same-domain collect copy, switch to reverse-verify after adopt, and let coverage route interview without blocking can_adopt.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-02 13:10:03 +08:00
parent bd79689f66
commit aab3ad4b84
22 changed files with 1019 additions and 82 deletions
@@ -110,11 +110,22 @@ function invalidStyleFollowup(
};
}
async function assertInvalidChoiceSkipped(followup: MethodFollowup) {
const accounting = fakeAccounting({}, {
fallback: () => {
throw new Error("invalid choice frame must not call the database");
},
async function assertUnrenderableChoiceFallsBackToSpoken(followup: MethodFollowup) {
const accounting = fakeAccounting({
set_agentic_rectification_conversation_focus: (_fn, args) => ({
id: FOCUS_ID,
case_id: CASE_ID,
question_id: args.p_question_id,
intent: args.p_intent,
target_evidence_id: null,
target_domain: args.p_target_domain,
target_kind: args.p_target_kind,
expected_answer_schema: args.p_expected_answer_schema,
status: "active",
asked_at: "2026-08-31T00:00:00.000Z",
resolved_at: null,
idempotent: false,
}),
});
const result = await persistServerOwnedFocus({
accounting: accounting.client,
@@ -124,8 +135,11 @@ async function assertInvalidChoiceSkipped(followup: MethodFollowup) {
decisionReceipt: null,
followup,
});
assert.equal(result.status, "invalid_choice_schema");
assert.equal(accounting.calls.length, 0);
assert.ok(result.status === "created" || result.status === "already_open");
assert.equal(result.focus?.intent, "collect_method_evidence");
const open = openQuestionFromPersistedFocus(result);
assert.equal(open?.kind, "collect_spoken");
assert.ok(open?.prompt?.trim());
}
function persistedFocus(questionId = "probe:education:2016") {
@@ -235,13 +249,13 @@ test("complete dynamic event options persist a server-owned focus", async () =>
});
});
test("non-renderable varga styles and empty event family skip focus persistence", async () => {
await assertInvalidChoiceSkipped(invalidStyleFollowup(
test("non-renderable varga styles and empty event family fall back to spoken collect", async () => {
await assertUnrenderableChoiceFallsBackToSpoken(invalidStyleFollowup(
[{ label: "巨蟹相处主动热情", answer_class: "yes" }],
"升学结果或学习环境出现明显变化",
"varga_style",
));
await assertInvalidChoiceSkipped(invalidStyleFollowup(DYNAMIC_STYLE_OPTIONS, " "));
await assertUnrenderableChoiceFallsBackToSpoken(invalidStyleFollowup(DYNAMIC_STYLE_OPTIONS, " "));
});
test("does not ask an already-answered discriminator probe again", async () => {