fix(rectification): ask finance and health, show Bhava and Pranapada

Interviews skipped D2/D11 and D30 unless the user volunteered them, so those scoring layers almost never ran. Ask them after family, keep relocation out of rotation, and display Bhava/Pranapada transitions without delaying adopt.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-08-20 11:49:27 +08:00
co-authored by Cursor
parent c1bd09351b
commit 1b93afc594
28 changed files with 860 additions and 51 deletions
@@ -164,7 +164,7 @@ test("holdout not_ready forbids unique-minute copy and still blocks confirm", as
assert.match(agentSource, /session_outcome=adopt_representative/);
assert.doesNotMatch(agentSource, /±2 分钟/);
assert.equal(PUBLIC_RECTIFICATION_TOOLS.length, 13);
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.7");
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.8");
const accounting = fakeAccounting({
...receiptHandlers,
@@ -120,7 +120,7 @@ test("eight-method routing asks relationship after dated education, not relocati
assert.equal(plan.next_followup?.method_id, "d9_relationship");
assert.equal(plan.next_followup?.domain, "relationship");
assert.equal(plan.stop_domain_rotation, true);
assert.deepEqual([...plan.not_in_rotation], ["relocation", "finance", "health"]);
assert.deepEqual([...plan.not_in_rotation], ["relocation"]);
assert.equal(plan.methods.find((item) => item.method_id === "appearance")?.status, "uncovered");
});
@@ -259,6 +259,8 @@ test("D9 differ becomes an internal ask theme without sign labels", () => {
{ layer: "d5", candidates_differ: false, ask_theme: null },
{ layer: "d7", candidates_differ: false, ask_theme: null },
{ layer: "d12", candidates_differ: false, ask_theme: null },
{ layer: "d11", candidates_differ: false, ask_theme: null },
{ layer: "d30", candidates_differ: false, ask_theme: null },
]);
assert.doesNotMatch(JSON.stringify({ scan, observations }), FORBIDDEN_LABELS);
const plan = buildMethodFollowupPlan({
@@ -267,6 +269,8 @@ test("D9 differ becomes an internal ask theme without sign labels", () => {
{ status: "confirmed", domain: "relationship", datePrecision: "year", occurredFrom: "2018-01-01", occurredTo: null },
{ status: "confirmed", domain: "career", datePrecision: "year", occurredFrom: "2019-01-01", occurredTo: null },
{ status: "confirmed", domain: "family", datePrecision: "year", occurredFrom: "2020-01-01", occurredTo: null },
{ status: "confirmed", domain: "finance", datePrecision: "year", occurredFrom: "2021-01-01", occurredTo: null },
{ status: "confirmed", domain: "health_pressure", datePrecision: "year", occurredFrom: "2022-01-01", occurredTo: null },
{ status: "confirmed", domain: "appearance", datePrecision: "unknown", occurredFrom: null, occurredTo: null },
{ status: "confirmed", domain: "marks", datePrecision: "unknown", occurredFrom: null, occurredTo: null },
],
@@ -307,6 +311,8 @@ test("D24-only window change folds into education follow-up without a second lay
{ status: "confirmed", domain: "relationship", datePrecision: "year", occurredFrom: "2018-01-01", occurredTo: null },
{ status: "confirmed", domain: "career", datePrecision: "year", occurredFrom: "2019-01-01", occurredTo: null },
{ status: "confirmed", domain: "family", datePrecision: "year", occurredFrom: "2020-01-01", occurredTo: null },
{ status: "confirmed", domain: "finance", datePrecision: "year", occurredFrom: "2021-01-01", occurredTo: null },
{ status: "confirmed", domain: "health_pressure", datePrecision: "year", occurredFrom: "2022-01-01", occurredTo: null },
{ status: "confirmed", domain: "appearance", datePrecision: "unknown", occurredFrom: null, occurredTo: null },
{ status: "confirmed", domain: "marks", datePrecision: "unknown", occurredFrom: null, occurredTo: null },
],
@@ -316,6 +322,54 @@ test("D24-only window change folds into education follow-up without a second lay
assert.equal(plan.next_followup?.ask_theme, "education_style");
});
test("D11-only window change folds into finance follow-up without delaying adopt", () => {
const scan = parseWindowScan({
scanned: true,
d9_lagna_count: 1,
d10_lagna_count: 1,
d2_lagna_count: 1,
d11_lagna_count: 2,
d30_lagna_count: 2,
d2_candidates_differ: false,
d11_candidates_differ: true,
d30_candidates_differ: true,
transitions: [
{ layer: "d11", at: "05:14" },
{ layer: "d30", at: "05:14" },
{ layer: "bhava", at: "05:14" },
{ layer: "pranapada", at: "05:14" },
],
});
assert.ok(scan);
assert.equal(scan.d11_candidates_differ, true);
assert.equal(scan.d2_candidates_differ, false);
assert.deepEqual(scan.transitions, [
{ layer: "d11", at: "05:14", user_meaning: "D11 在 05:14 发生变化" },
{ layer: "d30", at: "05:14", user_meaning: "D30 在 05:14 发生变化" },
{ layer: "bhava", at: "05:14", user_meaning: "Bhava Lagna 在 05:14 发生变化" },
{ layer: "pranapada", at: "05:14", user_meaning: "Pranapada Lagna 在 05:14 发生变化" },
]);
const observations = internalObservationsFromWindowScan(scan);
assert.equal(observations.find((item) => item.layer === "d11")?.candidates_differ, true);
assert.equal(observations.find((item) => item.layer === "d11")?.ask_theme, "finance_change");
assert.equal(observations.find((item) => item.layer === "d30")?.ask_theme, "health_pressure");
assert.equal(observations.some((item) => item.layer === "d2"), false);
const plan = buildMethodFollowupPlan({
evidence: [
{ status: "confirmed", domain: "relationship", datePrecision: "year", occurredFrom: "2018-01-01", occurredTo: null },
{ status: "confirmed", domain: "career", datePrecision: "year", occurredFrom: "2019-01-01", occurredTo: null },
{ status: "confirmed", domain: "family", datePrecision: "year", occurredFrom: "2020-01-01", occurredTo: null },
{ status: "confirmed", domain: "finance", datePrecision: "year", occurredFrom: "2021-01-01", occurredTo: null },
{ status: "confirmed", domain: "health_pressure", datePrecision: "year", occurredFrom: "2022-01-01", occurredTo: null },
{ status: "confirmed", domain: "appearance", datePrecision: "unknown", occurredFrom: null, occurredTo: null },
{ status: "confirmed", domain: "marks", datePrecision: "unknown", occurredFrom: null, occurredTo: null },
],
observations,
});
assert.equal(plan.next_followup?.method_id, "d2_finance");
assert.equal(plan.next_followup?.source, "varga_observation");
});
test("read-case follows method plan and hides D9/D10 labels even when SQL missing categories rotate", async () => {
const accounting = fakeAccounting({
...receiptHandlers,
@@ -543,9 +597,9 @@ test("rescore failure does not fail the evidence write", async () => {
assert.ok(result.rescore.error_code);
});
test("public tool surface stays at 13 and new cases bind 10.0.7", () => {
test("public tool surface stays at 13 and new cases bind 10.0.8", () => {
assert.equal(PUBLIC_RECTIFICATION_TOOLS.length, 13);
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.7");
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.8");
const deprecated = resolveExactSkillPackage(
"jyotish-birth-time-rectification",
"10.0.2",
@@ -574,7 +628,7 @@ test("public tool surface stays at 13 and new cases bind 10.0.7", () => {
assert.doesNotMatch(skill, /±5 分钟确定性/);
});
test("family then appearance then marks follow the method plan", () => {
test("family then finance then health then appearance follow the method plan", () => {
const afterFamily = buildMethodFollowupPlan({
evidence: [
{ status: "confirmed", domain: "education", datePrecision: "year", occurredFrom: "2016-01-01", occurredTo: null },
@@ -583,14 +637,40 @@ test("family then appearance then marks follow the method plan", () => {
{ status: "confirmed", domain: "family", datePrecision: "year", occurredFrom: "2020-01-01", occurredTo: null },
],
});
assert.equal(afterFamily.next_followup?.method_id, "appearance");
assert.equal(afterFamily.next_followup?.domain, "appearance");
assert.equal(afterFamily.next_followup?.method_id, "d2_finance");
assert.equal(afterFamily.next_followup?.domain, "finance");
const afterFinance = buildMethodFollowupPlan({
evidence: [
{ status: "confirmed", domain: "education", datePrecision: "year", occurredFrom: "2016-01-01", occurredTo: null },
{ status: "confirmed", domain: "relationship", datePrecision: "year", occurredFrom: "2018-01-01", occurredTo: null },
{ status: "confirmed", domain: "career", datePrecision: "year", occurredFrom: "2019-01-01", occurredTo: null },
{ status: "confirmed", domain: "family", datePrecision: "year", occurredFrom: "2020-01-01", occurredTo: null },
{ status: "confirmed", domain: "finance", datePrecision: "year", occurredFrom: "2021-01-01", occurredTo: null },
],
});
assert.equal(afterFinance.next_followup?.method_id, "d30_health");
assert.equal(afterFinance.next_followup?.domain, "health_pressure");
assert.match(afterFinance.next_followup?.user_prompt_hint ?? "", /不是医学判断/);
const afterHealth = buildMethodFollowupPlan({
evidence: [
{ status: "confirmed", domain: "education", datePrecision: "year", occurredFrom: "2016-01-01", occurredTo: null },
{ status: "confirmed", domain: "relationship", datePrecision: "year", occurredFrom: "2018-01-01", occurredTo: null },
{ status: "confirmed", domain: "career", datePrecision: "year", occurredFrom: "2019-01-01", occurredTo: null },
{ status: "confirmed", domain: "family", datePrecision: "year", occurredFrom: "2020-01-01", occurredTo: null },
{ status: "confirmed", domain: "finance", datePrecision: "year", occurredFrom: "2021-01-01", occurredTo: null },
{ status: "confirmed", domain: "health_pressure", datePrecision: "year", occurredFrom: "2022-01-01", occurredTo: null },
],
});
assert.equal(afterHealth.next_followup?.method_id, "appearance");
assert.equal(afterHealth.next_followup?.domain, "appearance");
const afterAppearance = buildMethodFollowupPlan({
evidence: [
{ status: "confirmed", domain: "education", datePrecision: "year", occurredFrom: "2016-01-01", occurredTo: null },
{ status: "confirmed", domain: "relationship", datePrecision: "year", occurredFrom: "2018-01-01", occurredTo: null },
{ status: "confirmed", domain: "career", datePrecision: "year", occurredFrom: "2019-01-01", occurredTo: null },
{ status: "confirmed", domain: "family", datePrecision: "year", occurredFrom: "2020-01-01", occurredTo: null },
{ status: "confirmed", domain: "finance", datePrecision: "year", occurredFrom: "2021-01-01", occurredTo: null },
{ status: "confirmed", domain: "health_pressure", datePrecision: "year", occurredFrom: "2022-01-01", occurredTo: null },
{ status: "confirmed", domain: "appearance", datePrecision: "unknown", occurredFrom: null, occurredTo: null },
],
});
@@ -202,9 +202,9 @@ test("read-case evidence context keeps day labels and confirm does not rewrite d
assert.equal("p_occurred_from" in confirmCall.args, false);
});
test("new-case skill identity is 10.0.7 and the prompt prefers batch ingest", () => {
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.7");
assert.match(skill, /^version: 10\.0\.7$/m);
test("new-case skill identity is 10.0.8 and the prompt prefers batch ingest", () => {
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.8");
assert.match(skill, /^version: 10\.0\.8$/m);
assert.match(skill, /不要对同一句用户消息里的多件事件逐条 propose\+confirm/);
assert.match(agentSource, /当前轮新事件一律走 rectification-record-evidence-batch/);
assert.doesNotMatch(agentSource, /分别调用 rectification-propose-evidence 和 rectification-confirm-evidence/);
@@ -76,11 +76,11 @@ test("system prompt carries only high-priority boundaries, never the method copy
test("agent pins the dedicated rectification skill and its fixed version", () => {
assert.equal(RECTIFICATION_V9_SKILL_NAME, "jyotish-birth-time-rectification");
assert.equal(basename(RECTIFICATION_V9_SKILL_PATH), RECTIFICATION_V9_SKILL_NAME);
assert.ok(RECTIFICATION_V9_PACKAGE_PATH.endsWith("skills/jyotish-birth-time-rectification/versions/10.0.7"));
assert.ok(RECTIFICATION_V9_PACKAGE_PATH.endsWith("skills/jyotish-birth-time-rectification/versions/10.0.8"));
assert.notEqual(RECTIFICATION_V9_SKILL_PATH, RECTIFICATION_V9_PACKAGE_PATH);
assert.equal(realpathSync(RECTIFICATION_V9_SKILL_PATH), RECTIFICATION_V9_PACKAGE_PATH);
assert.equal(RECTIFICATION_SKILL_NAME, "jyotish-birth-time-rectification");
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.7");
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.8");
});
test("step budgets are bounded per action with a hard ceiling", () => {
@@ -92,9 +92,9 @@ test("terminal transitions are one-way and evidence writes stop at terminal", ()
test("the active rectification skill pins the v10 identity and lives in the right directory", () => {
assert.equal(RECTIFICATION_SKILL_NAME, "jyotish-birth-time-rectification");
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.7");
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.8");
assert.match(skill, /^---\nname: jyotish-birth-time-rectification/m);
assert.match(skill, /^version: 10\.0\.7$/m);
assert.match(skill, /^version: 10\.0\.8$/m);
for (const reference of references) {
const content = readFileSync(`${skillDirectory}/references/${reference}`, "utf8");
assert.ok(content.length > 0, `${reference} must be non-empty`);
@@ -209,7 +209,7 @@ test("open RPC passes the pinned skill and server-derived baseline only", async
session_id: SESSION_ID,
status: "draft",
should_start_opening: true,
skill_version: "10.0.7",
skill_version: "10.0.8",
};
}
return null;
@@ -247,11 +247,11 @@ test("open RPC passes the pinned skill and server-derived baseline only", async
});
assert.equal(response.disposition, "created");
assert.equal(response.shouldStartOpening, true);
assert.equal(response.skillVersion, "10.0.7");
assert.equal(response.skillVersion, "10.0.8");
const openCall = accounting.calls.find((call) => call.fn === "open_agentic_rectification_case_v2");
assert.ok(openCall);
assert.equal(openCall.args.p_skill_name, "jyotish-birth-time-rectification");
assert.equal(openCall.args.p_skill_version, "10.0.7");
assert.equal(openCall.args.p_skill_version, "10.0.8");
assert.equal(openCall.args.p_user_id, "user-1");
// The server derives the baseline; the request never carries it from the browser.
assert.equal("birth_date" in openCall.args, false);
+2 -2
View File
@@ -85,8 +85,8 @@ test("checked-in registry verifies hashed product packages and leaves consult on
[
{
name: "jyotish-birth-time-rectification",
version: "10.0.7",
sha256: "52150d2e2d970fdfac81447355451f0b49b2a87c7565ac88f34bb1fea033d459",
version: "10.0.8",
sha256: "d096db3516d55d604e6f2bb7ab24de4a81c9b5cdcd9d24f0e3e4fa6495069463",
},
{
name: "jyotish-personal-report",