Этап 4 добивка двух хвостов: settlement polarity и чистое разделение month_close / RBP в live acceptance

This commit is contained in:
2026-03-29 16:30:10 +03:00
parent f74e7b697a
commit c82ebd70b7
12 changed files with 705 additions and 19 deletions
@@ -76,6 +76,37 @@ describe("assistant mode API", () => {
expect(session.body.session.items.length).toBe(4);
});
it("recovers company-specific scope for settlement follow-up without explicit month anchor", async () => {
const app = createApp();
const sessionId = `asst-scope-${Date.now()}`;
const first = await request(app).post("/api/assistant/message").send({
session_id: sessionId,
useMock: true,
promptVersion: "normalizer_v2_0_2",
user_message:
"По оплате поставщику на счете 60 в июле 2020 остался хвост. Проверь закрытие по договору и объекту расчетов."
});
expect(first.status).toBe(200);
const second = await request(app).post("/api/assistant/message").send({
session_id: sessionId,
useMock: true,
promptVersion: "normalizer_v2_0_2",
user_message: "Это по тому же договору: закрытие подтверждено или хвост все еще живет?"
});
expect(second.status).toBe(200);
const debug = second.body.debug ?? {};
expect(Array.isArray(debug.business_scope_resolved)).toBe(true);
expect(debug.business_scope_resolved).toContain("company_specific_accounting");
expect(Array.isArray(debug.scope_resolution_reason)).toBe(true);
expect(debug.scope_resolution_reason).toContain("settlement_claim_company_scope_recovery");
expect(["resolved_supplier", "resolved_customer", "mixed", "unknown", "not_applicable"]).toContain(
String(debug.polarity_resolution_status ?? "")
);
});
it("executes factual retrieval for routed fragments", async () => {
const app = createApp();
@@ -243,6 +243,18 @@ describe("stage4 blocker-pack runtime guards", () => {
expect(units.some((item: any) => item.lifecycle_domain === "customer_settlement")).toBe(false);
});
it("resolves supplier polarity with dominant 60-account signals despite weak customer noise", () => {
const guard = resolveDomainPolarityGuard({
userMessage:
"Поставщик и счет 60: нужно проверить закрытие долга. В поле комментария случайно есть слово customer.",
focusDomainHint: "settlements_60_62"
});
expect(guard.polarity).toBe("supplier_payable");
expect(guard.outcome).toBe("passed");
expect(guard.reason_codes).not.toContain("unresolved_supplier_customer_polarity");
});
it("cleans polluted account anchors from date/amount numerics", () => {
const guard = resolveDomainPolarityGuard({
userMessage: "VAT 233.33 on 13 july 2020 and 15 july 2020, account 68.02 in company snapshot.",
@@ -496,6 +508,9 @@ describe("stage4 blocker-pack runtime guards", () => {
expect(claimAudit.resolved_anchors.vat_signal).toHaveLength(0);
expect(claimAudit.resolved_anchors.fixed_asset_signal).toHaveLength(0);
expect(claimAudit.required_anchors).toContain("advance_signal");
expect(claimAudit.settlement_role).toBe("customer");
expect(claimAudit.polarity_resolution_status).toBe("resolved_customer");
expect((claimAudit.settlement_role_resolution_reason ?? []).length).toBeGreaterThan(0);
});
it("keeps VAT priority over supplier wording in shared domain inference", () => {