АРЧ АП11 - Архитектура после регресса: Архитектура: сделать profile exact-ответы business-first и убрать служебный aggregate-пreamble
This commit is contained in:
parent
d8ce2b7d88
commit
4bb0187510
|
|
@ -245,6 +245,10 @@ Latest continuity-authority convergence evidence after the current route pass:
|
|||
- deterministic organization-fact boundary replies can now trigger from grounded continuity even when `sessionScope.selectedOrganization` and `sessionScope.activeOrganization` are both empty at runtime entry;
|
||||
- the chat layer now records whether it entered with grounded continuity and which organization came from that continuity snapshot, making future saved-session review less blind;
|
||||
- proactive organization offer logic is now explicitly blocked when grounded address continuity already exists, so the chat layer does not re-offer company selection on top of an already grounded business session;
|
||||
- the first human-answer-shaping cleanup pass is now applied to heavy profile/aggregate exact answers:
|
||||
- `period_coverage_profile` and `document_type_and_account_section_profile` now start with a direct business-first lead (`Коротко: ...`) instead of service-flavored intros like `профиль собран` / `строк агрегата`;
|
||||
- the top block now states the business conclusion first and leaves ranked detail blocks below, which reduces the catalog-like feel without hiding the actual data;
|
||||
- this is still only the first shaping seam: other long exact replies, especially VAT and some ranking families, still need the same treatment;
|
||||
- this pass does not yet finish full single-owner continuity, but it narrows one of the remaining seams where route arbitration and scope memory could disagree about whether the session was still grounded.
|
||||
|
||||
## Next Execution Slice (2026-04-18)
|
||||
|
|
|
|||
|
|
@ -2229,10 +2229,30 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
const includeSections = focus === "full_profile" || focus === "sections_only" || focus === "sections_rare_only";
|
||||
const includeDocTypesLowOnly = focus === "doc_types_rare_only";
|
||||
const includeSectionsLowOnly = focus === "sections_rare_only";
|
||||
const lines = [
|
||||
"Профиль типов документов и разделов учета собран (movement-based aggregate).",
|
||||
`Строк агрегата: ${rows.length}.`
|
||||
];
|
||||
const topDocType = docTypeRanking[0] ?? null;
|
||||
const rareDocType = docTypeRankingLow[0] ?? null;
|
||||
const topSection = sectionRanking[0] ?? null;
|
||||
const rareSection = sectionRankingLow[0] ?? null;
|
||||
const formatSectionTitle = (section) => {
|
||||
const label = ACCOUNT_SECTION_LABELS[section];
|
||||
return label ? `${section} (${label})` : section;
|
||||
};
|
||||
const directLeadParts = [];
|
||||
if (includeDocTypesLowOnly && rareDocType) {
|
||||
directLeadParts.push(`реже всего используется тип документа ${rareDocType.docType} (${rareDocType.count})`);
|
||||
}
|
||||
else if (includeDocTypes && topDocType) {
|
||||
directLeadParts.push(`чаще всего используется тип документа ${topDocType.docType} (${topDocType.count})`);
|
||||
}
|
||||
if (includeSectionsLowOnly && rareSection) {
|
||||
directLeadParts.push(`среди разделов учета минимальная активность у ${formatSectionTitle(rareSection.section)} (${rareSection.count})`);
|
||||
}
|
||||
else if (includeSections && topSection) {
|
||||
directLeadParts.push(`среди разделов учета лидирует ${formatSectionTitle(topSection.section)} (${topSection.count})`);
|
||||
}
|
||||
const lines = directLeadParts.length > 0
|
||||
? [`Коротко: ${directLeadParts.join("; ")}.`]
|
||||
: ["Коротко: по доступному срезу профиль типов документов и разделов учета не подтвердился."];
|
||||
if (includeDocTypes) {
|
||||
if (docTypeRanking.length > 0) {
|
||||
if (includeDocTypesLowOnly) {
|
||||
|
|
@ -2263,8 +2283,7 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
if (includeSectionsLowOnly) {
|
||||
lines.push("Наименее заполненные разделы учета (по операциям Дт+Кт):");
|
||||
lines.push(...sectionRankingLow.map((item, index) => {
|
||||
const label = ACCOUNT_SECTION_LABELS[item.section];
|
||||
const sectionTitle = label ? `${item.section} (${label})` : item.section;
|
||||
const sectionTitle = formatSectionTitle(item.section);
|
||||
const share = formatPercent(item.count, sectionTotal);
|
||||
return share
|
||||
? `${index + 1}. ${sectionTitle}: ${item.count} (${share})`
|
||||
|
|
@ -2274,8 +2293,7 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
else {
|
||||
lines.push("Наиболее заполненные разделы учета (по операциям Дт+Кт):");
|
||||
lines.push(...sectionRanking.slice(0, 10).map((item, index) => {
|
||||
const label = ACCOUNT_SECTION_LABELS[item.section];
|
||||
const sectionTitle = label ? `${item.section} (${label})` : item.section;
|
||||
const sectionTitle = formatSectionTitle(item.section);
|
||||
const share = formatPercent(item.count, sectionTotal);
|
||||
return share
|
||||
? `${index + 1}. ${sectionTitle}: ${item.count} (${share})`
|
||||
|
|
@ -2283,8 +2301,7 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
}));
|
||||
lines.push("Разделы с минимальной активностью (среди использованных):");
|
||||
lines.push(...sectionRankingLow.map((item, index) => {
|
||||
const label = ACCOUNT_SECTION_LABELS[item.section];
|
||||
const sectionTitle = label ? `${item.section} (${label})` : item.section;
|
||||
const sectionTitle = formatSectionTitle(item.section);
|
||||
return `${index + 1}. ${sectionTitle}: ${item.count}`;
|
||||
}));
|
||||
}
|
||||
|
|
@ -2366,10 +2383,32 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
operationalWindow.operationalTo !== null &&
|
||||
operationalWindow.dataTo !== null &&
|
||||
operationalWindow.operationalTo < operationalWindow.dataTo;
|
||||
const lines = [
|
||||
"Профиль периодов базы собран (movement-based aggregate).",
|
||||
`Строк агрегата: ${rows.length}.`
|
||||
];
|
||||
const directLeadParts = [];
|
||||
if (includeCoverage) {
|
||||
if (hasTailYears &&
|
||||
operationalWindow.operationalFrom !== null &&
|
||||
operationalWindow.operationalTo !== null) {
|
||||
directLeadParts.push(`рабочий период с выраженной активностью: ${operationalWindow.operationalFrom}..${operationalWindow.operationalTo}`);
|
||||
}
|
||||
else if (minDate || maxDate) {
|
||||
directLeadParts.push(`данные в базе подтверждены за период ${minDate ?? "н/д"} .. ${maxDate ?? "н/д"}`);
|
||||
}
|
||||
}
|
||||
if (includeTopYear && topYearByDocs) {
|
||||
directLeadParts.push(`самый активный год по документам — ${topYearByDocs.year} (${topYearByDocs.count})`);
|
||||
}
|
||||
if (includeBottomYear && bottomYearByDocs) {
|
||||
directLeadParts.push(`самый спокойный год по документам — ${bottomYearByDocs.year} (${bottomYearByDocs.count})`);
|
||||
}
|
||||
if (includeTopMonth && topMonthByOps) {
|
||||
directLeadParts.push(`самый активный месяц по операциям — ${topMonthByOps.month} (${topMonthByOps.count})`);
|
||||
}
|
||||
if (includeBottomMonth && bottomMonthByOps) {
|
||||
directLeadParts.push(`самый спокойный месяц по операциям — ${bottomMonthByOps.month} (${bottomMonthByOps.count})`);
|
||||
}
|
||||
const lines = directLeadParts.length > 0
|
||||
? [`Коротко: ${directLeadParts.join("; ")}.`]
|
||||
: ["Коротко: по доступному срезу периодический профиль базы не подтвердился."];
|
||||
if (includeCoverage) {
|
||||
if (hasTailYears &&
|
||||
operationalWindow.operationalFrom !== null &&
|
||||
|
|
|
|||
|
|
@ -2861,11 +2861,30 @@ function composeFactualReplyBody(
|
|||
focus === "full_profile" || focus === "sections_only" || focus === "sections_rare_only";
|
||||
const includeDocTypesLowOnly = focus === "doc_types_rare_only";
|
||||
const includeSectionsLowOnly = focus === "sections_rare_only";
|
||||
const topDocType = docTypeRanking[0] ?? null;
|
||||
const rareDocType = docTypeRankingLow[0] ?? null;
|
||||
const topSection = sectionRanking[0] ?? null;
|
||||
const rareSection = sectionRankingLow[0] ?? null;
|
||||
const formatSectionTitle = (section: string): string => {
|
||||
const label = ACCOUNT_SECTION_LABELS[section];
|
||||
return label ? `${section} (${label})` : section;
|
||||
};
|
||||
|
||||
const lines: string[] = [
|
||||
"Профиль типов документов и разделов учета собран (movement-based aggregate).",
|
||||
`Строк агрегата: ${rows.length}.`
|
||||
];
|
||||
const directLeadParts: string[] = [];
|
||||
if (includeDocTypesLowOnly && rareDocType) {
|
||||
directLeadParts.push(`реже всего используется тип документа ${rareDocType.docType} (${rareDocType.count})`);
|
||||
} else if (includeDocTypes && topDocType) {
|
||||
directLeadParts.push(`чаще всего используется тип документа ${topDocType.docType} (${topDocType.count})`);
|
||||
}
|
||||
if (includeSectionsLowOnly && rareSection) {
|
||||
directLeadParts.push(`среди разделов учета минимальная активность у ${formatSectionTitle(rareSection.section)} (${rareSection.count})`);
|
||||
} else if (includeSections && topSection) {
|
||||
directLeadParts.push(`среди разделов учета лидирует ${formatSectionTitle(topSection.section)} (${topSection.count})`);
|
||||
}
|
||||
|
||||
const lines: string[] = directLeadParts.length > 0
|
||||
? [`Коротко: ${directLeadParts.join("; ")}.`]
|
||||
: ["Коротко: по доступному срезу профиль типов документов и разделов учета не подтвердился."];
|
||||
|
||||
if (includeDocTypes) {
|
||||
if (docTypeRanking.length > 0) {
|
||||
|
|
@ -2901,8 +2920,7 @@ function composeFactualReplyBody(
|
|||
lines.push("Наименее заполненные разделы учета (по операциям Дт+Кт):");
|
||||
lines.push(
|
||||
...sectionRankingLow.map((item, index) => {
|
||||
const label = ACCOUNT_SECTION_LABELS[item.section];
|
||||
const sectionTitle = label ? `${item.section} (${label})` : item.section;
|
||||
const sectionTitle = formatSectionTitle(item.section);
|
||||
const share = formatPercent(item.count, sectionTotal);
|
||||
return share
|
||||
? `${index + 1}. ${sectionTitle}: ${item.count} (${share})`
|
||||
|
|
@ -2913,8 +2931,7 @@ function composeFactualReplyBody(
|
|||
lines.push("Наиболее заполненные разделы учета (по операциям Дт+Кт):");
|
||||
lines.push(
|
||||
...sectionRanking.slice(0, 10).map((item, index) => {
|
||||
const label = ACCOUNT_SECTION_LABELS[item.section];
|
||||
const sectionTitle = label ? `${item.section} (${label})` : item.section;
|
||||
const sectionTitle = formatSectionTitle(item.section);
|
||||
const share = formatPercent(item.count, sectionTotal);
|
||||
return share
|
||||
? `${index + 1}. ${sectionTitle}: ${item.count} (${share})`
|
||||
|
|
@ -2925,8 +2942,7 @@ function composeFactualReplyBody(
|
|||
lines.push("Разделы с минимальной активностью (среди использованных):");
|
||||
lines.push(
|
||||
...sectionRankingLow.map((item, index) => {
|
||||
const label = ACCOUNT_SECTION_LABELS[item.section];
|
||||
const sectionTitle = label ? `${item.section} (${label})` : item.section;
|
||||
const sectionTitle = formatSectionTitle(item.section);
|
||||
return `${index + 1}. ${sectionTitle}: ${item.count}`;
|
||||
})
|
||||
);
|
||||
|
|
@ -3024,10 +3040,36 @@ function composeFactualReplyBody(
|
|||
operationalWindow.dataTo !== null &&
|
||||
operationalWindow.operationalTo < operationalWindow.dataTo;
|
||||
|
||||
const lines: string[] = [
|
||||
"Профиль периодов базы собран (movement-based aggregate).",
|
||||
`Строк агрегата: ${rows.length}.`
|
||||
];
|
||||
const directLeadParts: string[] = [];
|
||||
if (includeCoverage) {
|
||||
if (
|
||||
hasTailYears &&
|
||||
operationalWindow.operationalFrom !== null &&
|
||||
operationalWindow.operationalTo !== null
|
||||
) {
|
||||
directLeadParts.push(
|
||||
`рабочий период с выраженной активностью: ${operationalWindow.operationalFrom}..${operationalWindow.operationalTo}`
|
||||
);
|
||||
} else if (minDate || maxDate) {
|
||||
directLeadParts.push(`данные в базе подтверждены за период ${minDate ?? "н/д"} .. ${maxDate ?? "н/д"}`);
|
||||
}
|
||||
}
|
||||
if (includeTopYear && topYearByDocs) {
|
||||
directLeadParts.push(`самый активный год по документам — ${topYearByDocs.year} (${topYearByDocs.count})`);
|
||||
}
|
||||
if (includeBottomYear && bottomYearByDocs) {
|
||||
directLeadParts.push(`самый спокойный год по документам — ${bottomYearByDocs.year} (${bottomYearByDocs.count})`);
|
||||
}
|
||||
if (includeTopMonth && topMonthByOps) {
|
||||
directLeadParts.push(`самый активный месяц по операциям — ${topMonthByOps.month} (${topMonthByOps.count})`);
|
||||
}
|
||||
if (includeBottomMonth && bottomMonthByOps) {
|
||||
directLeadParts.push(`самый спокойный месяц по операциям — ${bottomMonthByOps.month} (${bottomMonthByOps.count})`);
|
||||
}
|
||||
|
||||
const lines: string[] = directLeadParts.length > 0
|
||||
? [`Коротко: ${directLeadParts.join("; ")}.`]
|
||||
: ["Коротко: по доступному срезу периодический профиль базы не подтвердился."];
|
||||
|
||||
if (includeCoverage) {
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -628,9 +628,11 @@ describe("address compose stage utf8 headers", () => {
|
|||
}
|
||||
]);
|
||||
expect(reply.responseType).toBe("FACTUAL_SUMMARY");
|
||||
expect(reply.text).toContain("Профиль периодов базы собран");
|
||||
expect(reply.text).toContain("Коротко:");
|
||||
expect(reply.text).toContain("Самый активный год по документам: 2019 (1004).");
|
||||
expect(reply.text).toContain("Самый активный месяц по операциям: 2015-02 (1249).");
|
||||
expect(reply.text).not.toContain("Строк агрегата");
|
||||
expect(reply.text).not.toContain("movement-based aggregate");
|
||||
});
|
||||
|
||||
it("renders document type + account section profile summary", () => {
|
||||
|
|
@ -685,10 +687,12 @@ describe("address compose stage utf8 headers", () => {
|
|||
}
|
||||
]);
|
||||
expect(reply.responseType).toBe("FACTUAL_SUMMARY");
|
||||
expect(reply.text).toContain("Профиль типов документов и разделов учета собран");
|
||||
expect(reply.text).toContain("Коротко:");
|
||||
expect(reply.text).toContain("Списание с расчетного счета: 2352");
|
||||
expect(reply.text).toContain("90 (Продажи): 2973");
|
||||
expect(reply.text).toContain("58 (Финансовые вложения): 2");
|
||||
expect(reply.text).not.toContain("Строк агрегата");
|
||||
expect(reply.text).not.toContain("movement-based aggregate");
|
||||
});
|
||||
|
||||
it("returns focused answer for active year question (without month block)", () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue