fix(rectification): hold time cards until an offer turn
Keep collecting while a method follow-up can still distinguish candidates, and show adoption cards only after offer-candidates. Align the natal house snapshot with the agent text column. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -320,8 +320,11 @@ test("candidate state renders from the snapshot API and never from sentinels", (
|
||||
assert.match(styles, /\.rectification-house-table \{[^}]*border:\s*1px solid var\(--color-border\)/);
|
||||
assert.match(styles, /\.rectification-house-table \{[^}]*background:\s*var\(--color-canvas-soft\)/);
|
||||
assert.match(styles, /\.message-list \{[\s\S]*--assistant-content-inset: calc\(32px \+ var\(--space-3\)\)/);
|
||||
assert.match(styles, /\.rectification-message-wrap \.rectification-candidates \{[\s\S]*width: calc\(100% - var\(--assistant-content-inset\)\)/);
|
||||
assert.match(styles, /\.rectification-message-wrap \.rectification-candidates \{[\s\S]*margin-inline-start: var\(--assistant-content-inset\)/);
|
||||
assert.doesNotMatch(styles, /\.rectification-snapshot \{[^}]*--assistant-content-inset/);
|
||||
assert.match(styles, /\.rectification-snapshot \{[\s\S]*width: calc\(100% - var\(--assistant-content-inset\)\)/);
|
||||
assert.match(styles, /\.rectification-snapshot \{[\s\S]*margin-inline-start: var\(--assistant-content-inset\)/);
|
||||
assert.doesNotMatch(styles, /\.rectification-house-table \{[^}]*padding-inline-start:\s*var\(--assistant-content-inset\)/);
|
||||
assert.doesNotMatch(styles, /\.rectification-house-table \{[^}]*--assistant-content-inset/);
|
||||
});
|
||||
|
||||
@@ -353,7 +356,7 @@ test("the natal house table is a live snapshot card outside the agent bubble", (
|
||||
assert.doesNotMatch(houseTable, /<ChatMessageRow/);
|
||||
});
|
||||
|
||||
test("time-selection cards appear under the latest settled agent bubble once selection is allowed", () => {
|
||||
test("time-selection cards appear under the latest settled agent bubble only after offer-candidates", () => {
|
||||
const messageLoop = chat.slice(
|
||||
chat.indexOf("{messages.map((message) => {"),
|
||||
chat.indexOf("{candidateResult?.houseTable &&"),
|
||||
@@ -362,9 +365,10 @@ test("time-selection cards appear under the latest settled agent bubble once sel
|
||||
const cardsIndex = messageLoop.indexOf("<RectificationCandidateCards");
|
||||
assert.ok(actionsIndex >= 0 && cardsIndex > actionsIndex);
|
||||
assert.match(chat, /candidateResult\?\.selectionAllowed/);
|
||||
assert.match(chat, /latestOfferMessageKey/);
|
||||
assert.match(chat, /showSelectionCards = Boolean\(\s*candidateResult\?\.selectionAllowed[\s\S]*!busy[\s\S]*regeneratingMessageKey === null/);
|
||||
assert.match(messageLoop, /showSelectionCards && message\.renderKey === latestOfferMessageKey/);
|
||||
assert.match(chat, /turnOfferedSelection/);
|
||||
assert.match(chat, /rectification-offer-candidates/);
|
||||
assert.match(chat, /showSelectionCards = Boolean\(\s*candidateResult\?\.selectionAllowed[\s\S]*turnOfferedSelection\(latestOfferMessageKey\)[\s\S]*!busy/);
|
||||
assert.match(messageLoop, /showSelectionCards && message\.renderKey === selectionCardMessageKey/);
|
||||
assert.doesNotMatch(messageLoop, /className="rectification-snapshot"/);
|
||||
});
|
||||
|
||||
@@ -417,6 +421,8 @@ test("the Agent prompt cannot offer candidates while asking for more evidence",
|
||||
// The hard boundary lives in the prompt; no tool input carries an
|
||||
// offer_selection boolean anymore.
|
||||
assert.match(agent, /不得在同一回复中一边要求继续补证据,一边提供候选采用/);
|
||||
assert.match(agent, /id 不是 adopt_representative 时不得调用 rectification-offer-candidates/);
|
||||
assert.match(agent, /selection_allowed 只表示可以采用代表性时间/);
|
||||
const tools = readFileSync(
|
||||
new URL("../src/mastra/rectification-v9-tools.ts", import.meta.url),
|
||||
"utf8",
|
||||
|
||||
@@ -168,6 +168,30 @@ test("user-stop action explains the window when follow-up remains but the user s
|
||||
assert.doesNotMatch(JSON.stringify(plan), FORBIDDEN_LABELS);
|
||||
});
|
||||
|
||||
test("selectionAllowed with remaining method follow-up keeps collecting and only adopts on stop", () => {
|
||||
const plan = buildMethodFollowupPlan({
|
||||
evidence: [{
|
||||
status: "confirmed",
|
||||
domain: "education",
|
||||
datePrecision: "month",
|
||||
occurredFrom: "2016-06-01",
|
||||
occurredTo: null,
|
||||
}],
|
||||
});
|
||||
const action = buildNextUserAction({
|
||||
scorableCount: 3,
|
||||
evidenceCount: 3,
|
||||
hasLatestResult: true,
|
||||
selectionAllowed: true,
|
||||
sessionOutcome: "collect_evidence",
|
||||
nextFollowup: plan.next_followup,
|
||||
workingTime: "05:07",
|
||||
});
|
||||
assert.equal(plan.next_followup?.method_id, "d9_relationship");
|
||||
assert.equal(action.id, "ask_method_followup");
|
||||
assert.equal(action.on_user_stop.id, "adopt_representative");
|
||||
});
|
||||
|
||||
test("adopt_representative defers method follow-up instead of asking this turn", () => {
|
||||
const plan = buildMethodFollowupPlan({
|
||||
evidence: [{
|
||||
@@ -311,19 +335,19 @@ test("read-case follows method plan and hides D9/D10 labels even when SQL missin
|
||||
}>;
|
||||
}).execute({ caseId: CASE_ID });
|
||||
assert.deepEqual(projection.conversation_summary.missing_evidence_categories, ["relocation", "health", "finance"]);
|
||||
assert.equal(projection.method_followup_plan.next_followup, null);
|
||||
assert.equal(projection.method_followup_plan.deferred_followup?.method_id, "d9_relationship");
|
||||
assert.equal(projection.method_followup_plan.deferred_followup?.domain, "relationship");
|
||||
assert.equal(projection.method_followup_plan.session_outcome, "adopt_representative");
|
||||
assert.equal(projection.method_followup_plan.next_followup?.method_id, "d9_relationship");
|
||||
assert.equal(projection.method_followup_plan.next_followup?.domain, "relationship");
|
||||
assert.equal(projection.method_followup_plan.deferred_followup, null);
|
||||
assert.equal(projection.method_followup_plan.session_outcome, "collect_evidence");
|
||||
assert.equal(
|
||||
(projection as { next_user_action?: { id?: string; on_user_stop?: { id?: string } } }).next_user_action?.id,
|
||||
"adopt_representative",
|
||||
"ask_method_followup",
|
||||
);
|
||||
assert.equal(
|
||||
(projection as { next_user_action?: { on_user_stop?: { id?: string } } }).next_user_action?.on_user_stop?.id,
|
||||
"adopt_representative",
|
||||
);
|
||||
assert.equal(projection.latest_result.session_outcome.kind, "adopt_representative");
|
||||
assert.equal(projection.latest_result.session_outcome.kind, "collect_evidence");
|
||||
assert.equal(projection.internal_observations.find((item) => item.layer === "d9")?.ask_theme, "relationship_style");
|
||||
assert.equal(projection.latest_result.confirmation_allowed, false);
|
||||
assert.ok(projection.latest_result.indistinguishable_width_minutes >= 25);
|
||||
|
||||
@@ -63,6 +63,7 @@ test("system prompt carries only high-priority boundaries, never the method copy
|
||||
assert.match(prompt, /session_outcome=adopt_representative/);
|
||||
assert.match(prompt, /还不能确认唯一分钟/);
|
||||
assert.match(prompt, /next_user_action/);
|
||||
assert.match(prompt, /rectification-offer-candidates/);
|
||||
assert.match(prompt, /on_user_stop/);
|
||||
assert.match(prompt, /禁止只说记下了/);
|
||||
assert.match(prompt, /分盘句和宫位表由界面展示/);
|
||||
|
||||
@@ -86,7 +86,7 @@ test("checked-in registry verifies hashed product packages and leaves consult on
|
||||
{
|
||||
name: "jyotish-birth-time-rectification",
|
||||
version: "10.0.3",
|
||||
sha256: "3ad634db7f18f2434da6a98e758ce4bc7b4b07c1badea953e28cb662c7980ab6",
|
||||
sha256: "3116ee1dc5292e5e24237489955655a4e45874565f9d4e8004ae111b769363e2",
|
||||
},
|
||||
{
|
||||
name: "jyotish-personal-report",
|
||||
|
||||
Reference in New Issue
Block a user