Закрыть phase96 reviewed-route складских резервов и ликвидности

This commit is contained in:
dctouch 2026-05-13 00:11:01 +03:00
parent b99a3be083
commit 5220cb2e0e
24 changed files with 1189 additions and 139 deletions

View File

@ -84,10 +84,11 @@ Fresh validation cut:
- Completed autonomy slice inside that loop: `Accounting Profit-Margin Reviewed Route`: `accounting_profit_margin` is now promoted from `needs_route_enablement` into a reviewed 90/91/99 accounting-result route with accepted live replay.
- Completed autonomy slice inside that loop: `Debt Due-Date Aging Reviewed Route`: `debt_due_date_aging_quality` is now promoted from proxy-only route-candidate gap into a reviewed payment-term/open-balance route with accepted live replay.
- Completed autonomy slice inside that loop: `Vendor/Procurement Quality Reviewed Route`: `vendor_risk_procurement_quality` now promotes to reviewed procurement-concentration evidence when confirmed outgoing payment, bank-like recipient segregation, non-financial recipient, counterparty-role, and contract-usage signals are reachable; phase95 live replay is accepted.
- Current live canary: `phase95_vendor_procurement_quality_reviewed_route_live2` accepted `7/7`.
- Current accepted autorun: `AGENT | Phase 95 vendor/procurement quality reviewed route` (`gen-ag05121357-9ea5d6`).
- Completed autonomy slice inside that loop: `Inventory Reserve/Liquidation Quality Reviewed Route`: `inventory_reserve_liquidation_quality` now promotes to reviewed inventory quality-event evidence from posted write-off, receipt-adjustment, stocktaking, and revaluation documents; phase96 live replay is accepted.
- Current live canary: `phase96_inventory_reserve_liquidation_quality_rerun` accepted `2/2`.
- Current accepted autorun: `AGENT | Phase 96 inventory reserve/liquidation quality-events` (`gen-ag05122057-c9786e`).
- Implementation breadth: `~99% (Open-World Bounded Autonomy Breadth through Slice 25)`.
- Next active slice: select the remaining phase92 proof family `inventory_reserve_liquidation_quality`.
- Next active slice: start the broader open-world schema/primitive discovery module and use phase91-phase96 as regression canaries.
- Active module progress: `~99% (Agentic Semantic Development Loop, accepted dogfood loop + autorun hygiene; manual GUI confirmation still required)`.
## Reporting Rule
@ -99,7 +100,7 @@ Use these labels when reporting progress:
- `Прогресс модуля: 99% (Open-World Bounded Autonomy Breadth, active slice: Semantic Control Gate)` while discussing current module closure after the EHMO-derived critical subset accepted live again with W5/W7 hardening.
- `Прогресс модуля: 99% (Agentic Semantic Development Loop, accepted dogfood loop + autorun hygiene; manual GUI confirmation still required)` when discussing the current development-loop operating layer.
- `Прогресс модуля: 100% (Open-World Route Candidate Promotion, declared phase90 slice accepted)` when discussing the route-candidate handoff slice itself.
- `Прогресс модуля: 92% (Route-Candidate-Driven Enablement Loop, active slice: third reviewed proof-family route accepted)` when discussing the current candidate-driven enablement loop.
- `Прогресс модуля: 100% (Route-Candidate-Driven Enablement Loop, final reviewed proof-family route accepted; use as regression gate)` when discussing the current candidate-driven enablement loop.
- `Open-World Business Overview implementation breadth: ~99%, Semantic Control Gate critical subset accepted, fat GUI pack still pending` when discussing only the already wired Slice 25 breadth.
- `Прогресс модуля: X% (Open-World Bounded Autonomy Breadth, active slice: <name>)` for later breadth work after the Semantic Control Gate is accepted.
@ -135,7 +136,7 @@ Remaining work belongs to the next breadth module:
- confirm the latest autorun Cyrillic hygiene cut in the GUI after backend refresh and inspect frontend/API payloads if old replacement characters remain visible;
- continue dogfooding the `Agentic Semantic Development Loop` on real stage packs, especially generated-question quality, semantic business audit, repair handoff, and rerun acceptance;
- finish closure of the `Open-World Semantic Control Gate` opened by `assistant-stage1-EHMOy3lNFt`; the EHMO-derived critical subset is accepted live after W5/W7 hardening, but the fat GUI pack and residual answer-shape roughness still need final review;
- extend `business_overview` beyond money-flow/activity, customer and supplier concentration, document/account-section activity mix, counterparty role split, contract usage, yearly operating-flow dynamics, explicit profit/margin wording boundaries, explicit debt due-date wording boundaries, explicit inventory reserve/liquidation wording boundaries, explicit supplier/procurement-quality wording boundaries, explicit-period VAT/tax, as-of-date debt position, open-settlement concentration, contract-date debt age, debt staleness-risk proxy, as-of-date inventory position, trading-margin proxy, sales-to-stock inventory proxy, warehouse staleness-risk proxy, the missing-proof ledger, the reviewed accounting profit/margin route, the reviewed debt due-date aging route, and the reviewed vendor/procurement concentration route into confirmed reserve/write-off/liquidation inventory evidence families;
- extend open-world coverage beyond the reviewed `business_overview` families already wired for money-flow/activity, customer and supplier concentration, document/account-section activity mix, counterparty role split, contract usage, yearly operating-flow dynamics, explicit profit/margin, debt due-date aging, inventory reserve/liquidation quality events, supplier/procurement quality, explicit-period VAT/tax, as-of-date debt and inventory position, open-settlement concentration, contract-date debt age, staleness proxies, trading-margin proxy, sales-to-stock inventory proxy, the missing-proof ledger, and the phase93-phase96 reviewed routes;
- broader dynamic schema traversal for unfamiliar 1C asks;
- more primitive descriptors where live evidence proves a real gap;
- more replay-backed domain packs that start from user business meaning, not from route convenience;
@ -156,17 +157,19 @@ For current planning, read:
1. `README.md`
2. this document
3. `29 - debt_due_date_aging_reviewed_route_2026-05-10.md`
4. `28 - accounting_profit_margin_reviewed_route_2026-05-10.md`
5. `27 - proof_family_enablement_candidates_2026-05-10.md`
6. `26 - route_candidate_driven_enablement_loop_2026-05-10.md`
7. `25 - open_world_route_candidate_promotion_2026-05-10.md`
8. `24 - agentic_semantic_development_loop_and_autorun_hygiene_2026-05-10.md`
9. `23 - current_execution_spine_and_semantic_control_gate_2026-05-05.md`
10. `22 - open_world_bounded_autonomy_breadth_2026-05-01.md`
11. `20 - planner_autonomy_consolidation_2026-05-01.md`
12. `19 - inventory_stock_open_world_breadth_proof_2026-05-01.md`
13. `17 - post_f_semantic_integrity_hardening_2026-04-23.md`
14. `16 - data_need_graph_and_open_world_mcp_plan_2026-04-22.md`
3. `31 - inventory_reserve_liquidation_quality_reviewed_route_2026-05-12.md`
4. `30 - vendor_procurement_quality_reviewed_route_2026-05-12.md`
5. `29 - debt_due_date_aging_reviewed_route_2026-05-10.md`
6. `28 - accounting_profit_margin_reviewed_route_2026-05-10.md`
7. `27 - proof_family_enablement_candidates_2026-05-10.md`
8. `26 - route_candidate_driven_enablement_loop_2026-05-10.md`
9. `25 - open_world_route_candidate_promotion_2026-05-10.md`
10. `24 - agentic_semantic_development_loop_and_autorun_hygiene_2026-05-10.md`
11. `23 - current_execution_spine_and_semantic_control_gate_2026-05-05.md`
12. `22 - open_world_bounded_autonomy_breadth_2026-05-01.md`
13. `20 - planner_autonomy_consolidation_2026-05-01.md`
14. `19 - inventory_stock_open_world_breadth_proof_2026-05-01.md`
15. `17 - post_f_semantic_integrity_hardening_2026-04-23.md`
16. `16 - data_need_graph_and_open_world_mcp_plan_2026-04-22.md`
Documents `01` through `15` remain valuable, but mostly as the historical architecture trail.

View File

@ -97,7 +97,7 @@ Status:
- implementation state: operational dogfood loop exists and has an accepted first loop artifact;
- semantic status: accepted loop artifact is useful, but manual GUI confirmation remains required;
- hygiene status: saved autorun/runtime Cyrillic repair is covered by code/tests and the GUI-side check is reported clean;
- current autonomy status: `Open-World Route Candidate Promotion` is live-accepted at `5/5`; `Route-Candidate-Driven Enablement Loop` has accepted phase91/phase92 canaries; `accounting_profit_margin` is promoted into a reviewed 90/91/99 route by phase93; `debt_due_date_aging_quality` is promoted into a reviewed payment-term/open-balance route by phase94; both reviewed proof-family routes are saved as accepted AGENT autoruns;
- current autonomy status: `Open-World Route Candidate Promotion` is live-accepted at `5/5`; `Route-Candidate-Driven Enablement Loop` has accepted phase91/phase92 canaries; `accounting_profit_margin` is promoted into a reviewed 90/91/99 route by phase93; `debt_due_date_aging_quality` is promoted into a reviewed payment-term/open-balance route by phase94; `vendor_risk_procurement_quality` is promoted into reviewed procurement-concentration evidence by phase95; `inventory_reserve_liquidation_quality` is promoted into reviewed inventory quality-event evidence by phase96; all four reviewed proof-family routes are saved as accepted AGENT autoruns;
- risk: medium, because the loop is now infrastructure for future acceptance decisions, not just a local route fix.
Recommended reporting line:
@ -113,16 +113,16 @@ Still open:
- the first accepted dogfood loop proves the mechanism, not all future stage packs;
- generated question quality still needs pressure from real GUI runs and user feedback;
- broad arbitrary 1C autonomy is still bounded by reviewed routes, truth gates, and replay evidence;
- route-candidate-driven enablement still needs more proof-family promotions after the accepted `accounting_profit_margin` and `debt_due_date_aging_quality` routes;
- route-candidate-driven enablement is now closed after the accepted phase93-phase96 proof-family routes, and should be treated as a regression gate rather than as an open promotion backlog;
- manual GUI confirmation remains required before declaring a fat AGENT pack fully accepted.
## Next Work
Next operational pass:
1. Use phase94 as the current candidate-driven enablement canary.
1. Use phase91-phase96 as current candidate-driven enablement canaries.
2. Continue dogfooding the stage-loop on real Open-World/agentic packs.
3. Pick the next proof family, likely vendor/procurement quality or inventory reserve/liquidation, and require the same live replay/save-after-acceptance discipline.
3. Move the active autonomy work to broader schema/primitive discovery and keep the same live replay/save-after-acceptance discipline.
4. Keep Post-F, phase83, inventory, business-overview, and mojibake autorun cases as regression canaries.
## Canonical Reading Order Update
@ -131,13 +131,15 @@ For current planning, read:
1. `README.md`
2. `21 - current_status_canon_2026-05-01.md`
3. `29 - debt_due_date_aging_reviewed_route_2026-05-10.md`
4. `28 - accounting_profit_margin_reviewed_route_2026-05-10.md`
5. `27 - proof_family_enablement_candidates_2026-05-10.md`
6. `26 - route_candidate_driven_enablement_loop_2026-05-10.md`
7. `25 - open_world_route_candidate_promotion_2026-05-10.md`
8. this document
9. `23 - current_execution_spine_and_semantic_control_gate_2026-05-05.md`
10. `22 - open_world_bounded_autonomy_breadth_2026-05-01.md`
11. `20 - planner_autonomy_consolidation_2026-05-01.md`
12. `17 - post_f_semantic_integrity_hardening_2026-04-23.md`
3. `31 - inventory_reserve_liquidation_quality_reviewed_route_2026-05-12.md`
4. `30 - vendor_procurement_quality_reviewed_route_2026-05-12.md`
5. `29 - debt_due_date_aging_reviewed_route_2026-05-10.md`
6. `28 - accounting_profit_margin_reviewed_route_2026-05-10.md`
7. `27 - proof_family_enablement_candidates_2026-05-10.md`
8. `26 - route_candidate_driven_enablement_loop_2026-05-10.md`
9. `25 - open_world_route_candidate_promotion_2026-05-10.md`
10. this document
11. `23 - current_execution_spine_and_semantic_control_gate_2026-05-05.md`
12. `22 - open_world_bounded_autonomy_breadth_2026-05-01.md`
13. `20 - planner_autonomy_consolidation_2026-05-01.md`
14. `17 - post_f_semantic_integrity_hardening_2026-04-23.md`

View File

@ -52,6 +52,8 @@ Live semantic replay:
- accepted user-runnable autorun for that route replay: `AGENT | Phase 94 debt due-date aging reviewed route` (`gen-ag05101319-c04f79`).
- third proof-family route replay: `artifacts/domain_runs/phase95_vendor_procurement_quality_reviewed_route_live2`, `7/7` passed, `0` warnings, `0` failures.
- accepted user-runnable autorun for that route replay: `AGENT | Phase 95 vendor/procurement quality reviewed route` (`gen-ag05121357-9ea5d6`).
- final proof-family route replay: `artifacts/domain_runs/phase96_inventory_reserve_liquidation_quality_rerun`, `2/2` passed, `0` warnings, `0` failures.
- accepted user-runnable autorun for that route replay: `AGENT | Phase 96 inventory reserve/liquidation quality-events` (`gen-ag05122057-c9786e`).
The replay proves the user-facing route-candidate canary remains healthy while the development tooling starts treating route candidates as repair-loop input:
@ -64,25 +66,25 @@ The replay proves the user-facing route-candidate canary remains healthy while t
- exact accounting profit/margin wording now has the first reviewed route implementation and can move to `ready_for_reviewed_execution` through confirmed 90/91/99 accounting rows.
- exact due-date debt aging wording now has the second reviewed route implementation and can move to `ready_for_reviewed_execution` through confirmed open-balance/payment-term evidence, while absent payment terms produce an honest checked-negative boundary answer.
- exact vendor-risk/procurement wording now has a live-accepted reviewed procurement-concentration route that can move to `ready_for_reviewed_execution` when confirmed outgoing payment, bank-like recipient segregation, non-financial recipient, counterparty-role, and contract-usage evidence are reachable. It still does not prove supplier reliability, delivery quality, payment purpose, contract-term compliance, or full expense structure.
- exact reserve/write-off/liquidation wording now has a live-accepted reviewed inventory quality-events route that can move to `ready_for_reviewed_execution` when posted write-off, receipt-adjustment, stocktaking, or revaluation documents are reachable. It still does not prove market liquidation value, management reserve policy, or full warehouse health.
## Status
Current module wording:
`Route-Candidate-Driven Enablement Loop, active slice: third reviewed proof-family route accepted`
`Route-Candidate-Driven Enablement Loop, active slice: final reviewed proof-family route accepted`
Progress: `92%`.
Progress: `100%`.
The first cut proved the handoff mechanics and live canary. The second cut proved real proof-family candidates and a saved accepted AGENT pack. The third cut proved the intended promotion loop on `accounting_profit_margin`. The fourth cut proved the same loop on `debt_due_date_aging_quality`, including short boundary follow-up continuity and saved accepted autorun hygiene. The fifth cut proves `vendor_risk_procurement_quality` as reviewed procurement-concentration evidence with accepted phase95 replay. The module is still not complete because the inventory reserve/liquidation proof family remains open.
The first cut proved the handoff mechanics and live canary. The second cut proved real proof-family candidates and a saved accepted AGENT pack. The third cut proved the intended promotion loop on `accounting_profit_margin`. The fourth cut proved the same loop on `debt_due_date_aging_quality`, including short boundary follow-up continuity and saved accepted autorun hygiene. The fifth cut proved `vendor_risk_procurement_quality` as reviewed procurement-concentration evidence with accepted phase95 replay. The final cut proved `inventory_reserve_liquidation_quality` as reviewed inventory quality-event evidence with accepted phase96 replay. The declared route-candidate-driven enablement loop is now closed; future arbitrary 1C breadth work should treat these routes as regression gates, not as open blockers.
## Next Work
Next slices:
1. Pick the final phase92 proof family: `inventory_reserve_liquidation_quality`.
2. Implement or explicitly bound that final family only if reliable 1C evidence is reachable.
3. Rerun phase95 as a canary plus the focused inventory route-specific pack.
4. Save the accepted pack into autoruns only after live replay and semantic review pass.
1. Treat this module as closed and keep phase91/92/93/94/95/96 as regression canaries.
2. Start the next broader open-world autonomy slice: schema/primitive discovery beyond the selected phase92 proof families.
3. Keep save-after-acceptance discipline for any new AGENT packs.
See also:
@ -90,3 +92,4 @@ See also:
- [28 - accounting_profit_margin_reviewed_route_2026-05-10.md](./28%20-%20accounting_profit_margin_reviewed_route_2026-05-10.md)
- [29 - debt_due_date_aging_reviewed_route_2026-05-10.md](./29%20-%20debt_due_date_aging_reviewed_route_2026-05-10.md)
- [30 - vendor_procurement_quality_reviewed_route_2026-05-12.md](./30%20-%20vendor_procurement_quality_reviewed_route_2026-05-12.md)
- [31 - inventory_reserve_liquidation_quality_reviewed_route_2026-05-12.md](./31%20-%20inventory_reserve_liquidation_quality_reviewed_route_2026-05-12.md)

View File

@ -101,23 +101,23 @@ This means `vendor_risk_procurement_quality` is no longer only a missing-proof c
Current module wording:
`Route-Candidate-Driven Enablement Loop, active slice: third reviewed proof-family route accepted`
`Route-Candidate-Driven Enablement Loop, final reviewed proof-family route accepted; use as regression gate`
Progress: `92%`.
Progress: `100%`.
This cut proved the missing-proof candidate surface and accepted user-runnable AGENT canary. Phase93 then implemented the first exact reviewed route for the accounting profit/margin family. Phase94 implemented the second reviewed route for due-date debt aging and verified short boundary follow-up continuity. Phase95 now promotes vendor/procurement quality through reviewed procurement-concentration evidence and accepted live replay. The remaining closure work is inventory reserve/liquidation enablement or an explicit bounded non-implementation decision.
This cut proved the missing-proof candidate surface and accepted user-runnable AGENT canary. Phase93 then implemented the first exact reviewed route for the accounting profit/margin family. Phase94 implemented the second reviewed route for due-date debt aging and verified short boundary follow-up continuity. Phase95 promoted vendor/procurement quality through reviewed procurement-concentration evidence. Phase96 promoted inventory reserve/liquidation quality through reviewed inventory quality-event documents. The declared route-candidate-driven enablement loop is now closed and should be used as a regression gate for broader autonomy work.
## Next Work
Next slices:
1. Wire or explicitly bound the final remaining family: `inventory_reserve_liquidation_quality`.
2. Keep proxy-only inventory reserve/liquidation wording bounded until reviewed evidence exists.
3. Rerun phase95 as a canary plus the focused inventory route-specific pack.
4. Save the next AGENT autorun only after live replay and semantic review pass.
1. Use phase91-phase96 as regression canaries.
2. Start the next broader open-world schema/primitive discovery module.
3. Keep saving AGENT autoruns only after live replay and semantic review pass.
See also:
- [28 - accounting_profit_margin_reviewed_route_2026-05-10.md](./28%20-%20accounting_profit_margin_reviewed_route_2026-05-10.md)
- [29 - debt_due_date_aging_reviewed_route_2026-05-10.md](./29%20-%20debt_due_date_aging_reviewed_route_2026-05-10.md)
- [30 - vendor_procurement_quality_reviewed_route_2026-05-12.md](./30%20-%20vendor_procurement_quality_reviewed_route_2026-05-12.md)
- [31 - inventory_reserve_liquidation_quality_reviewed_route_2026-05-12.md](./31%20-%20inventory_reserve_liquidation_quality_reviewed_route_2026-05-12.md)

View File

@ -81,16 +81,16 @@ This keeps the phase93 accounting route as a canary while proving the candidate-
Current module wording:
`Route-Candidate-Driven Enablement Loop, active slice: third reviewed proof-family route accepted`
`Route-Candidate-Driven Enablement Loop, final reviewed proof-family route accepted; use as regression gate`
Progress: `92%`.
Progress: `100%`.
This was the first proof that the loop can turn a route candidate into an executable reviewed route. Phase94 repeated the pattern for due-date debt aging, and phase95 accepted vendor/procurement quality through reviewed procurement-concentration evidence. The module is not yet complete because inventory reserve/liquidation still needs the same treatment or an explicit bounded non-implementation decision.
This was the first proof that the loop can turn a route candidate into an executable reviewed route. Phase94 repeated the pattern for due-date debt aging, phase95 accepted vendor/procurement quality through reviewed procurement-concentration evidence, and phase96 accepted inventory reserve/liquidation quality through reviewed inventory quality-event documents. The declared loop is now closed and this route remains a regression canary.
## Next Work
Next slices:
1. Select the remaining proof family: `inventory_reserve_liquidation_quality`.
2. Wire only the smallest reliable reviewed route, not a broad heuristic.
3. Keep proxy-only reserve/liquidation wording bounded until the route is accepted.
1. Use this route as a regression canary during broader autonomy work.
2. Continue the next module through open-world schema/primitive discovery rather than more phase92 proof-family closure.
3. Keep proof-specific wording bounded unless a reviewed route has live evidence.

View File

@ -77,17 +77,16 @@ The accepted replay proves:
Current module wording:
`Route-Candidate-Driven Enablement Loop, active slice: third reviewed proof-family route accepted`
`Route-Candidate-Driven Enablement Loop, final reviewed proof-family route accepted; use as regression gate`
Progress: `92%`.
Progress: `100%`.
This is the second live-accepted proof that the loop can turn a route candidate into an executable reviewed route. Phase95 now accepts `vendor_risk_procurement_quality` through reviewed procurement-concentration evidence. The module is not yet complete because inventory reserve/liquidation still needs either reviewed exact route enablement or an explicit bounded non-implementation decision.
This is the second live-accepted proof that the loop can turn a route candidate into an executable reviewed route. Phase95 accepted `vendor_risk_procurement_quality` through reviewed procurement-concentration evidence, and phase96 accepted `inventory_reserve_liquidation_quality` through reviewed inventory quality-event documents. The declared loop is now closed and this route remains a regression canary.
## Next Work
Next slices:
1. Identify whether reliable 1C evidence exists for `inventory_reserve_liquidation_quality`.
2. Wire the smallest reviewed route only if it can prove the inventory business claim without overreach.
3. Keep proxy-only evidence bounded if exact proof is not reachable.
4. Rerun phase95 as a canary plus the focused inventory route-specific pack.
1. Use phase94, phase95, and phase96 as canaries for due-date, vendor, and inventory-quality continuity.
2. Move the active plan to broader open-world schema/primitive discovery.
3. Keep proxy-only evidence bounded when a reviewed route is not available.

View File

@ -62,29 +62,29 @@ Semantic/live replay:
Current module wording:
`Route-Candidate-Driven Enablement Loop, active slice: third reviewed proof-family route accepted`
`Route-Candidate-Driven Enablement Loop, superseded by phase96 final reviewed proof-family acceptance`
Progress: `92%`.
Progress: `100%` in the parent loop after phase96.
The loop has now promoted three phase92 proof families from candidate gaps into reviewed execution or locally reviewed execution:
This phase remains the vendor/procurement-quality proof. After this note, phase96 promoted the final remaining inventory reserve/liquidation proof family, so the parent Route-Candidate-Driven Enablement Loop is now closed.
The loop has now promoted four phase92 proof families from candidate gaps into reviewed execution or locally reviewed execution:
- `accounting_profit_margin` accepted live by phase93;
- `debt_due_date_aging_quality` accepted live by phase94;
- `vendor_risk_procurement_quality` accepted live through procurement-concentration evidence by phase95.
The module is still not complete because the remaining `inventory_reserve_liquidation_quality` family still needs either reviewed-route enablement or an explicit bounded non-implementation decision.
- `inventory_reserve_liquidation_quality` accepted live through inventory quality-event evidence by phase96.
## Next Work
Next slices:
1. Select the final remaining phase92 family: `inventory_reserve_liquidation_quality`.
2. Determine whether reachable 1C evidence can prove reserve/write-off/liquidation quality without overreach.
3. If yes, wire the smallest reviewed route and run a focused phase96 semantic replay.
4. If no, record an explicit bounded non-implementation decision and keep the proxy answer honest.
1. Use phase95 as a vendor/procurement regression canary during broader autonomy work.
2. See phase96 for the final inventory reserve/liquidation route and module closure.
See also:
- [26 - route_candidate_driven_enablement_loop_2026-05-10.md](./26%20-%20route_candidate_driven_enablement_loop_2026-05-10.md)
- [27 - proof_family_enablement_candidates_2026-05-10.md](./27%20-%20proof_family_enablement_candidates_2026-05-10.md)
- [29 - debt_due_date_aging_reviewed_route_2026-05-10.md](./29%20-%20debt_due_date_aging_reviewed_route_2026-05-10.md)
- [31 - inventory_reserve_liquidation_quality_reviewed_route_2026-05-12.md](./31%20-%20inventory_reserve_liquidation_quality_reviewed_route_2026-05-12.md)

View File

@ -0,0 +1,100 @@
# 31 - Inventory Reserve/Liquidation Quality Reviewed Route (2026-05-12)
This note records the final phase92 proof-family promotion inside the `Route-Candidate-Driven Enablement Loop`.
## Why This Slice Exists
Phase92 exposed `inventory_reserve_liquidation_quality` as a real route-candidate gap:
- users can naturally ask whether warehouse stock has reserves, write-offs, obsolete goods, or liquidation value;
- earlier business overview could show stock position, sales-to-stock proxy, and staleness-risk proxy;
- those proxies were useful but still not reviewed evidence for write-offs, stocktaking, revaluation, reserve policy, or market liquidation value.
The safe implementation target was therefore not a fake "full inventory health" route. It was the narrowest reviewed 1C evidence route that can answer what is actually reachable.
## Implementation Cut
Implemented locally:
- added `inventory_quality_events_for_organization` as a reviewed address recipe intent;
- added `inventory_quality_events_profile` over posted 1C documents:
- `Документ.СписаниеТоваров`;
- `Документ.ОприходованиеТоваров`;
- `Документ.ИнвентаризацияТоваровНаСкладе`;
- `Документ.ПереоценкаТоваровВРознице`;
- `business_overview` now runs this probe only for reserve/liquidation/write-off/obsolete-stock boundary wording;
- `derived_business_overview.inventory_quality_events` records:
- checked period;
- matched event rows;
- write-off count and amount;
- receipt-adjustment count and amount;
- stocktaking count;
- revaluation count;
- first/latest event date when events exist;
- evidence status: `reviewed_no_quality_events_found`, `reviewed_inventory_control_events_only`, or `reviewed_writeoff_or_adjustment_events_found`;
- `inventory_reserve_liquidation_quality` is removed from `missing_proof_families` only when this reviewed route executed;
- `inventory_reserve_boundary` route candidates can now promote to `ready_for_reviewed_execution`;
- user-facing direct answers include the organization scope, period, checked document families, and explicit no-overclaim boundary.
## Boundaries
Still not claimed:
- market liquidation value;
- management reserve amount or reserve policy;
- confirmed obsolete stock classification;
- complete warehouse health;
- FIFO liquidation or sell-through quality;
- accounting correctness of the source documents.
The route can honestly say "checked documents found / not found". It cannot turn absence of events into a legal or market conclusion that no obsolete stock exists.
## Validation
Local validation:
- `npm.cmd test -- assistantMcpDiscoveryAnswerAdapter.test.ts assistantMcpDiscoveryRuntimeBridge.test.ts assistantMcpDiscoveryResponseCandidate.test.ts` passed `84/84` with `1` skipped;
- `npm.cmd test -- assistantMcpDiscoveryPilotExecutor.test.ts` passed `34/34`;
- `npm.cmd run build` passed;
- direct live MCP query for `address_inventory_quality_events_for_organization_v1` against `ООО Альтернатива Плюс / 2020` returned `fetched_rows=0`, `matched_rows=0`, `error=null`, proving the new union query is syntactically valid and yields a checked negative in the current base.
Semantic/live replay:
- spec: `docs/orchestration/address_truth_harness_phase96_inventory_reserve_liquidation_quality.json`;
- first live run: `artifacts/domain_runs/phase96_inventory_reserve_liquidation_quality_live`, partial/fail because the answer did not repeat the explicit organization in the direct line;
- rerun after organization anchoring and compact-candidate cleanup: `artifacts/domain_runs/phase96_inventory_reserve_liquidation_quality_rerun`;
- final status: `accepted`, `2/2` passed, `0` warnings, `0` failures;
- accepted autorun: `AGENT | Phase 96 inventory reserve/liquidation quality-events` (`gen-ag05122057-c9786e`).
## Status
Current module wording:
`Route-Candidate-Driven Enablement Loop, active slice: final reviewed proof-family route accepted`
Progress: `100%`.
The loop has now promoted all phase92 proof-family candidates that were selected for this module:
- `accounting_profit_margin` accepted live by phase93;
- `debt_due_date_aging_quality` accepted live by phase94;
- `vendor_risk_procurement_quality` accepted live through procurement-concentration evidence by phase95;
- `inventory_reserve_liquidation_quality` accepted live through reviewed inventory quality-event documents by phase96.
This closes the declared Route-Candidate-Driven Enablement Loop slice. It does not close arbitrary 1C autonomy: the next work should move to the next broader module, using these proof-family routes as regression gates.
## Next Work
Next slices:
1. Reclassify Route-Candidate-Driven Enablement Loop as closed and use it as a regression gate.
2. Start the next open-world autonomy slice: broader schema/primitive discovery beyond the phase92 proof families.
3. Preserve phase93/94/95/96 accepted autoruns as canaries before expanding new unknown 1C domains.
See also:
- [26 - route_candidate_driven_enablement_loop_2026-05-10.md](./26%20-%20route_candidate_driven_enablement_loop_2026-05-10.md)
- [27 - proof_family_enablement_candidates_2026-05-10.md](./27%20-%20proof_family_enablement_candidates_2026-05-10.md)
- [28 - accounting_profit_margin_reviewed_route_2026-05-10.md](./28%20-%20accounting_profit_margin_reviewed_route_2026-05-10.md)
- [29 - debt_due_date_aging_reviewed_route_2026-05-10.md](./29%20-%20debt_due_date_aging_reviewed_route_2026-05-10.md)
- [30 - vendor_procurement_quality_reviewed_route_2026-05-12.md](./30%20-%20vendor_procurement_quality_reviewed_route_2026-05-12.md)

View File

@ -48,8 +48,9 @@ This package answers the next question:
28. [28 - accounting_profit_margin_reviewed_route_2026-05-10.md](./28%20-%20accounting_profit_margin_reviewed_route_2026-05-10.md)
29. [29 - debt_due_date_aging_reviewed_route_2026-05-10.md](./29%20-%20debt_due_date_aging_reviewed_route_2026-05-10.md)
30. [30 - vendor_procurement_quality_reviewed_route_2026-05-12.md](./30%20-%20vendor_procurement_quality_reviewed_route_2026-05-12.md)
31. [31 - inventory_reserve_liquidation_quality_reviewed_route_2026-05-12.md](./31%20-%20inventory_reserve_liquidation_quality_reviewed_route_2026-05-12.md)
## Current Status Snapshot (2026-05-10)
## Current Status Snapshot (2026-05-12)
This package is no longer planning-only.
@ -103,16 +104,19 @@ Status canon for planning:
- The accepted user-runnable autorun for that slice is `AGENT | Phase 94 debt due-date aging reviewed route` (`gen-ag05101319-c04f79`).
- The third proof-family route is now implemented and accepted: `vendor_risk_procurement_quality` moves from missing proof-family gap into reviewed procurement-concentration evidence when outgoing payment, bank-like recipient, non-financial recipient, counterparty-role, and contract-usage signals are reachable; `phase95_vendor_procurement_quality_reviewed_route_live2` passed `7/7`.
- The accepted user-runnable autorun for that slice is `AGENT | Phase 95 vendor/procurement quality reviewed route` (`gen-ag05121357-9ea5d6`).
- The fourth/final proof-family route is now implemented and accepted: `inventory_reserve_liquidation_quality` moves from missing proof-family gap into reviewed inventory quality-event evidence over posted write-off, receipt-adjustment, stocktaking, and revaluation documents; `phase96_inventory_reserve_liquidation_quality_rerun` passed `2/2`.
- The accepted user-runnable autorun for that slice is `AGENT | Phase 96 inventory reserve/liquidation quality-events` (`gen-ag05122057-c9786e`).
- The phase94 replay spec was repaired to real UTF-8 Russian before autorun persistence, so the saved user-runnable pack does not repeat the earlier GUI mojibake/card-text regression.
- The short source of truth for status wording is [21 - current_status_canon_2026-05-01.md](./21%20-%20current_status_canon_2026-05-01.md).
- The current execution spine after EHMO is [23 - current_execution_spine_and_semantic_control_gate_2026-05-05.md](./23%20-%20current_execution_spine_and_semantic_control_gate_2026-05-05.md).
- The current stage-loop/hygiene overlay after the AGENT dogfood cut is [24 - agentic_semantic_development_loop_and_autorun_hygiene_2026-05-10.md](./24%20-%20agentic_semantic_development_loop_and_autorun_hygiene_2026-05-10.md).
- The current route-candidate autonomy slice is [25 - open_world_route_candidate_promotion_2026-05-10.md](./25%20-%20open_world_route_candidate_promotion_2026-05-10.md).
- The current route-candidate enablement-loop slice is [26 - route_candidate_driven_enablement_loop_2026-05-10.md](./26%20-%20route_candidate_driven_enablement_loop_2026-05-10.md).
- The current proof-family enablement-candidate slice is [27 - proof_family_enablement_candidates_2026-05-10.md](./27%20-%20proof_family_enablement_candidates_2026-05-10.md).
- The current first reviewed proof-family route slice is [28 - accounting_profit_margin_reviewed_route_2026-05-10.md](./28%20-%20accounting_profit_margin_reviewed_route_2026-05-10.md).
- The current second reviewed proof-family route slice is [29 - debt_due_date_aging_reviewed_route_2026-05-10.md](./29%20-%20debt_due_date_aging_reviewed_route_2026-05-10.md).
- The current third reviewed proof-family route slice is [30 - vendor_procurement_quality_reviewed_route_2026-05-12.md](./30%20-%20vendor_procurement_quality_reviewed_route_2026-05-12.md).
- The closed route-candidate enablement-loop slice is [26 - route_candidate_driven_enablement_loop_2026-05-10.md](./26%20-%20route_candidate_driven_enablement_loop_2026-05-10.md), now used as a regression gate.
- The closed proof-family enablement-candidate slice is [27 - proof_family_enablement_candidates_2026-05-10.md](./27%20-%20proof_family_enablement_candidates_2026-05-10.md).
- The first reviewed proof-family route slice is [28 - accounting_profit_margin_reviewed_route_2026-05-10.md](./28%20-%20accounting_profit_margin_reviewed_route_2026-05-10.md).
- The second reviewed proof-family route slice is [29 - debt_due_date_aging_reviewed_route_2026-05-10.md](./29%20-%20debt_due_date_aging_reviewed_route_2026-05-10.md).
- The third reviewed proof-family route slice is [30 - vendor_procurement_quality_reviewed_route_2026-05-12.md](./30%20-%20vendor_procurement_quality_reviewed_route_2026-05-12.md).
- The fourth/final reviewed proof-family route slice is [31 - inventory_reserve_liquidation_quality_reviewed_route_2026-05-12.md](./31%20-%20inventory_reserve_liquidation_quality_reviewed_route_2026-05-12.md).
It now documents a turnaround that is already operational in code, already materially past the acute regression breakpoint, and already moved through bounded MCP autonomy, Post-F hardening, inventory breadth proof, and the declared Planner Autonomy slice:
@ -176,13 +180,13 @@ Current honest status:
- pre-multidomain readiness: `~90%`
- bounded-autonomy foundation readiness: `~89%`
- open-world bounded-autonomy readiness: `~87%`
- active Open-World Bounded Autonomy Breadth implementation breadth: `~99%`, with business-overview evidence fusion, the reviewed `business_overview` catalog/data-need/planner route-fabric slice, the fresh multi-probe runtime bridge, the explicit-period VAT/tax fact-family bridge, the explicit-period debt-position bridge, the explicit-date inventory-position bridge, the open-settlement quality bridge accepted by live semantic replay, selected-item profitability bridged by local semantic/runtime regression tests, contract-date debt age bridged locally, debt staleness-risk proxy bridged locally, debt due-date boundary arbitration bridged locally, inventory reserve/liquidation boundary arbitration bridged locally, supplier/procurement-quality boundary arbitration bridged locally, supplier concentration proxy bridged locally, document/account-section activity profile bridged locally, counterparty population/roles and contract usage profiles bridged locally, yearly operating-flow proxy bridged locally, earnings/best-year wording arbitration bridged locally, profit/margin wording boundary arbitration bridged locally, analyst synthesis added to business-overview answer drafting, company-period trading margin proxy bridged locally, inventory sales-to-stock proxy bridged locally, inventory staleness-risk proxy bridged locally, gap-specific answer shaping bridged locally, missing proof families recorded as runtime evidence ledger, exact accounting profit/margin promoted into a reviewed 90/91/99 route by phase93, debt due-date aging promoted into a reviewed payment-term/open-balance route by phase94, and vendor/procurement quality promoted into reviewed procurement-concentration evidence by phase95; confirmed reserve/write-off/liquidation inventory evidence is still pending
- active Open-World Bounded Autonomy Breadth implementation breadth: `~99%`, with business-overview evidence fusion, the reviewed `business_overview` catalog/data-need/planner route-fabric slice, the fresh multi-probe runtime bridge, the explicit-period VAT/tax fact-family bridge, the explicit-period debt-position bridge, the explicit-date inventory-position bridge, the open-settlement quality bridge accepted by live semantic replay, selected-item profitability bridged by local semantic/runtime regression tests, contract-date debt age bridged locally, debt staleness-risk proxy bridged locally, debt due-date boundary arbitration bridged locally, inventory reserve/liquidation boundary arbitration bridged locally, supplier/procurement-quality boundary arbitration bridged locally, supplier concentration proxy bridged locally, document/account-section activity profile bridged locally, counterparty population/roles and contract usage profiles bridged locally, yearly operating-flow proxy bridged locally, earnings/best-year wording arbitration bridged locally, profit/margin wording boundary arbitration bridged locally, analyst synthesis added to business-overview answer drafting, company-period trading margin proxy bridged locally, inventory sales-to-stock proxy bridged locally, inventory staleness-risk proxy bridged locally, gap-specific answer shaping bridged locally, missing proof families recorded as runtime evidence ledger, exact accounting profit/margin promoted into a reviewed 90/91/99 route by phase93, debt due-date aging promoted into a reviewed payment-term/open-balance route by phase94, vendor/procurement quality promoted into reviewed procurement-concentration evidence by phase95, and inventory reserve/write-off/liquidation quality promoted into reviewed inventory quality-event evidence by phase96
- active Open-World Bounded Autonomy Breadth accepted-module progress: `~99%`, because the EHMO-derived `Open-World Semantic Control Gate` critical subset accepts live at `21/21` after W5/W7 hardening; full closure is still held back for the fat manual GUI pack and remaining answer-shape residual review
- Post-F semantic integrity module progress: `~99%` operationally closed, with remaining risk now treated as next-slice discovery rather than an open blocker inside the closed slice
- active inventory-stock breadth slice progress: `100%` for the declared scenario pack, not for arbitrary inventory questions
- Planner Autonomy Consolidation progress: `100%` for the declared module, with catalog-fabric, value-flow arbitration, lifecycle bounded inference, broad-evaluation bridge, inventory catalog templates, inventory runtime-boundary honesty, exact inventory recipe bridging, unambiguous metadata-surface lane inference, catalog chain-template scoring, structured chain-match contract exposure, runtime/debug propagation, subject-aware bidirectional comparison arbitration, structured catalog-alignment verdicts, representative alignment regression guard, catalog-alignment reason-code telemetry, explicit `alignment_status` propagation, truth-harness/acceptance-matrix surfacing, soft divergence warning, `catalog_alignment_ok` acceptance invariant, step-level expected catalog-alignment assertions, phase66 and phase32 spec alignment expectations, AGENT source-catalog surfacing, generated phase83 mixed planner-brain replay spec, checked-source user-facing error sanitation, surface-grounded catalog promotion, and guarded live phase83 acceptance validated. Broader unfamiliar 1C asks are now next-module breadth work rather than an open blocker inside this declared slice
- Open-World Route Candidate Promotion progress: `100%` for the declared phase90 slice, with structured `route_candidate` runtime contract, artifact propagation, live semantic replay accepted at `5/5`, and accepted AGENT autorun persistence; broader autonomous route enablement remains the next active slice
- Route-Candidate-Driven Enablement Loop progress: `92%`, with deterministic repair-target grouping, Lead Codex handoff surfacing, local tooling tests, live phase91 canary acceptance, phase92 proof-family candidates accepted/saved as a user-runnable AGENT autorun, `accounting_profit_margin` promoted into reviewed 90/91/99 execution by phase93 live replay, `debt_due_date_aging_quality` promoted into reviewed payment-term/open-balance execution by phase94 live replay, and `vendor_risk_procurement_quality` promoted into reviewed procurement-concentration evidence by phase95 live replay; the remaining inventory reserve/liquidation proof family is still pending
- Route-Candidate-Driven Enablement Loop progress: `100%`, with deterministic repair-target grouping, Lead Codex handoff surfacing, local tooling tests, live phase91 canary acceptance, phase92 proof-family candidates accepted/saved as a user-runnable AGENT autorun, `accounting_profit_margin` promoted into reviewed 90/91/99 execution by phase93 live replay, `debt_due_date_aging_quality` promoted into reviewed payment-term/open-balance execution by phase94 live replay, `vendor_risk_procurement_quality` promoted into reviewed procurement-concentration evidence by phase95 live replay, and `inventory_reserve_liquidation_quality` promoted into reviewed inventory quality-event evidence by phase96 live replay; the declared route-candidate-driven enablement loop is now closed and should be used as a regression gate for the next broader autonomy slice
- graph snapshot after latest rebuild: see `graphify-out/GRAPH_REPORT.md`
- current regression-gate breakpoint:
- the validated hot paths are no longer structurally broken;
@ -272,6 +276,7 @@ Latest live proof now includes:
- accounting profit/margin reviewed route accepted locally/live: targeted runtime/answer/turn-input/candidate/intent tests passed `194/194` with `8` skipped; targeted VAT tax-period regression passed; `address_truth_harness_phase93_accounting_profit_margin_reviewed_route_live3_20260510` accepted `6/6`, proving 90/91/99 accounting result, short profit/loss follow-up continuity, VAT continuity, value-flow canary, and inventory reserve boundary canary together; the accepted autorun is `AGENT | Phase 93 accounting profit-margin reviewed route` (`gen-ag05101213-596d99`).
- debt due-date aging reviewed route accepted locally/live: transition policy passed `38/38`, turn-input adapter passed `103/103` with `7` skipped, executor/answer/candidate/runtime bridge passed `113/113` with `1` skipped, build passed; `phase94_debt_due_date_aging_reviewed_route_live4` accepted `7/7`, proving payment-term/open-balance checked-negative overdue answers, short due-date boundary follow-up continuity, profit/margin/VAT/value-flow canaries, and reserve/vendor boundary safety together; the accepted autorun is `AGENT | Phase 94 debt due-date aging reviewed route` (`gen-ag05101319-c04f79`).
- vendor/procurement quality reviewed route accepted locally/live: executor/runtime bridge/answer/candidate tests passed `118/118` with `1` skipped, build passed; `phase95_vendor_procurement_quality_reviewed_route_live2` accepted `7/7`; `vendor_risk_procurement_quality` now derives reviewed procurement-concentration evidence from confirmed outgoing payment rows, separates bank-like outgoing leaders from ordinary supplier dependency, removes the proof family from `missing_proof_families` when this reviewed evidence exists, and can promote `vendor_risk_procurement_boundary` route candidates to `ready_for_reviewed_execution`; the accepted autorun is `AGENT | Phase 95 vendor/procurement quality reviewed route` (`gen-ag05121357-9ea5d6`).
- inventory reserve/liquidation quality reviewed route accepted locally/live: answer/runtime/candidate tests passed `84/84` with `1` skipped, pilot-executor tests passed `34/34`, build passed; direct MCP query for `address_inventory_quality_events_for_organization_v1` returned `fetched_rows=0`, `matched_rows=0`, `error=null`; `phase96_inventory_reserve_liquidation_quality_rerun` accepted `2/2`; `inventory_reserve_liquidation_quality` now derives reviewed evidence from posted write-off, receipt-adjustment, stocktaking, and revaluation documents, removes the proof family from `missing_proof_families` when this reviewed route executes, anchors the organization in the direct answer, and can promote `inventory_reserve_boundary` route candidates to `ready_for_reviewed_execution`; the accepted autorun is `AGENT | Phase 96 inventory reserve/liquidation quality-events` (`gen-ag05122057-c9786e`).
Current architectural reading:
@ -351,6 +356,7 @@ Read in this order:
29. `28 - accounting_profit_margin_reviewed_route_2026-05-10.md`
30. `29 - debt_due_date_aging_reviewed_route_2026-05-10.md`
31. `30 - vendor_procurement_quality_reviewed_route_2026-05-12.md`
32. `31 - inventory_reserve_liquidation_quality_reviewed_route_2026-05-12.md`
## Planning Rules

View File

@ -0,0 +1,71 @@
{
"schema_version": "domain_truth_harness_spec_v1",
"scenario_id": "address_truth_harness_phase96_inventory_reserve_liquidation_quality",
"domain": "address_phase96_inventory_reserve_liquidation_quality",
"title": "Phase 96 inventory reserve/liquidation quality-events replay",
"description": "Targeted replay for the reviewed inventory quality-events route: direct reserve/write-off/liquidation questions should trigger checked 1C document evidence for write-offs, receipt adjustments, stocktaking, and retail revaluation, while still refusing to invent market liquidation value or management reserve policy.",
"bindings": {},
"steps": [
{
"step_id": "step_01_direct_inventory_reserve_liquidation_boundary",
"title": "Direct reserve/liquidation boundary uses reviewed inventory quality events",
"question": "По ООО Альтернатива Плюс за 2020 год проверь склад: были ли списания товаров, оприходования/корректировки, инвентаризации, переоценки, резервы под неликвиды или ликвидационная стоимость? Скажи коротко и честно, что подтверждено 1С, а что нельзя утверждать.",
"expected_catalog_alignment_status": "selected_matches_top",
"expected_catalog_chain_top_match": "business_overview",
"expected_catalog_selected_matches_top": true,
"allowed_reply_types": [
"partial_coverage",
"factual_with_explanation"
],
"required_answer_patterns_all": [
"(?i)альтернатива|организац|компани",
"(?i)2020|2020 год",
"(?i)склад|товар",
"(?i)списан|оприход|инвентаризац|переоцен",
"(?i)провер|подтвержд|1с",
"(?i)не рыночн|не ликвидационн|не управленческ|не резерв"
],
"forbidden_answer_patterns": [
"(?i)business_overview_route_template_v1|inventory_quality_events_profile",
"(?i)query_movements|query_documents|primitive|planner_|runtime_|pilot_",
"(?i)ликвидационная стоимость составляет|резерв составляет|подтвержденный резерв под неликвид"
],
"criticality": "critical",
"semantic_tags": [
"inventory_reserve_liquidation_quality",
"reviewed_inventory_quality_events",
"direct_answer_first",
"no_market_liquidation_overclaim"
]
},
{
"step_id": "step_02_followup_inventory_liquidity_boundary",
"title": "Follow-up does not turn checked quality events into full liquidity claim",
"question": "А по этим же данным можно сказать, что склад ликвидный и неликвидов нет?",
"expected_catalog_alignment_status": "selected_matches_top",
"expected_catalog_chain_top_match": "business_overview",
"expected_catalog_selected_matches_top": true,
"allowed_reply_types": [
"partial_coverage",
"factual_with_explanation"
],
"required_answer_patterns_all": [
"(?i)склад|товар|остат",
"(?i)ликвид|неликвид",
"(?i)не подтвержд|нельзя|не значит|отдельн",
"(?i)списан|инвентаризац|переоцен|quality|событ"
],
"forbidden_answer_patterns": [
"(?i)business_overview_route_template_v1|inventory_quality_events_profile",
"(?i)query_movements|query_documents|primitive|planner_|runtime_|pilot_",
"(?i)склад ликвидный$|неликвидов нет$|ликвидность подтверждена"
],
"criticality": "critical",
"semantic_tags": [
"inventory_liquidity_boundary",
"followup_context_carryover",
"no_inventory_health_overclaim"
]
}
]
}

View File

@ -283,6 +283,61 @@ const INVENTORY_ON_HAND_AS_OF_QUERY_TEMPLATE = `
УПОРЯДОЧИТЬ ПО
Количество __ORDER_DIRECTION__
`;
const INVENTORY_QUALITY_EVENTS_QUERY_TEMPLATE = `
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
Списание.Дата КАК Период,
ПРЕДСТАВЛЕНИЕ(Списание.Ссылка) КАК Регистратор,
"Списание товаров" КАК ТипСобытия,
ПРЕДСТАВЛЕНИЕ(Списание.Организация) КАК Организация,
ПРЕДСТАВЛЕНИЕ(Списание.Склад) КАК Склад,
Списание.СуммаДокумента КАК Сумма,
Списание.Основание КАК Основание,
Списание.Комментарий КАК Комментарий
ИЗ
Документ.СписаниеТоваров КАК Списание
__WHERE_WRITE_OFF__
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
Оприходование.Дата КАК Период,
ПРЕДСТАВЛЕНИЕ(Оприходование.Ссылка) КАК Регистратор,
"Оприходование товаров" КАК ТипСобытия,
ПРЕДСТАВЛЕНИЕ(Оприходование.Организация) КАК Организация,
ПРЕДСТАВЛЕНИЕ(Оприходование.Склад) КАК Склад,
Оприходование.СуммаДокумента КАК Сумма,
Оприходование.Основание КАК Основание,
Оприходование.Комментарий КАК Комментарий
ИЗ
Документ.ОприходованиеТоваров КАК Оприходование
__WHERE_RECEIPT__
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
Инвентаризация.Дата КАК Период,
ПРЕДСТАВЛЕНИЕ(Инвентаризация.Ссылка) КАК Регистратор,
"Инвентаризация товаров на складе" КАК ТипСобытия,
ПРЕДСТАВЛЕНИЕ(Инвентаризация.Организация) КАК Организация,
ПРЕДСТАВЛЕНИЕ(Инвентаризация.Склад) КАК Склад,
0 КАК Сумма,
Инвентаризация.ПричинаПроведенияИнвентаризации КАК Основание,
Инвентаризация.Комментарий КАК Комментарий
ИЗ
Документ.ИнвентаризацияТоваровНаСкладе КАК Инвентаризация
__WHERE_INVENTORY_COUNT__
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
Переоценка.Дата КАК Период,
ПРЕДСТАВЛЕНИЕ(Переоценка.Ссылка) КАК Регистратор,
"Переоценка товаров в рознице" КАК ТипСобытия,
ПРЕДСТАВЛЕНИЕ(Переоценка.Организация) КАК Организация,
ПРЕДСТАВЛЕНИЕ(Переоценка.Склад) КАК Склад,
0 КАК Сумма,
"" КАК Основание,
Переоценка.Комментарий КАК Комментарий
ИЗ
Документ.ПереоценкаТоваровВРознице КАК Переоценка
__WHERE_REVALUATION__
УПОРЯДОЧИТЬ ПО
Период __ORDER_DIRECTION__
`;
const BANK_DOCS_QUERY_TEMPLATE = `
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
БанкСписание.Дата КАК Период,
@ -933,6 +988,16 @@ const BASE_RECIPES = [
account_scope_mode: "strict",
query_template: "inventory_aging_by_purchase_date_profile"
},
{
recipe_id: "address_inventory_quality_events_for_organization_v1",
intent: "inventory_quality_events_for_organization",
purpose: "Check posted inventory quality event documents: write-offs, stocktaking, receipt adjustments, and retail revaluation",
required_filters: [],
optional_filters: ["as_of_date", "period_from", "period_to", "organization", "warehouse", "limit", "sort"],
default_limit: 400,
account_scope_mode: "preferred",
query_template: "inventory_quality_events_profile"
},
{
recipe_id: "address_open_contracts_confirmed_as_of_date_v1",
intent: "open_contracts_confirmed_as_of_date",
@ -1326,6 +1391,48 @@ function buildInventoryMovementQuery(filters, resolvedLimit, side) {
.replace("__WHERE_CLAUSE__", buildWhereClause(filters, "Движения.Период", [inventoryCondition, itemCondition].filter((item) => Boolean(item))))
.replaceAll("__ORDER_DIRECTION__", resolveOrderDirection(filters.sort));
}
function buildWarehouseReferenceCondition(filters, fieldPaths) {
const warehouse = typeof filters.warehouse === "string" ? filters.warehouse.trim() : "";
if (!warehouse) {
return null;
}
const tokens = Array.from(new Set(warehouse
.split(/[^A-Za-zА-Яа-яЁё0-9]+/u)
.map((token) => token.trim())
.filter((token) => token.length >= 3)
.filter((token) => !["склад", "warehouse"].includes(token.toLowerCase()))));
const effectiveTokens = tokens.length > 0 ? tokens : [warehouse];
const clauses = fieldPaths
.map((fieldPath) => String(fieldPath ?? "").trim())
.filter((fieldPath) => fieldPath.length > 0)
.map((fieldPath) => {
const tokenConditions = effectiveTokens.map((token) => {
const escapedToken = toQueryStringLiteral(token);
return `${fieldPath}.Наименование ПОДОБНО "%${escapedToken}%"`;
});
return tokenConditions.length === 1 ? tokenConditions[0] : `(${tokenConditions.join(" И ")})`;
});
if (clauses.length === 0) {
return null;
}
return clauses.length === 1 ? clauses[0] : `(${clauses.join(" ИЛИ ")})`;
}
function buildInventoryQualityDocumentWhereClause(filters, dateFieldPath, organizationFieldPath, warehouseFieldPath) {
return buildWhereClause(filters, dateFieldPath, [
`${dateFieldPath.replace(/\.Дата$/u, ".Проведен")} = ИСТИНА`,
buildOrganizationReferenceCondition(filters, [organizationFieldPath]),
buildWarehouseReferenceCondition(filters, [warehouseFieldPath])
].filter((item) => Boolean(item)));
}
function buildInventoryQualityEventsQuery(filters, resolvedLimit) {
return INVENTORY_QUALITY_EVENTS_QUERY_TEMPLATE
.replaceAll("__LIMIT__", String(resolvedLimit))
.replace("__WHERE_WRITE_OFF__", buildInventoryQualityDocumentWhereClause(filters, "Списание.Дата", "Списание.Организация", "Списание.Склад"))
.replace("__WHERE_RECEIPT__", buildInventoryQualityDocumentWhereClause(filters, "Оприходование.Дата", "Оприходование.Организация", "Оприходование.Склад"))
.replace("__WHERE_INVENTORY_COUNT__", buildInventoryQualityDocumentWhereClause(filters, "Инвентаризация.Дата", "Инвентаризация.Организация", "Инвентаризация.Склад"))
.replace("__WHERE_REVALUATION__", buildInventoryQualityDocumentWhereClause(filters, "Переоценка.Дата", "Переоценка.Организация", "Переоценка.Склад"))
.replaceAll("__ORDER_DIRECTION__", resolveOrderDirection(filters.sort));
}
function buildInventoryItemReferenceCondition(filters, fieldPaths) {
const item = typeof filters.item === "string" ? filters.item.trim() : "";
if (!item) {
@ -1599,6 +1706,7 @@ function maxLimitForIntent(intent) {
intent === "inventory_profitability_for_item" ||
intent === "inventory_purchase_to_sale_chain" ||
intent === "inventory_aging_by_purchase_date" ||
intent === "inventory_quality_events_for_organization" ||
intent === "open_contracts_confirmed_as_of_date" ||
intent === "list_contracts_by_counterparty" ||
intent === "list_documents_by_counterparty" ||
@ -1790,27 +1898,11 @@ function buildAddressRecipePlan(recipe, filters) {
? buildInventoryPurchaseToSaleDocumentQuery(filters, resolvedLimit)
: recipe.query_template === "inventory_aging_by_purchase_date_profile"
? buildInventoryMovementQuery(filters, resolvedLimit, "dt")
: recipe.query_template === "contracts_by_counterparty_profile"
? CONTRACTS_BY_COUNTERPARTY_QUERY_TEMPLATE.replaceAll("__LIMIT__", String(resolvedLimit))
: recipe.query_template === "open_contracts_confirmed_as_of_balance_profile"
? (() => {
const asOfExpr = (typeof filters.as_of_date === "string" && filters.as_of_date.trim().length > 0
? toDateTimeExpr(filters.as_of_date, true)
: null) ??
(typeof filters.period_to === "string" && filters.period_to.trim().length > 0
? toDateTimeExpr(filters.period_to, true)
: null) ??
(typeof filters.period_from === "string" && filters.period_from.trim().length > 0
? toDateTimeExpr(filters.period_from, true)
: null) ??
"ТЕКУЩАЯДАТА()";
return OPEN_CONTRACTS_CONFIRMED_AS_OF_QUERY_TEMPLATE
.replaceAll("__LIMIT__", String(resolvedLimit))
.replaceAll("__AS_OF_EXPR__", asOfExpr)
.replaceAll("__OPEN_CONTRACT_ACCOUNTS_MATCH__", buildAccountPrefixPredicate("Остатки.Счет", ["60", "62", "76"]))
.replaceAll("__ORDER_DIRECTION__", resolveOrderDirection(filters.sort));
})()
: recipe.query_template === "payables_confirmed_as_of_balance_profile"
: recipe.query_template === "inventory_quality_events_profile"
? buildInventoryQualityEventsQuery(filters, resolvedLimit)
: recipe.query_template === "contracts_by_counterparty_profile"
? CONTRACTS_BY_COUNTERPARTY_QUERY_TEMPLATE.replaceAll("__LIMIT__", String(resolvedLimit))
: recipe.query_template === "open_contracts_confirmed_as_of_balance_profile"
? (() => {
const asOfExpr = (typeof filters.as_of_date === "string" && filters.as_of_date.trim().length > 0
? toDateTimeExpr(filters.as_of_date, true)
@ -1825,10 +1917,10 @@ function buildAddressRecipePlan(recipe, filters) {
return OPEN_CONTRACTS_CONFIRMED_AS_OF_QUERY_TEMPLATE
.replaceAll("__LIMIT__", String(resolvedLimit))
.replaceAll("__AS_OF_EXPR__", asOfExpr)
.replaceAll("__OPEN_CONTRACT_ACCOUNTS_MATCH__", buildAccountPrefixPredicate("Остатки.Счет", ["60", "76"]))
.replaceAll("__OPEN_CONTRACT_ACCOUNTS_MATCH__", buildAccountPrefixPredicate("Остатки.Счет", ["60", "62", "76"]))
.replaceAll("__ORDER_DIRECTION__", resolveOrderDirection(filters.sort));
})()
: recipe.query_template === "receivables_confirmed_as_of_balance_profile"
: recipe.query_template === "payables_confirmed_as_of_balance_profile"
? (() => {
const asOfExpr = (typeof filters.as_of_date === "string" && filters.as_of_date.trim().length > 0
? toDateTimeExpr(filters.as_of_date, true)
@ -1843,20 +1935,38 @@ function buildAddressRecipePlan(recipe, filters) {
return OPEN_CONTRACTS_CONFIRMED_AS_OF_QUERY_TEMPLATE
.replaceAll("__LIMIT__", String(resolvedLimit))
.replaceAll("__AS_OF_EXPR__", asOfExpr)
.replaceAll("__OPEN_CONTRACT_ACCOUNTS_MATCH__", buildAccountPrefixPredicate("Остатки.Счет", ["62", "76"]))
.replaceAll("__OPEN_CONTRACT_ACCOUNTS_MATCH__", buildAccountPrefixPredicate("Остатки.Счет", ["60", "76"]))
.replaceAll("__ORDER_DIRECTION__", resolveOrderDirection(filters.sort));
})()
: MOVEMENTS_QUERY_TEMPLATE
.replace("__LIMIT__", String(resolvedLimit))
.replace("__WHERE_CLAUSE__", (() => {
const extraConditions = [];
const accountCondition = buildMovementAccountCondition(filters);
if (accountCondition) {
extraConditions.push(accountCondition);
}
return buildWhereClause(filters, "Движения.Период", extraConditions);
})())
.replaceAll("__ORDER_DIRECTION__", resolveOrderDirection(filters.sort));
: recipe.query_template === "receivables_confirmed_as_of_balance_profile"
? (() => {
const asOfExpr = (typeof filters.as_of_date === "string" && filters.as_of_date.trim().length > 0
? toDateTimeExpr(filters.as_of_date, true)
: null) ??
(typeof filters.period_to === "string" && filters.period_to.trim().length > 0
? toDateTimeExpr(filters.period_to, true)
: null) ??
(typeof filters.period_from === "string" && filters.period_from.trim().length > 0
? toDateTimeExpr(filters.period_from, true)
: null) ??
"ТЕКУЩАЯДАТА()";
return OPEN_CONTRACTS_CONFIRMED_AS_OF_QUERY_TEMPLATE
.replaceAll("__LIMIT__", String(resolvedLimit))
.replaceAll("__AS_OF_EXPR__", asOfExpr)
.replaceAll("__OPEN_CONTRACT_ACCOUNTS_MATCH__", buildAccountPrefixPredicate("Остатки.Счет", ["62", "76"]))
.replaceAll("__ORDER_DIRECTION__", resolveOrderDirection(filters.sort));
})()
: MOVEMENTS_QUERY_TEMPLATE
.replace("__LIMIT__", String(resolvedLimit))
.replace("__WHERE_CLAUSE__", (() => {
const extraConditions = [];
const accountCondition = buildMovementAccountCondition(filters);
if (accountCondition) {
extraConditions.push(accountCondition);
}
return buildWhereClause(filters, "Движения.Период", extraConditions);
})())
.replaceAll("__ORDER_DIRECTION__", resolveOrderDirection(filters.sort));
return {
recipe,
query,

View File

@ -393,6 +393,9 @@ function isVendorRiskBoundaryTurn(pilot) {
return action === "vendor_risk_procurement_boundary" || unsupported === "vendor_risk_procurement_boundary";
}
function businessOverviewInventoryUnknownLabel(overview) {
if (overview.inventory_quality_events) {
return "рыночная ликвидационная стоимость и управленческий резерв склада";
}
if (overview.inventory_staleness_risk_proxy) {
return "резервы/списания/ликвидационная стоимость склада";
}
@ -621,6 +624,24 @@ function businessOverviewVendorProcurementQualityText(overview) {
}
return `Procurement-concentration route за ${period} отработал по исходящим платежам на ${total}, но надежной небанковской концентрации поставщика по найденным строкам не хватает.${contractText} Полный vendor-risk аудит не подтвержден.`;
}
function businessOverviewInventoryQualityEventsText(overview) {
const quality = overview.inventory_quality_events;
if (!quality) {
return null;
}
const period = quality.period_scope ?? "проверенное окно";
const organization = overview.organization_scope ? ` по организации ${overview.organization_scope}` : "";
const eventWindow = quality.first_event_date && quality.latest_event_date
? ` Окно найденных событий: ${quality.first_event_date} - ${quality.latest_event_date}.`
: "";
if (quality.evidence_status === "reviewed_no_quality_events_found") {
return `Коротко: проверил складские документы списания, оприходования, инвентаризации и переоценки${organization} за ${period}; подтвержденных событий списания/корректировки/инвентаризации/переоценки не найдено. Это сильный отрицательный сигнал по доступным документам 1С, но не рыночная ликвидационная стоимость и не управленческий резерв под неликвиды.`;
}
if (quality.evidence_status === "reviewed_inventory_control_events_only") {
return `Коротко: проверил складские quality-события${organization} за ${period}; списаний и оприходований/корректировок с суммой не найдено, но есть инвентаризации ${quality.inventory_count_rows} и переоценки ${quality.revaluation_rows}.${eventWindow} Это контрольные складские документы, а не подтвержденный резерв или рыночная ликвидационная оценка.`;
}
return `Коротко: проверил складские quality-события${organization} за ${period}; списаний ${quality.writeoff_rows} на ${quality.writeoff_amount_human_ru}, оприходований/корректировок ${quality.receipt_adjustment_rows} на ${quality.receipt_adjustment_amount_human_ru}, инвентаризаций ${quality.inventory_count_rows}, переоценок ${quality.revaluation_rows}.${eventWindow} Это подтвержденные документы 1С по складским событиям, но не самостоятельная рыночная ликвидационная стоимость и не расчет управленческого резерва.`;
}
function headlineFor(mode, pilot) {
const askedMonthlyBreakdown = pilot.derived_bidirectional_value_flow?.aggregation_axis === "month" ||
pilot.derived_value_flow?.aggregation_axis === "month";
@ -653,6 +674,10 @@ function headlineFor(mode, pilot) {
return "Нельзя точно определить, какая дебиторка просрочена, по текущему срезу 1С; есть только debt-quality proxy, но нет проверенного due-date маршрута по договорам, срокам оплаты и погашению расчетов.";
}
if (isInventoryReserveBoundaryTurn(pilot)) {
const inventoryQualityEventsText = businessOverviewInventoryQualityEventsText(overview);
if (inventoryQualityEventsText) {
return inventoryQualityEventsText;
}
const inventoryBasis = overview.inventory_staleness_risk_proxy
? "есть только складской staleness-risk proxy по найденным строкам"
: overview.inventory_position || overview.inventory_turnover_proxy
@ -724,6 +749,9 @@ function headlineFor(mode, pilot) {
if (overview.inventory_staleness_risk_proxy) {
families.push("staleness risk proxy склада");
}
if (overview.inventory_quality_events) {
families.push("складские quality-события");
}
const unknownFamilies = overview.accounting_financial_result
? ["аудированная/юридически подтвержденная прибыль"]
: [overview.trading_margin_proxy ? "чистая прибыль/точная маржа" : "прибыль/маржа"];
@ -946,6 +974,9 @@ function buildMustNotClaim(pilot) {
claims.push("Do not present an inventory snapshot or purchase-date aging signal as turnover, obsolescence, liquidation value, or full inventory health.");
claims.push("Do not present business overview inventory turnover proxy as full inventory liquidity, FIFO turnover, obsolescence analysis, or liquidation value.");
claims.push("Do not present business overview inventory staleness risk proxy as confirmed obsolete stock, reserve, write-off, or liquidation value.");
if (pilot.derived_business_overview?.inventory_quality_events) {
claims.push("Do not present reviewed inventory quality events as confirmed obsolete stock, reserve policy, market liquidation value, management reserve, or full inventory health.");
}
if (pilot.derived_business_overview?.top_customers?.some(isFinancialInstitutionBucket) ||
pilot.derived_business_overview?.top_suppliers?.some(isFinancialInstitutionBucket)) {
claims.push("Do not present bank-like counterparties as ordinary customers, suppliers, revenue, procurement dependency, or business quality evidence without payment-purpose/contract proof.");
@ -1436,6 +1467,10 @@ function derivedBusinessOverviewConfirmedLines(pilot) {
const proxy = overview.inventory_staleness_risk_proxy;
lines.push(`Staleness risk proxy склада на ${proxy.as_of_date}: самая ранняя дата закупочного сигнала ${proxy.oldest_purchase_date}, возраст ${proxy.max_purchase_age_days} дн., sales-to-stock ${proxy.sales_to_stock_amount_ratio}x, оценка ${inventoryStalenessRiskBandRu(proxy.risk_band)}. Это не подтвержденная неликвидность, не резерв и не ликвидационная стоимость.`);
}
const inventoryQualityEventsText = businessOverviewInventoryQualityEventsText(overview);
if (inventoryQualityEventsText) {
lines.push(inventoryQualityEventsText.replace(/^Коротко:\s*/u, ""));
}
return lines;
}
function businessOverviewCashSynthesisLine(overview) {
@ -1603,6 +1638,15 @@ function businessOverviewRiskSynthesisLine(overview) {
if (overview.inventory_staleness_risk_proxy) {
signals.push(`staleness risk proxy склада: ${inventoryStalenessRiskBandRu(overview.inventory_staleness_risk_proxy.risk_band)}, возраст ${overview.inventory_staleness_risk_proxy.max_purchase_age_days} дн.`);
}
if (overview.inventory_quality_events) {
const quality = overview.inventory_quality_events;
if (quality.evidence_status === "reviewed_no_quality_events_found") {
signals.push("складские quality-события: документы списания, оприходования, инвентаризации и переоценки проверены, подтвержденных событий не найдено");
}
else {
signals.push(`складские quality-события: списаний ${quality.writeoff_rows} на ${quality.writeoff_amount_human_ru}, оприходований/корректировок ${quality.receipt_adjustment_rows} на ${quality.receipt_adjustment_amount_human_ru}, инвентаризаций ${quality.inventory_count_rows}, переоценок ${quality.revaluation_rows}`);
}
}
return signals.length > 0
? `Риски и контуры внимания по подтвержденным данным: ${signals.join("; ")}.`
: null;
@ -1616,7 +1660,8 @@ function businessOverviewExecutiveVerdictLine(overview) {
overview.debt_staleness_risk_proxy ||
overview.inventory_position ||
overview.inventory_turnover_proxy ||
overview.inventory_staleness_risk_proxy);
overview.inventory_staleness_risk_proxy ||
overview.inventory_quality_events);
const hasOperationalProfileSignal = Boolean(overview.document_activity_profile || overview.counterparty_profile || overview.contract_usage_profile);
const hasExtraSignals = hasTaxDebtInventorySignals || hasOperationalProfileSignal;
if (!hasCash && !hasExtraSignals) {
@ -1750,6 +1795,10 @@ function buildAssistantMcpDiscoveryAnswerDraft(pilot) {
if (pilot.derived_business_overview?.inventory_staleness_risk_proxy) {
pushReason(reasonCodes, "answer_contains_business_overview_inventory_staleness_risk_proxy");
}
if (pilot.derived_business_overview?.inventory_quality_events) {
pushReason(reasonCodes, "answer_contains_business_overview_inventory_quality_events");
pushReason(reasonCodes, `answer_contains_business_overview_inventory_quality_events_${pilot.derived_business_overview.inventory_quality_events.evidence_status}`);
}
if (pilot.derived_business_overview?.missing_proof_families?.length) {
pushReason(reasonCodes, "answer_contains_business_overview_missing_proof_ledger");
}

View File

@ -211,6 +211,16 @@ function shouldRunDebtDueDateAgingProbe(planner) {
.join(" ");
return /(?:debt_due_date_boundary|due[-_ ]?date|overdue|aging|просроч|срок\s+оплат|дебиторк|кредиторск)/iu.test(combined);
}
function shouldRunInventoryQualityEventsProbe(planner) {
const actionFamily = toNonEmptyString(planner.data_need_graph?.action_family);
const turnActionFamily = toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.asked_action_family);
const unsupportedFamily = toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.unsupported_but_understood_family);
const proofExpectation = toNonEmptyString(planner.data_need_graph?.proof_expectation);
const combined = [actionFamily, turnActionFamily, unsupportedFamily, proofExpectation]
.filter((item) => Boolean(item))
.join(" ");
return /(?:inventory_reserve|reserve_liquidation|liquidation|write[-_ ]?off|obsolete|obsolescence|inventory_reserve_liquidation_quality|резерв|списан|ликвидац|неликвид|обесцен)/iu.test(combined);
}
function buildBusinessOverviewInventoryFilters(planner) {
const meaning = planner.discovery_plan.turn_meaning_ref;
const organization = toNonEmptyString(meaning?.explicit_organization_scope);
@ -3021,6 +3031,69 @@ function deriveBusinessOverviewInventoryStalenessRiskProxy(input) {
inference_basis: "purchase_date_age_and_sales_to_stock_proxy_confirmed_1c_rows"
};
}
function rowInventoryQualityEventType(row) {
return rowTextValue(row, ["ТипСобытия", "EventType", "event_type", "Регистратор", "Registrator", "registrator"]) ?? "";
}
function deriveBusinessOverviewInventoryQualityEvents(input) {
const result = input.inventoryQualityEventsResult;
if (!result || result.error) {
return null;
}
let writeoffRows = 0;
let writeoffAmount = 0;
let receiptAdjustmentRows = 0;
let receiptAdjustmentAmount = 0;
let inventoryCountRows = 0;
let revaluationRows = 0;
const eventDates = [];
for (const row of result.rows) {
const eventType = rowInventoryQualityEventType(row);
const amount = rowAmountValue(row) ?? 0;
const date = rowDateValue(row);
if (date) {
eventDates.push(date);
}
if (/списан|write[-_ ]?off/iu.test(eventType)) {
writeoffRows += 1;
writeoffAmount += amount;
continue;
}
if (/оприход|receipt|positive/i.test(eventType)) {
receiptAdjustmentRows += 1;
receiptAdjustmentAmount += amount;
continue;
}
if (/инвентаризац|stocktaking|inventory count/i.test(eventType)) {
inventoryCountRows += 1;
continue;
}
if (/переоцен|revaluation/i.test(eventType)) {
revaluationRows += 1;
}
}
const sortedDates = eventDates.sort((left, right) => left.localeCompare(right));
const evidenceStatus = writeoffRows > 0 || receiptAdjustmentRows > 0
? "reviewed_writeoff_or_adjustment_events_found"
: inventoryCountRows > 0 || revaluationRows > 0
? "reviewed_inventory_control_events_only"
: "reviewed_no_quality_events_found";
return {
period_scope: input.periodScope,
rows_matched: result.matched_rows,
writeoff_rows: writeoffRows,
writeoff_amount: writeoffAmount,
writeoff_amount_human_ru: formatAmountHumanRu(writeoffAmount),
receipt_adjustment_rows: receiptAdjustmentRows,
receipt_adjustment_amount: receiptAdjustmentAmount,
receipt_adjustment_amount_human_ru: formatAmountHumanRu(receiptAdjustmentAmount),
inventory_count_rows: inventoryCountRows,
revaluation_rows: revaluationRows,
first_event_date: sortedDates[0] ?? null,
latest_event_date: sortedDates[sortedDates.length - 1] ?? null,
evidence_status: evidenceStatus,
inference_basis: "inventory_quality_documents_confirmed_1c_rows"
};
}
function deriveBusinessOverviewVendorProcurementQuality(input) {
if (!input.rankedOutgoing ||
input.rankedOutgoing.ranked_values.length <= 0 ||
@ -3109,10 +3182,10 @@ function buildBusinessOverviewMissingProofFamilies(input) {
must_not_claim: "confirmed_overdue_debt_credit_risk_or_due_date_aging"
});
}
if (missing.has("inventory_position") ||
if ((missing.has("inventory_position") ||
missing.has("inventory_turnover_quality") ||
missing.has("inventory_liquidity_quality") ||
missing.has("inventory_reserve_liquidation_quality")) {
missing.has("inventory_reserve_liquidation_quality")) && !input.inventoryQualityEvents) {
pushUnique({
family: "inventory_reserve_liquidation_quality",
current_status: input.inventoryStalenessRiskProxy
@ -3195,6 +3268,10 @@ function deriveBusinessOverview(input) {
inventoryPosition,
inventoryTurnoverProxy
});
const inventoryQualityEvents = deriveBusinessOverviewInventoryQualityEvents({
inventoryQualityEventsResult: input.inventoryQualityEventsResult,
periodScope: input.periodScope
});
const vendorProcurementQuality = deriveBusinessOverviewVendorProcurementQuality({
rankedOutgoing,
outgoing,
@ -3219,6 +3296,7 @@ function deriveBusinessOverview(input) {
Boolean(inventoryPosition),
Boolean(inventoryTurnoverProxy),
Boolean(inventoryStalenessRiskProxy),
Boolean(inventoryQualityEvents),
Boolean(vendorProcurementQuality)
].filter(Boolean).length;
if (checkedSignalCount <= 0) {
@ -3232,11 +3310,13 @@ function deriveBusinessOverview(input) {
debtDueDateAging ? null : debtOpenSettlementQuality ? "debt_due_date_aging_quality" : "debt_open_settlement_quality",
taxPosition ? null : "tax_position",
inventoryPosition
? inventoryStalenessRiskProxy
? "inventory_reserve_liquidation_quality"
: inventoryTurnoverProxy
? "inventory_liquidity_quality"
: "inventory_turnover_quality"
? inventoryQualityEvents
? null
: inventoryStalenessRiskProxy
? "inventory_reserve_liquidation_quality"
: inventoryTurnoverProxy
? "inventory_liquidity_quality"
: "inventory_turnover_quality"
: "inventory_position",
inventoryPosition?.aging_signal ? null : "inventory_aging_quality"
].filter((item) => Boolean(item));
@ -3250,6 +3330,7 @@ function deriveBusinessOverview(input) {
inventoryPosition,
inventoryTurnoverProxy,
inventoryStalenessRiskProxy,
inventoryQualityEvents,
vendorProcurementQuality,
hasSupplierConcentrationSignal: (rankedOutgoing?.ranked_values.length ?? 0) > 0
});
@ -3275,6 +3356,7 @@ function deriveBusinessOverview(input) {
inventory_position: inventoryPosition,
inventory_turnover_proxy: inventoryTurnoverProxy,
inventory_staleness_risk_proxy: inventoryStalenessRiskProxy,
inventory_quality_events: inventoryQualityEvents,
document_activity_profile: documentActivityProfile,
counterparty_profile: counterpartyProfile,
contract_usage_profile: contractUsageProfile,
@ -3283,7 +3365,7 @@ function deriveBusinessOverview(input) {
checked_signal_count: checkedSignalCount,
missing_signal_families: missingSignalFamilies,
missing_proof_families: missingProofFamilies,
inference_basis: hasBusinessOverviewProfileSignal || inventoryPosition || accountingFinancialResult
inference_basis: hasBusinessOverviewProfileSignal || inventoryPosition || inventoryQualityEvents || accountingFinancialResult
? "business_overview_from_confirmed_1c_multi_family_rows"
: debtOpenSettlementQuality || debtDueDateAging
? "business_overview_from_confirmed_1c_multi_family_rows"
@ -3343,6 +3425,9 @@ function summarizeBusinessOverviewRows(input) {
if (input.inventoryAgingResult && !input.inventoryAgingResult.error) {
parts.push(`${input.inventoryAgingResult.fetched_rows} inventory aging rows fetched, ${input.inventoryAgingResult.matched_rows} matched`);
}
if (input.inventoryQualityEventsResult && !input.inventoryQualityEventsResult.error) {
parts.push(`${input.inventoryQualityEventsResult.fetched_rows} inventory quality-event rows fetched, ${input.inventoryQualityEventsResult.matched_rows} matched`);
}
return parts.length > 0 ? parts.join("; ") : null;
}
function buildBusinessOverviewConfirmedFacts(derived) {
@ -3511,6 +3596,18 @@ function buildBusinessOverviewConfirmedFacts(derived) {
const proxy = derived.inventory_staleness_risk_proxy;
facts.push(`Staleness risk proxy склада на ${proxy.as_of_date}: самая ранняя дата закупочного сигнала ${proxy.oldest_purchase_date}, возраст ${proxy.max_purchase_age_days} дн., sales-to-stock ${proxy.sales_to_stock_amount_ratio}x, оценка ${inventoryStalenessRiskBandRu(proxy.risk_band)}. Это не подтвержденная неликвидность, не резерв и не ликвидационная стоимость.`);
}
if (derived.inventory_quality_events) {
const quality = derived.inventory_quality_events;
const eventWindow = quality.first_event_date && quality.latest_event_date
? ` Окно найденных событий: ${quality.first_event_date}${quality.latest_event_date}.`
: "";
if (quality.evidence_status === "reviewed_no_quality_events_found") {
facts.push(`Reviewed inventory quality route проверил складские документы списания, оприходования, инвентаризации и переоценки${period}: подтвержденных событий не найдено. Это проверенный отрицательный результат по доступным документам, но не рыночная ликвидационная оценка и не управленческий резерв.`);
}
else {
facts.push(`Reviewed inventory quality route проверил складские документы${period}: списаний ${quality.writeoff_rows} на ${quality.writeoff_amount_human_ru}, оприходований/корректировок ${quality.receipt_adjustment_rows} на ${quality.receipt_adjustment_amount_human_ru}, инвентаризаций ${quality.inventory_count_rows}, переоценок ${quality.revaluation_rows}.${eventWindow} Это подтвержденные документы 1С, но не самостоятельная рыночная ликвидационная стоимость.`);
}
}
return facts;
}
function buildBusinessOverviewInferredFacts(derived) {
@ -4218,6 +4315,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
let contractUsageProfileResult = null;
let inventoryOnHandResult = null;
let inventoryAgingResult = null;
let inventoryQualityEventsResult = null;
const valueFilters = buildValueFlowFilters(planner);
const lifecycleFilters = buildLifecycleFilters(planner);
const profileFilters = buildBusinessOverviewProfileFilters(planner);
@ -4227,6 +4325,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
const debtFilters = buildBusinessOverviewDebtFilters(planner);
const debtDueDateAgingProbeEnabled = shouldRunDebtDueDateAgingProbe(planner);
const inventoryFilters = buildBusinessOverviewInventoryFilters(planner);
const inventoryQualityEventsProbeEnabled = shouldRunInventoryQualityEventsProbe(planner);
const debtAsOfDate = toNonEmptyString(debtFilters?.as_of_date);
const inventoryAsOfDate = toNonEmptyString(inventoryFilters?.as_of_date);
const incomingSelection = (0, addressRecipeCatalog_1.selectAddressRecipe)("customer_revenue_and_payments", valueFilters);
@ -4262,6 +4361,9 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
const inventoryAgingSelection = inventoryFilters
? (0, addressRecipeCatalog_1.selectAddressRecipe)("inventory_aging_by_purchase_date", inventoryFilters)
: null;
const inventoryQualityEventsSelection = inventoryQualityEventsProbeEnabled
? (0, addressRecipeCatalog_1.selectAddressRecipe)("inventory_quality_events_for_organization", inventoryFilters ?? buildBusinessOverviewProfileFilters(planner))
: null;
if (!incomingSelection.selected_recipe || !outgoingSelection.selected_recipe || !lifecycleSelection.selected_recipe) {
pushReason(reasonCodes, "pilot_business_overview_recipe_not_available");
const missing = [
@ -4389,6 +4491,16 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
pushReason(reasonCodes, "pilot_business_overview_inventory_recipe_not_available");
pushUnique(queryLimitations, "Business overview inventory-position probe requires an executable inventory on-hand as-of-date recipe");
}
if (inventoryQualityEventsSelection?.selected_recipe) {
pushReason(reasonCodes, "pilot_business_overview_inventory_quality_events_recipe_selected");
}
else if (!inventoryQualityEventsProbeEnabled) {
pushReason(reasonCodes, "pilot_business_overview_inventory_quality_events_probe_skipped_without_boundary_need");
}
else {
pushReason(reasonCodes, "pilot_business_overview_inventory_quality_events_recipe_not_available");
pushUnique(queryLimitations, "Business overview inventory quality probe requires an executable inventory quality-events recipe");
}
for (const step of dryRun.execution_steps) {
if (step.primitive_id === "query_movements") {
const incomingExecution = await executeCoverageAwareValueFlowQuery({
@ -4622,6 +4734,16 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
});
probeResults.push(queryResultToProbeResult(step.primitive_id, contractUsageProfileResult));
}
if (inventoryQualityEventsSelection?.selected_recipe) {
const inventoryQualityEventsFilters = inventoryFilters ?? buildBusinessOverviewProfileFilters(planner);
const inventoryQualityEventsPlan = (0, addressRecipeCatalog_1.buildAddressRecipePlan)(inventoryQualityEventsSelection.selected_recipe, inventoryQualityEventsFilters);
inventoryQualityEventsResult = await runtimeDeps.executeAddressMcpQuery({
query: inventoryQualityEventsPlan.query,
limit: inventoryQualityEventsPlan.limit,
account_scope: inventoryQualityEventsPlan.account_scope
});
probeResults.push(queryResultToProbeResult(step.primitive_id, inventoryQualityEventsResult));
}
if (lifecycleResult.error) {
pushUnique(queryLimitations, lifecycleResult.error);
pushReason(reasonCodes, "pilot_business_overview_query_documents_mcp_error");
@ -4657,6 +4779,13 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
else if (contractUsageProfileResult) {
pushReason(reasonCodes, "pilot_business_overview_contract_usage_profile_query_mcp_executed");
}
if (inventoryQualityEventsResult?.error) {
pushUnique(queryLimitations, inventoryQualityEventsResult.error);
pushReason(reasonCodes, "pilot_business_overview_inventory_quality_events_query_mcp_error");
}
else if (inventoryQualityEventsResult) {
pushReason(reasonCodes, "pilot_business_overview_inventory_quality_events_query_mcp_executed");
}
continue;
}
skippedPrimitives.push(step.primitive_id);
@ -4679,6 +4808,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
debtAsOfDate,
inventoryOnHandResult,
inventoryAgingResult,
inventoryQualityEventsResult,
inventoryAsOfDate,
organizationScope,
periodScope: dateScope
@ -4744,6 +4874,10 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
if (derivedBusinessOverview.inventory_staleness_risk_proxy) {
pushReason(reasonCodes, "pilot_derived_business_overview_inventory_staleness_risk_proxy_from_confirmed_rows");
}
if (derivedBusinessOverview.inventory_quality_events) {
pushReason(reasonCodes, "pilot_derived_business_overview_inventory_quality_events_from_reviewed_rows");
pushReason(reasonCodes, `pilot_derived_business_overview_inventory_quality_events_${derivedBusinessOverview.inventory_quality_events.evidence_status}`);
}
if (derivedBusinessOverview.missing_proof_families.length > 0) {
pushReason(reasonCodes, "pilot_business_overview_missing_proof_families_recorded");
}
@ -4763,7 +4897,8 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
counterpartyProfileResult,
contractUsageProfileResult,
inventoryOnHandResult,
inventoryAgingResult
inventoryAgingResult,
inventoryQualityEventsResult
});
const evidence = (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({
plan: planner.discovery_plan,

View File

@ -830,10 +830,18 @@ function buildCompactBusinessOverviewReply(entryPoint, draft) {
}
if (inventoryReserveBoundary) {
const headline = toNonEmptyString(draft.headline);
const inventoryQualityEvents = toRecordObject(overview.inventory_quality_events);
const cleanHeadline = headline?.replace(/^Коротко:\s*/iu, "").trim();
lines.push(cleanHeadline
? `Коротко: ${localizeLine(cleanHeadline)}`
: "Коротко: точно подтвердить резерв под неликвиды по текущим данным нельзя.");
if (inventoryQualityEvents) {
if (limitLine) {
lines.push(limitLine);
}
const reply = lines.join("\n").trim();
return reply.length > 0 && !hasInternalMechanics(reply) ? reply : null;
}
const boundaryLines = userFacingLines([
...toStringList(draft.unknown_lines),
...toStringList(draft.limitation_lines)

View File

@ -296,6 +296,62 @@ const INVENTORY_ON_HAND_AS_OF_QUERY_TEMPLATE = `
Количество __ORDER_DIRECTION__
`;
const INVENTORY_QUALITY_EVENTS_QUERY_TEMPLATE = `
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
Списание.Дата КАК Период,
ПРЕДСТАВЛЕНИЕ(Списание.Ссылка) КАК Регистратор,
"Списание товаров" КАК ТипСобытия,
ПРЕДСТАВЛЕНИЕ(Списание.Организация) КАК Организация,
ПРЕДСТАВЛЕНИЕ(Списание.Склад) КАК Склад,
Списание.СуммаДокумента КАК Сумма,
Списание.Основание КАК Основание,
Списание.Комментарий КАК Комментарий
ИЗ
Документ.СписаниеТоваров КАК Списание
__WHERE_WRITE_OFF__
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
Оприходование.Дата КАК Период,
ПРЕДСТАВЛЕНИЕ(Оприходование.Ссылка) КАК Регистратор,
"Оприходование товаров" КАК ТипСобытия,
ПРЕДСТАВЛЕНИЕ(Оприходование.Организация) КАК Организация,
ПРЕДСТАВЛЕНИЕ(Оприходование.Склад) КАК Склад,
Оприходование.СуммаДокумента КАК Сумма,
Оприходование.Основание КАК Основание,
Оприходование.Комментарий КАК Комментарий
ИЗ
Документ.ОприходованиеТоваров КАК Оприходование
__WHERE_RECEIPT__
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
Инвентаризация.Дата КАК Период,
ПРЕДСТАВЛЕНИЕ(Инвентаризация.Ссылка) КАК Регистратор,
"Инвентаризация товаров на складе" КАК ТипСобытия,
ПРЕДСТАВЛЕНИЕ(Инвентаризация.Организация) КАК Организация,
ПРЕДСТАВЛЕНИЕ(Инвентаризация.Склад) КАК Склад,
0 КАК Сумма,
Инвентаризация.ПричинаПроведенияИнвентаризации КАК Основание,
Инвентаризация.Комментарий КАК Комментарий
ИЗ
Документ.ИнвентаризацияТоваровНаСкладе КАК Инвентаризация
__WHERE_INVENTORY_COUNT__
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
Переоценка.Дата КАК Период,
ПРЕДСТАВЛЕНИЕ(Переоценка.Ссылка) КАК Регистратор,
"Переоценка товаров в рознице" КАК ТипСобытия,
ПРЕДСТАВЛЕНИЕ(Переоценка.Организация) КАК Организация,
ПРЕДСТАВЛЕНИЕ(Переоценка.Склад) КАК Склад,
0 КАК Сумма,
"" КАК Основание,
Переоценка.Комментарий КАК Комментарий
ИЗ
Документ.ПереоценкаТоваровВРознице КАК Переоценка
__WHERE_REVALUATION__
УПОРЯДОЧИТЬ ПО
Период __ORDER_DIRECTION__
`;
const BANK_DOCS_QUERY_TEMPLATE = `
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
БанкСписание.Дата КАК Период,
@ -958,6 +1014,16 @@ const BASE_RECIPES: AddressRecipeDefinition[] = [
account_scope_mode: "strict",
query_template: "inventory_aging_by_purchase_date_profile"
},
{
recipe_id: "address_inventory_quality_events_for_organization_v1",
intent: "inventory_quality_events_for_organization",
purpose: "Check posted inventory quality event documents: write-offs, stocktaking, receipt adjustments, and retail revaluation",
required_filters: [],
optional_filters: ["as_of_date", "period_from", "period_to", "organization", "warehouse", "limit", "sort"],
default_limit: 400,
account_scope_mode: "preferred",
query_template: "inventory_quality_events_profile"
},
{
recipe_id: "address_open_contracts_confirmed_as_of_date_v1",
intent: "open_contracts_confirmed_as_of_date",
@ -1432,6 +1498,77 @@ function buildInventoryMovementQuery(
.replaceAll("__ORDER_DIRECTION__", resolveOrderDirection(filters.sort));
}
function buildWarehouseReferenceCondition(filters: AddressFilterSet, fieldPaths: string[]): string | null {
const warehouse = typeof filters.warehouse === "string" ? filters.warehouse.trim() : "";
if (!warehouse) {
return null;
}
const tokens = Array.from(
new Set(
warehouse
.split(/[^A-Za-zА-Яа-яЁё0-9]+/u)
.map((token) => token.trim())
.filter((token) => token.length >= 3)
.filter((token) => !["склад", "warehouse"].includes(token.toLowerCase()))
)
);
const effectiveTokens = tokens.length > 0 ? tokens : [warehouse];
const clauses = fieldPaths
.map((fieldPath) => String(fieldPath ?? "").trim())
.filter((fieldPath) => fieldPath.length > 0)
.map((fieldPath) => {
const tokenConditions = effectiveTokens.map((token) => {
const escapedToken = toQueryStringLiteral(token);
return `${fieldPath}.Наименование ПОДОБНО "%${escapedToken}%"`;
});
return tokenConditions.length === 1 ? tokenConditions[0] : `(${tokenConditions.join(" И ")})`;
});
if (clauses.length === 0) {
return null;
}
return clauses.length === 1 ? clauses[0] : `(${clauses.join(" ИЛИ ")})`;
}
function buildInventoryQualityDocumentWhereClause(
filters: AddressFilterSet,
dateFieldPath: string,
organizationFieldPath: string,
warehouseFieldPath: string
): string {
return buildWhereClause(filters, dateFieldPath, [
`${dateFieldPath.replace(/\.Дата$/u, ".Проведен")} = ИСТИНА`,
buildOrganizationReferenceCondition(filters, [organizationFieldPath]),
buildWarehouseReferenceCondition(filters, [warehouseFieldPath])
].filter((item): item is string => Boolean(item)));
}
function buildInventoryQualityEventsQuery(filters: AddressFilterSet, resolvedLimit: number): string {
return INVENTORY_QUALITY_EVENTS_QUERY_TEMPLATE
.replaceAll("__LIMIT__", String(resolvedLimit))
.replace(
"__WHERE_WRITE_OFF__",
buildInventoryQualityDocumentWhereClause(filters, "Списание.Дата", "Списание.Организация", "Списание.Склад")
)
.replace(
"__WHERE_RECEIPT__",
buildInventoryQualityDocumentWhereClause(filters, "Оприходование.Дата", "Оприходование.Организация", "Оприходование.Склад")
)
.replace(
"__WHERE_INVENTORY_COUNT__",
buildInventoryQualityDocumentWhereClause(
filters,
"Инвентаризация.Дата",
"Инвентаризация.Организация",
"Инвентаризация.Склад"
)
)
.replace(
"__WHERE_REVALUATION__",
buildInventoryQualityDocumentWhereClause(filters, "Переоценка.Дата", "Переоценка.Организация", "Переоценка.Склад")
)
.replaceAll("__ORDER_DIRECTION__", resolveOrderDirection(filters.sort));
}
function buildInventoryItemReferenceCondition(filters: AddressFilterSet, fieldPaths: string[]): string | null {
const item = typeof filters.item === "string" ? filters.item.trim() : "";
if (!item) {
@ -1783,6 +1920,7 @@ function maxLimitForIntent(intent: AddressIntent): number {
intent === "inventory_profitability_for_item" ||
intent === "inventory_purchase_to_sale_chain" ||
intent === "inventory_aging_by_purchase_date" ||
intent === "inventory_quality_events_for_organization" ||
intent === "open_contracts_confirmed_as_of_date" ||
intent === "list_contracts_by_counterparty" ||
intent === "list_documents_by_counterparty" ||
@ -2037,6 +2175,8 @@ export function buildAddressRecipePlan(
? buildInventoryPurchaseToSaleDocumentQuery(filters, resolvedLimit)
: recipe.query_template === "inventory_aging_by_purchase_date_profile"
? buildInventoryMovementQuery(filters, resolvedLimit, "dt")
: recipe.query_template === "inventory_quality_events_profile"
? buildInventoryQualityEventsQuery(filters, resolvedLimit)
: recipe.query_template === "contracts_by_counterparty_profile"
? CONTRACTS_BY_COUNTERPARTY_QUERY_TEMPLATE.replaceAll("__LIMIT__", String(resolvedLimit))
: recipe.query_template === "open_contracts_confirmed_as_of_balance_profile"

View File

@ -510,6 +510,9 @@ function isVendorRiskBoundaryTurn(pilot: AssistantMcpDiscoveryPilotExecutionCont
}
function businessOverviewInventoryUnknownLabel(overview: BusinessOverview): string {
if (overview.inventory_quality_events) {
return "рыночная ликвидационная стоимость и управленческий резерв склада";
}
if (overview.inventory_staleness_risk_proxy) {
return "резервы/списания/ликвидационная стоимость склада";
}
@ -764,6 +767,26 @@ function businessOverviewVendorProcurementQualityText(overview: BusinessOverview
return `Procurement-concentration route за ${period} отработал по исходящим платежам на ${total}, но надежной небанковской концентрации поставщика по найденным строкам не хватает.${contractText} Полный vendor-risk аудит не подтвержден.`;
}
function businessOverviewInventoryQualityEventsText(overview: BusinessOverview): string | null {
const quality = overview.inventory_quality_events;
if (!quality) {
return null;
}
const period = quality.period_scope ?? "проверенное окно";
const organization = overview.organization_scope ? ` по организации ${overview.organization_scope}` : "";
const eventWindow =
quality.first_event_date && quality.latest_event_date
? ` Окно найденных событий: ${quality.first_event_date} - ${quality.latest_event_date}.`
: "";
if (quality.evidence_status === "reviewed_no_quality_events_found") {
return `Коротко: проверил складские документы списания, оприходования, инвентаризации и переоценки${organization} за ${period}; подтвержденных событий списания/корректировки/инвентаризации/переоценки не найдено. Это сильный отрицательный сигнал по доступным документам 1С, но не рыночная ликвидационная стоимость и не управленческий резерв под неликвиды.`;
}
if (quality.evidence_status === "reviewed_inventory_control_events_only") {
return `Коротко: проверил складские quality-события${organization} за ${period}; списаний и оприходований/корректировок с суммой не найдено, но есть инвентаризации ${quality.inventory_count_rows} и переоценки ${quality.revaluation_rows}.${eventWindow} Это контрольные складские документы, а не подтвержденный резерв или рыночная ликвидационная оценка.`;
}
return `Коротко: проверил складские quality-события${organization} за ${period}; списаний ${quality.writeoff_rows} на ${quality.writeoff_amount_human_ru}, оприходований/корректировок ${quality.receipt_adjustment_rows} на ${quality.receipt_adjustment_amount_human_ru}, инвентаризаций ${quality.inventory_count_rows}, переоценок ${quality.revaluation_rows}.${eventWindow} Это подтвержденные документы 1С по складским событиям, но не самостоятельная рыночная ликвидационная стоимость и не расчет управленческого резерва.`;
}
function headlineFor(mode: AssistantMcpDiscoveryAnswerMode, pilot: AssistantMcpDiscoveryPilotExecutionContract): string {
const askedMonthlyBreakdown =
pilot.derived_bidirectional_value_flow?.aggregation_axis === "month" ||
@ -797,6 +820,10 @@ function headlineFor(mode: AssistantMcpDiscoveryAnswerMode, pilot: AssistantMcpD
return "Нельзя точно определить, какая дебиторка просрочена, по текущему срезу 1С; есть только debt-quality proxy, но нет проверенного due-date маршрута по договорам, срокам оплаты и погашению расчетов.";
}
if (isInventoryReserveBoundaryTurn(pilot)) {
const inventoryQualityEventsText = businessOverviewInventoryQualityEventsText(overview);
if (inventoryQualityEventsText) {
return inventoryQualityEventsText;
}
const inventoryBasis = overview.inventory_staleness_risk_proxy
? "есть только складской staleness-risk proxy по найденным строкам"
: overview.inventory_position || overview.inventory_turnover_proxy
@ -870,6 +897,9 @@ function headlineFor(mode: AssistantMcpDiscoveryAnswerMode, pilot: AssistantMcpD
if (overview.inventory_staleness_risk_proxy) {
families.push("staleness risk proxy склада");
}
if (overview.inventory_quality_events) {
families.push("складские quality-события");
}
const unknownFamilies = overview.accounting_financial_result
? ["аудированная/юридически подтвержденная прибыль"]
: [overview.trading_margin_proxy ? "чистая прибыль/точная маржа" : "прибыль/маржа"];
@ -1101,6 +1131,9 @@ function buildMustNotClaim(pilot: AssistantMcpDiscoveryPilotExecutionContract):
claims.push("Do not present an inventory snapshot or purchase-date aging signal as turnover, obsolescence, liquidation value, or full inventory health.");
claims.push("Do not present business overview inventory turnover proxy as full inventory liquidity, FIFO turnover, obsolescence analysis, or liquidation value.");
claims.push("Do not present business overview inventory staleness risk proxy as confirmed obsolete stock, reserve, write-off, or liquidation value.");
if (pilot.derived_business_overview?.inventory_quality_events) {
claims.push("Do not present reviewed inventory quality events as confirmed obsolete stock, reserve policy, market liquidation value, management reserve, or full inventory health.");
}
if (
pilot.derived_business_overview?.top_customers?.some(isFinancialInstitutionBucket) ||
pilot.derived_business_overview?.top_suppliers?.some(isFinancialInstitutionBucket)
@ -1676,6 +1709,10 @@ function derivedBusinessOverviewConfirmedLines(pilot: AssistantMcpDiscoveryPilot
`Staleness risk proxy склада на ${proxy.as_of_date}: самая ранняя дата закупочного сигнала ${proxy.oldest_purchase_date}, возраст ${proxy.max_purchase_age_days} дн., sales-to-stock ${proxy.sales_to_stock_amount_ratio}x, оценка ${inventoryStalenessRiskBandRu(proxy.risk_band)}. Это не подтвержденная неликвидность, не резерв и не ликвидационная стоимость.`
);
}
const inventoryQualityEventsText = businessOverviewInventoryQualityEventsText(overview);
if (inventoryQualityEventsText) {
lines.push(inventoryQualityEventsText.replace(/^Коротко:\s*/u, ""));
}
return lines;
}
@ -1858,6 +1895,16 @@ function businessOverviewRiskSynthesisLine(overview: BusinessOverview): string |
`staleness risk proxy склада: ${inventoryStalenessRiskBandRu(overview.inventory_staleness_risk_proxy.risk_band)}, возраст ${overview.inventory_staleness_risk_proxy.max_purchase_age_days} дн.`
);
}
if (overview.inventory_quality_events) {
const quality = overview.inventory_quality_events;
if (quality.evidence_status === "reviewed_no_quality_events_found") {
signals.push("складские quality-события: документы списания, оприходования, инвентаризации и переоценки проверены, подтвержденных событий не найдено");
} else {
signals.push(
`складские quality-события: списаний ${quality.writeoff_rows} на ${quality.writeoff_amount_human_ru}, оприходований/корректировок ${quality.receipt_adjustment_rows} на ${quality.receipt_adjustment_amount_human_ru}, инвентаризаций ${quality.inventory_count_rows}, переоценок ${quality.revaluation_rows}`
);
}
}
return signals.length > 0
? `Риски и контуры внимания по подтвержденным данным: ${signals.join("; ")}.`
: null;
@ -1873,7 +1920,8 @@ function businessOverviewExecutiveVerdictLine(overview: BusinessOverview): strin
overview.debt_staleness_risk_proxy ||
overview.inventory_position ||
overview.inventory_turnover_proxy ||
overview.inventory_staleness_risk_proxy
overview.inventory_staleness_risk_proxy ||
overview.inventory_quality_events
);
const hasOperationalProfileSignal = Boolean(
overview.document_activity_profile || overview.counterparty_profile || overview.contract_usage_profile
@ -2020,6 +2068,10 @@ export function buildAssistantMcpDiscoveryAnswerDraft(
if (pilot.derived_business_overview?.inventory_staleness_risk_proxy) {
pushReason(reasonCodes, "answer_contains_business_overview_inventory_staleness_risk_proxy");
}
if (pilot.derived_business_overview?.inventory_quality_events) {
pushReason(reasonCodes, "answer_contains_business_overview_inventory_quality_events");
pushReason(reasonCodes, `answer_contains_business_overview_inventory_quality_events_${pilot.derived_business_overview.inventory_quality_events.evidence_status}`);
}
if (pilot.derived_business_overview?.missing_proof_families?.length) {
pushReason(reasonCodes, "answer_contains_business_overview_missing_proof_ledger");
}

View File

@ -265,6 +265,7 @@ export interface AssistantMcpDiscoveryDerivedBusinessOverview {
inventory_position: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryPosition | null;
inventory_turnover_proxy: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryTurnoverProxy | null;
inventory_staleness_risk_proxy: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryStalenessRiskProxy | null;
inventory_quality_events: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryQualityEvents | null;
document_activity_profile: AssistantMcpDiscoveryDerivedBusinessOverviewDocumentActivityProfile | null;
counterparty_profile: AssistantMcpDiscoveryDerivedBusinessOverviewCounterpartyProfile | null;
contract_usage_profile: AssistantMcpDiscoveryDerivedBusinessOverviewContractUsageProfile | null;
@ -521,6 +522,26 @@ export interface AssistantMcpDiscoveryDerivedBusinessOverviewInventoryStalenessR
inference_basis: "purchase_date_age_and_sales_to_stock_proxy_confirmed_1c_rows";
}
export interface AssistantMcpDiscoveryDerivedBusinessOverviewInventoryQualityEvents {
period_scope: string | null;
rows_matched: number;
writeoff_rows: number;
writeoff_amount: number;
writeoff_amount_human_ru: string;
receipt_adjustment_rows: number;
receipt_adjustment_amount: number;
receipt_adjustment_amount_human_ru: string;
inventory_count_rows: number;
revaluation_rows: number;
first_event_date: string | null;
latest_event_date: string | null;
evidence_status:
| "reviewed_no_quality_events_found"
| "reviewed_writeoff_or_adjustment_events_found"
| "reviewed_inventory_control_events_only";
inference_basis: "inventory_quality_documents_confirmed_1c_rows";
}
export interface AssistantMcpDiscoveryDerivedMetadataSurface {
metadata_scope: string | null;
requested_meta_types: string[];
@ -829,6 +850,17 @@ function shouldRunDebtDueDateAgingProbe(planner: AssistantMcpDiscoveryPlannerCon
return /(?:debt_due_date_boundary|due[-_ ]?date|overdue|aging|просроч|срок\s+оплат|дебиторк|кредиторск)/iu.test(combined);
}
function shouldRunInventoryQualityEventsProbe(planner: AssistantMcpDiscoveryPlannerContract): boolean {
const actionFamily = toNonEmptyString(planner.data_need_graph?.action_family);
const turnActionFamily = toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.asked_action_family);
const unsupportedFamily = toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.unsupported_but_understood_family);
const proofExpectation = toNonEmptyString(planner.data_need_graph?.proof_expectation);
const combined = [actionFamily, turnActionFamily, unsupportedFamily, proofExpectation]
.filter((item): item is string => Boolean(item))
.join(" ");
return /(?:inventory_reserve|reserve_liquidation|liquidation|write[-_ ]?off|obsolete|obsolescence|inventory_reserve_liquidation_quality|резерв|списан|ликвидац|неликвид|обесцен)/iu.test(combined);
}
function buildBusinessOverviewInventoryFilters(planner: AssistantMcpDiscoveryPlannerContract): AddressFilterSet | null {
const meaning = planner.discovery_plan.turn_meaning_ref;
const organization = toNonEmptyString(meaning?.explicit_organization_scope);
@ -4148,6 +4180,79 @@ function deriveBusinessOverviewInventoryStalenessRiskProxy(input: {
};
}
function rowInventoryQualityEventType(row: Record<string, unknown>): string {
return rowTextValue(row, ["ТипСобытия", "EventType", "event_type", "Регистратор", "Registrator", "registrator"]) ?? "";
}
function deriveBusinessOverviewInventoryQualityEvents(input: {
inventoryQualityEventsResult: AddressMcpQueryExecutorResult | null;
periodScope: string | null;
}): AssistantMcpDiscoveryDerivedBusinessOverviewInventoryQualityEvents | null {
const result = input.inventoryQualityEventsResult;
if (!result || result.error) {
return null;
}
let writeoffRows = 0;
let writeoffAmount = 0;
let receiptAdjustmentRows = 0;
let receiptAdjustmentAmount = 0;
let inventoryCountRows = 0;
let revaluationRows = 0;
const eventDates: string[] = [];
for (const row of result.rows) {
const eventType = rowInventoryQualityEventType(row);
const amount = rowAmountValue(row) ?? 0;
const date = rowDateValue(row);
if (date) {
eventDates.push(date);
}
if (/списан|write[-_ ]?off/iu.test(eventType)) {
writeoffRows += 1;
writeoffAmount += amount;
continue;
}
if (/оприход|receipt|positive/i.test(eventType)) {
receiptAdjustmentRows += 1;
receiptAdjustmentAmount += amount;
continue;
}
if (/инвентаризац|stocktaking|inventory count/i.test(eventType)) {
inventoryCountRows += 1;
continue;
}
if (/переоцен|revaluation/i.test(eventType)) {
revaluationRows += 1;
}
}
const sortedDates = eventDates.sort((left, right) => left.localeCompare(right));
const evidenceStatus: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryQualityEvents["evidence_status"] =
writeoffRows > 0 || receiptAdjustmentRows > 0
? "reviewed_writeoff_or_adjustment_events_found"
: inventoryCountRows > 0 || revaluationRows > 0
? "reviewed_inventory_control_events_only"
: "reviewed_no_quality_events_found";
return {
period_scope: input.periodScope,
rows_matched: result.matched_rows,
writeoff_rows: writeoffRows,
writeoff_amount: writeoffAmount,
writeoff_amount_human_ru: formatAmountHumanRu(writeoffAmount),
receipt_adjustment_rows: receiptAdjustmentRows,
receipt_adjustment_amount: receiptAdjustmentAmount,
receipt_adjustment_amount_human_ru: formatAmountHumanRu(receiptAdjustmentAmount),
inventory_count_rows: inventoryCountRows,
revaluation_rows: revaluationRows,
first_event_date: sortedDates[0] ?? null,
latest_event_date: sortedDates[sortedDates.length - 1] ?? null,
evidence_status: evidenceStatus,
inference_basis: "inventory_quality_documents_confirmed_1c_rows"
};
}
function deriveBusinessOverviewVendorProcurementQuality(input: {
rankedOutgoing: AssistantMcpDiscoveryDerivedRankedValueFlow | null;
outgoing: AssistantMcpDiscoveryValueFlowSideSummary;
@ -4226,6 +4331,7 @@ function buildBusinessOverviewMissingProofFamilies(input: {
inventoryPosition: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryPosition | null;
inventoryTurnoverProxy: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryTurnoverProxy | null;
inventoryStalenessRiskProxy: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryStalenessRiskProxy | null;
inventoryQualityEvents: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryQualityEvents | null;
vendorProcurementQuality: AssistantMcpDiscoveryDerivedBusinessOverviewVendorProcurementQuality | null;
hasSupplierConcentrationSignal: boolean;
}): AssistantMcpDiscoveryBusinessOverviewMissingProofFamily[] {
@ -4267,12 +4373,12 @@ function buildBusinessOverviewMissingProofFamilies(input: {
});
}
if (
if ((
missing.has("inventory_position") ||
missing.has("inventory_turnover_quality") ||
missing.has("inventory_liquidity_quality") ||
missing.has("inventory_reserve_liquidation_quality")
) {
) && !input.inventoryQualityEvents) {
pushUnique({
family: "inventory_reserve_liquidation_quality",
current_status: input.inventoryStalenessRiskProxy
@ -4322,6 +4428,7 @@ function deriveBusinessOverview(input: {
debtAsOfDate: string | null;
inventoryOnHandResult: AddressMcpQueryExecutorResult | null;
inventoryAgingResult: AddressMcpQueryExecutorResult | null;
inventoryQualityEventsResult: AddressMcpQueryExecutorResult | null;
inventoryAsOfDate: string | null;
organizationScope: string | null;
periodScope: string | null;
@ -4390,6 +4497,10 @@ function deriveBusinessOverview(input: {
inventoryPosition,
inventoryTurnoverProxy
});
const inventoryQualityEvents = deriveBusinessOverviewInventoryQualityEvents({
inventoryQualityEventsResult: input.inventoryQualityEventsResult,
periodScope: input.periodScope
});
const vendorProcurementQuality = deriveBusinessOverviewVendorProcurementQuality({
rankedOutgoing,
outgoing,
@ -4414,6 +4525,7 @@ function deriveBusinessOverview(input: {
Boolean(inventoryPosition),
Boolean(inventoryTurnoverProxy),
Boolean(inventoryStalenessRiskProxy),
Boolean(inventoryQualityEvents),
Boolean(vendorProcurementQuality)
].filter(Boolean).length;
if (checkedSignalCount <= 0) {
@ -4430,7 +4542,9 @@ function deriveBusinessOverview(input: {
debtDueDateAging ? null : debtOpenSettlementQuality ? "debt_due_date_aging_quality" : "debt_open_settlement_quality",
taxPosition ? null : "tax_position",
inventoryPosition
? inventoryStalenessRiskProxy
? inventoryQualityEvents
? null
: inventoryStalenessRiskProxy
? "inventory_reserve_liquidation_quality"
: inventoryTurnoverProxy
? "inventory_liquidity_quality"
@ -4448,6 +4562,7 @@ function deriveBusinessOverview(input: {
inventoryPosition,
inventoryTurnoverProxy,
inventoryStalenessRiskProxy,
inventoryQualityEvents,
vendorProcurementQuality,
hasSupplierConcentrationSignal: (rankedOutgoing?.ranked_values.length ?? 0) > 0
});
@ -4473,6 +4588,7 @@ function deriveBusinessOverview(input: {
inventory_position: inventoryPosition,
inventory_turnover_proxy: inventoryTurnoverProxy,
inventory_staleness_risk_proxy: inventoryStalenessRiskProxy,
inventory_quality_events: inventoryQualityEvents,
document_activity_profile: documentActivityProfile,
counterparty_profile: counterpartyProfile,
contract_usage_profile: contractUsageProfile,
@ -4483,7 +4599,7 @@ function deriveBusinessOverview(input: {
missing_signal_families: missingSignalFamilies,
missing_proof_families: missingProofFamilies,
inference_basis:
hasBusinessOverviewProfileSignal || inventoryPosition || accountingFinancialResult
hasBusinessOverviewProfileSignal || inventoryPosition || inventoryQualityEvents || accountingFinancialResult
? "business_overview_from_confirmed_1c_multi_family_rows"
: debtOpenSettlementQuality || debtDueDateAging
? "business_overview_from_confirmed_1c_multi_family_rows"
@ -4513,6 +4629,7 @@ function summarizeBusinessOverviewRows(input: {
contractUsageProfileResult: AddressMcpQueryExecutorResult | null;
inventoryOnHandResult: AddressMcpQueryExecutorResult | null;
inventoryAgingResult: AddressMcpQueryExecutorResult | null;
inventoryQualityEventsResult: AddressMcpQueryExecutorResult | null;
}): string | null {
const parts: string[] = [];
if (input.incomingResult && !input.incomingResult.error) {
@ -4560,6 +4677,9 @@ function summarizeBusinessOverviewRows(input: {
if (input.inventoryAgingResult && !input.inventoryAgingResult.error) {
parts.push(`${input.inventoryAgingResult.fetched_rows} inventory aging rows fetched, ${input.inventoryAgingResult.matched_rows} matched`);
}
if (input.inventoryQualityEventsResult && !input.inventoryQualityEventsResult.error) {
parts.push(`${input.inventoryQualityEventsResult.fetched_rows} inventory quality-event rows fetched, ${input.inventoryQualityEventsResult.matched_rows} matched`);
}
return parts.length > 0 ? parts.join("; ") : null;
}
@ -4780,6 +4900,22 @@ function buildBusinessOverviewConfirmedFacts(derived: AssistantMcpDiscoveryDeriv
`Staleness risk proxy склада на ${proxy.as_of_date}: самая ранняя дата закупочного сигнала ${proxy.oldest_purchase_date}, возраст ${proxy.max_purchase_age_days} дн., sales-to-stock ${proxy.sales_to_stock_amount_ratio}x, оценка ${inventoryStalenessRiskBandRu(proxy.risk_band)}. Это не подтвержденная неликвидность, не резерв и не ликвидационная стоимость.`
);
}
if (derived.inventory_quality_events) {
const quality = derived.inventory_quality_events;
const eventWindow =
quality.first_event_date && quality.latest_event_date
? ` Окно найденных событий: ${quality.first_event_date}${quality.latest_event_date}.`
: "";
if (quality.evidence_status === "reviewed_no_quality_events_found") {
facts.push(
`Reviewed inventory quality route проверил складские документы списания, оприходования, инвентаризации и переоценки${period}: подтвержденных событий не найдено. Это проверенный отрицательный результат по доступным документам, но не рыночная ликвидационная оценка и не управленческий резерв.`
);
} else {
facts.push(
`Reviewed inventory quality route проверил складские документы${period}: списаний ${quality.writeoff_rows} на ${quality.writeoff_amount_human_ru}, оприходований/корректировок ${quality.receipt_adjustment_rows} на ${quality.receipt_adjustment_amount_human_ru}, инвентаризаций ${quality.inventory_count_rows}, переоценок ${quality.revaluation_rows}.${eventWindow} Это подтвержденные документы 1С, но не самостоятельная рыночная ликвидационная стоимость.`
);
}
}
return facts;
}
@ -5616,6 +5752,7 @@ export async function executeAssistantMcpDiscoveryPilot(
let contractUsageProfileResult: AddressMcpQueryExecutorResult | null = null;
let inventoryOnHandResult: AddressMcpQueryExecutorResult | null = null;
let inventoryAgingResult: AddressMcpQueryExecutorResult | null = null;
let inventoryQualityEventsResult: AddressMcpQueryExecutorResult | null = null;
const valueFilters = buildValueFlowFilters(planner);
const lifecycleFilters = buildLifecycleFilters(planner);
const profileFilters = buildBusinessOverviewProfileFilters(planner);
@ -5625,6 +5762,7 @@ export async function executeAssistantMcpDiscoveryPilot(
const debtFilters = buildBusinessOverviewDebtFilters(planner);
const debtDueDateAgingProbeEnabled = shouldRunDebtDueDateAgingProbe(planner);
const inventoryFilters = buildBusinessOverviewInventoryFilters(planner);
const inventoryQualityEventsProbeEnabled = shouldRunInventoryQualityEventsProbe(planner);
const debtAsOfDate = toNonEmptyString(debtFilters?.as_of_date);
const inventoryAsOfDate = toNonEmptyString(inventoryFilters?.as_of_date);
const incomingSelection = selectAddressRecipe("customer_revenue_and_payments", valueFilters);
@ -5660,6 +5798,9 @@ export async function executeAssistantMcpDiscoveryPilot(
const inventoryAgingSelection = inventoryFilters
? selectAddressRecipe("inventory_aging_by_purchase_date", inventoryFilters)
: null;
const inventoryQualityEventsSelection = inventoryQualityEventsProbeEnabled
? selectAddressRecipe("inventory_quality_events_for_organization", inventoryFilters ?? buildBusinessOverviewProfileFilters(planner))
: null;
if (!incomingSelection.selected_recipe || !outgoingSelection.selected_recipe || !lifecycleSelection.selected_recipe) {
pushReason(reasonCodes, "pilot_business_overview_recipe_not_available");
@ -5771,6 +5912,14 @@ export async function executeAssistantMcpDiscoveryPilot(
pushReason(reasonCodes, "pilot_business_overview_inventory_recipe_not_available");
pushUnique(queryLimitations, "Business overview inventory-position probe requires an executable inventory on-hand as-of-date recipe");
}
if (inventoryQualityEventsSelection?.selected_recipe) {
pushReason(reasonCodes, "pilot_business_overview_inventory_quality_events_recipe_selected");
} else if (!inventoryQualityEventsProbeEnabled) {
pushReason(reasonCodes, "pilot_business_overview_inventory_quality_events_probe_skipped_without_boundary_need");
} else {
pushReason(reasonCodes, "pilot_business_overview_inventory_quality_events_recipe_not_available");
pushUnique(queryLimitations, "Business overview inventory quality probe requires an executable inventory quality-events recipe");
}
for (const step of dryRun.execution_steps) {
if (step.primitive_id === "query_movements") {
const incomingExecution = await executeCoverageAwareValueFlowQuery({
@ -6007,6 +6156,19 @@ export async function executeAssistantMcpDiscoveryPilot(
});
probeResults.push(queryResultToProbeResult(step.primitive_id, contractUsageProfileResult));
}
if (inventoryQualityEventsSelection?.selected_recipe) {
const inventoryQualityEventsFilters = inventoryFilters ?? buildBusinessOverviewProfileFilters(planner);
const inventoryQualityEventsPlan = buildAddressRecipePlan(
inventoryQualityEventsSelection.selected_recipe,
inventoryQualityEventsFilters
);
inventoryQualityEventsResult = await runtimeDeps.executeAddressMcpQuery({
query: inventoryQualityEventsPlan.query,
limit: inventoryQualityEventsPlan.limit,
account_scope: inventoryQualityEventsPlan.account_scope
});
probeResults.push(queryResultToProbeResult(step.primitive_id, inventoryQualityEventsResult));
}
if (lifecycleResult.error) {
pushUnique(queryLimitations, lifecycleResult.error);
pushReason(reasonCodes, "pilot_business_overview_query_documents_mcp_error");
@ -6037,6 +6199,12 @@ export async function executeAssistantMcpDiscoveryPilot(
} else if (contractUsageProfileResult) {
pushReason(reasonCodes, "pilot_business_overview_contract_usage_profile_query_mcp_executed");
}
if (inventoryQualityEventsResult?.error) {
pushUnique(queryLimitations, inventoryQualityEventsResult.error);
pushReason(reasonCodes, "pilot_business_overview_inventory_quality_events_query_mcp_error");
} else if (inventoryQualityEventsResult) {
pushReason(reasonCodes, "pilot_business_overview_inventory_quality_events_query_mcp_executed");
}
continue;
}
@ -6061,6 +6229,7 @@ export async function executeAssistantMcpDiscoveryPilot(
debtAsOfDate,
inventoryOnHandResult,
inventoryAgingResult,
inventoryQualityEventsResult,
inventoryAsOfDate,
organizationScope,
periodScope: dateScope
@ -6126,6 +6295,10 @@ export async function executeAssistantMcpDiscoveryPilot(
if (derivedBusinessOverview.inventory_staleness_risk_proxy) {
pushReason(reasonCodes, "pilot_derived_business_overview_inventory_staleness_risk_proxy_from_confirmed_rows");
}
if (derivedBusinessOverview.inventory_quality_events) {
pushReason(reasonCodes, "pilot_derived_business_overview_inventory_quality_events_from_reviewed_rows");
pushReason(reasonCodes, `pilot_derived_business_overview_inventory_quality_events_${derivedBusinessOverview.inventory_quality_events.evidence_status}`);
}
if (derivedBusinessOverview.missing_proof_families.length > 0) {
pushReason(reasonCodes, "pilot_business_overview_missing_proof_families_recorded");
}
@ -6145,7 +6318,8 @@ export async function executeAssistantMcpDiscoveryPilot(
counterpartyProfileResult,
contractUsageProfileResult,
inventoryOnHandResult,
inventoryAgingResult
inventoryAgingResult,
inventoryQualityEventsResult
});
const evidence = resolveAssistantMcpDiscoveryEvidence({
plan: planner.discovery_plan,

View File

@ -997,12 +997,20 @@ function buildCompactBusinessOverviewReply(
if (inventoryReserveBoundary) {
const headline = toNonEmptyString(draft.headline);
const inventoryQualityEvents = toRecordObject(overview.inventory_quality_events);
const cleanHeadline = headline?.replace(/^Коротко:\s*/iu, "").trim();
lines.push(
cleanHeadline
? `Коротко: ${localizeLine(cleanHeadline)}`
: "Коротко: точно подтвердить резерв под неликвиды по текущим данным нельзя."
);
if (inventoryQualityEvents) {
if (limitLine) {
lines.push(limitLine);
}
const reply = lines.join("\n").trim();
return reply.length > 0 && !hasInternalMechanics(reply) ? reply : null;
}
const boundaryLines = userFacingLines([
...toStringList(draft.unknown_lines),
...toStringList(draft.limitation_lines)

View File

@ -36,6 +36,7 @@ export type AddressIntent =
| "inventory_profitability_for_item"
| "inventory_purchase_to_sale_chain"
| "inventory_aging_by_purchase_date"
| "inventory_quality_events_for_organization"
| "account_balance_snapshot"
| "open_items_by_counterparty_or_contract"
| "list_documents_by_counterparty"
@ -204,7 +205,8 @@ export interface AddressRecipeDefinition {
| "inventory_trading_margin_proxy_profile"
| "inventory_profitability_profile"
| "inventory_purchase_to_sale_chain_profile"
| "inventory_aging_by_purchase_date_profile";
| "inventory_aging_by_purchase_date_profile"
| "inventory_quality_events_profile";
required_filters: Array<keyof AddressFilterSet>;
optional_filters: Array<keyof AddressFilterSet>;
default_limit: number;

View File

@ -710,10 +710,14 @@ describe("assistant MCP discovery answer adapter", () => {
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
expect(draft.headline).toContain(
"\u0442\u043e\u0447\u043d\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u0440\u0435\u0437\u0435\u0440\u0432"
"\u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043b \u0441\u043a\u043b\u0430\u0434\u0441\u043a\u0438\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b"
);
expect(draft.headline).toContain(
"\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439"
);
expect(draft.headline).toContain(
"\u043d\u0435 \u0440\u044b\u043d\u043e\u0447\u043d\u0430\u044f \u043b\u0438\u043a\u0432\u0438\u0434\u0430\u0446\u0438\u043e\u043d\u043d\u0430\u044f \u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c"
);
expect(draft.headline).toContain("\u043d\u0435\u043b\u044c\u0437\u044f");
expect(draft.headline).toContain("staleness-risk proxy");
expect(draft.headline).not.toContain("бизнес-обзор");
expect(draft.must_not_claim).toContain("Do not present business overview inventory staleness risk proxy as confirmed obsolete stock, reserve, write-off, or liquidation value.");
});

View File

@ -475,7 +475,7 @@ describe("assistant MCP discovery runtime bridge", () => {
expect(userFacing).not.toContain("MCP discovery pilot");
});
it("marks exact business-overview proof gaps as route enablement instead of reviewed execution", async () => {
it("promotes inventory reserve boundary after reviewed quality-event route executes", async () => {
const deps = buildSequentialDeps([
{ rows: [{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Client A" }] },
{ rows: [{ Period: "2020-01-20T00:00:00", Amount: 50000, Counterparty: "Supplier A" }] },
@ -548,20 +548,21 @@ describe("assistant MCP discovery runtime bridge", () => {
expect(result.bridge_status).toBe("answer_draft_ready");
expect(result.business_fact_answer_allowed).toBe(true);
expect(result.route_candidate).toMatchObject({
candidate_status: "needs_route_enablement",
candidate_status: "ready_for_reviewed_execution",
selected_chain_id: "business_overview",
business_fact_family: "business_overview",
action_family: "inventory_reserve_boundary",
executable_now: false
executable_now: true,
enablement_reason: null
});
expect(result.route_candidate.enablement_reason).toContain("inventory_reserve_liquidation_quality");
expect(result.route_candidate.enablement_reason).toContain(
"reviewed_inventory_quality_route_with_reserves_writeoffs_obsolescence_and_liquidation_value"
expect(result.pilot.derived_business_overview?.inventory_quality_events?.evidence_status).toBe(
"reviewed_no_quality_events_found"
);
expect(result.route_candidate.forbidden_overclaim_flags).toContain(
"confirmed_obsolete_stock_reserve_writeoff_or_liquidation_value"
expect(result.pilot.derived_business_overview?.missing_proof_families.map((item) => item.family)).not.toContain(
"inventory_reserve_liquidation_quality"
);
expect(result.reason_codes).toContain("runtime_bridge_route_candidate_needs_route_enablement");
expect(result.answer_draft.reason_codes).toContain("answer_contains_business_overview_inventory_quality_events");
expect(result.reason_codes).toContain("runtime_bridge_route_candidate_ready_for_reviewed_execution");
});
it("promotes profit-margin boundary when accounting 90/91/99 proof is available", async () => {

View File

@ -1,4 +1,47 @@
[
{
"generation_id": "gen-ag05122057-c9786e",
"created_at": "2026-05-12T20:57:28+00:00",
"mode": "saved_user_sessions",
"title": "AGENT | Phase 96 inventory reserve/liquidation quality-events",
"count": 2,
"domain": "address_phase96_inventory_reserve_liquidation_quality",
"questions": [
"По ООО Альтернатива Плюс за 2020 год проверь склад: были ли списания товаров, оприходования/корректировки, инвентаризации, переоценки, резервы под неликвиды или ликвидационная стоимость? Скажи коротко и честно, что подтверждено 1С, а что нельзя утверждать.",
"А по этим же данным можно сказать, что склад ликвидный и неликвидов нет?"
],
"generated_by": "Codex",
"saved_case_set_file": "assistant_autogen_saved_user_sessions_20260512205728_gen-ag05122057-c9786e.json",
"context": {
"llm_provider": null,
"model": null,
"assistant_prompt_version": null,
"decomposition_prompt_version": null,
"prompt_fingerprint": null,
"autogen_personality_id": null,
"autogen_personality_prompt": null,
"source_session_id": null,
"saved_session_file": "assistant_saved_session_20260512205728_gen-ag05122057-c9786e.json",
"saved_case_set_kind": "agent_semantic_scenario",
"agent_run": true,
"agent_focus": "inventory_reserve_liquidation_quality",
"architecture_phase": "Route-Candidate-Driven Enablement Loop",
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\address_truth_harness_phase96_inventory_reserve_liquidation_quality.json",
"scenario_id": "address_truth_harness_phase96_inventory_reserve_liquidation_quality",
"semantic_tags": [
"direct_answer_first",
"followup_context_carryover",
"inventory_liquidity_boundary",
"inventory_reserve_liquidation_quality",
"no_inventory_health_overclaim",
"no_market_liquidation_overclaim",
"reviewed_inventory_quality_events"
],
"validation_status": "accepted_live_replay",
"validated_run_dir": "artifacts\\domain_runs\\phase96_inventory_reserve_liquidation_quality_rerun",
"saved_after_validated_replay": true
}
},
{
"generation_id": "gen-ag05121628-50ea6c",
"created_at": "2026-05-12T16:28:41+00:00",

View File

@ -0,0 +1,109 @@
{
"saved_at": "2026-05-12T20:57:28+00:00",
"generation_id": "gen-ag05122057-c9786e",
"mode": "saved_user_sessions",
"title": "AGENT | Phase 96 inventory reserve/liquidation quality-events",
"agent_run": true,
"questions": [
"По ООО Альтернатива Плюс за 2020 год проверь склад: были ли списания товаров, оприходования/корректировки, инвентаризации, переоценки, резервы под неликвиды или ликвидационная стоимость? Скажи коротко и честно, что подтверждено 1С, а что нельзя утверждать.",
"А по этим же данным можно сказать, что склад ликвидный и неликвидов нет?"
],
"metadata": {
"assistant_prompt_version": null,
"decomposition_prompt_version": null,
"prompt_fingerprint": null,
"agent_focus": "inventory_reserve_liquidation_quality",
"architecture_phase": "Route-Candidate-Driven Enablement Loop",
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\address_truth_harness_phase96_inventory_reserve_liquidation_quality.json",
"scenario_id": "address_truth_harness_phase96_inventory_reserve_liquidation_quality",
"semantic_tags": [
"direct_answer_first",
"followup_context_carryover",
"inventory_liquidity_boundary",
"inventory_reserve_liquidation_quality",
"no_inventory_health_overclaim",
"no_market_liquidation_overclaim",
"reviewed_inventory_quality_events"
],
"validation_status": "accepted_live_replay",
"validated_run_dir": "artifacts\\domain_runs\\phase96_inventory_reserve_liquidation_quality_rerun",
"saved_after_validated_replay": true,
"save_gate": {
"schema_version": "agent_semantic_save_gate_v1",
"validation_status": "accepted_live_replay",
"validated_run_dir": "artifacts\\domain_runs\\phase96_inventory_reserve_liquidation_quality_rerun",
"final_status": "accepted",
"review_overall_status": "pass",
"business_overall_status": "pass",
"steps_total": 2,
"steps_passed": 2,
"steps_failed": 0,
"steps_with_business_failures": 0,
"steps_with_business_warnings": 0,
"acceptance_gate_passed": true,
"saved_after_validated_replay": true
}
},
"source_session_id": null,
"session": {
"session_id": null,
"mode": "agent_semantic_run",
"items": [
{
"message_id": "agent-user-001",
"role": "user",
"text": "По ООО Альтернатива Плюс за 2020 год проверь склад: были ли списания товаров, оприходования/корректировки, инвентаризации, переоценки, резервы под неликвиды или ликвидационная стоимость? Скажи коротко и честно, что подтверждено 1С, а что нельзя утверждать.",
"created_at": "2026-05-12T20:57:28+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-002",
"role": "user",
"text": "А по этим же данным можно сказать, что склад ликвидный и неликвидов нет?",
"created_at": "2026-05-12T20:57:28+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
}
],
"agent_run": true,
"metadata": {
"assistant_prompt_version": null,
"decomposition_prompt_version": null,
"prompt_fingerprint": null,
"agent_focus": "inventory_reserve_liquidation_quality",
"architecture_phase": "Route-Candidate-Driven Enablement Loop",
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\address_truth_harness_phase96_inventory_reserve_liquidation_quality.json",
"scenario_id": "address_truth_harness_phase96_inventory_reserve_liquidation_quality",
"semantic_tags": [
"direct_answer_first",
"followup_context_carryover",
"inventory_liquidity_boundary",
"inventory_reserve_liquidation_quality",
"no_inventory_health_overclaim",
"no_market_liquidation_overclaim",
"reviewed_inventory_quality_events"
],
"validation_status": "accepted_live_replay",
"validated_run_dir": "artifacts\\domain_runs\\phase96_inventory_reserve_liquidation_quality_rerun",
"saved_after_validated_replay": true,
"save_gate": {
"schema_version": "agent_semantic_save_gate_v1",
"validation_status": "accepted_live_replay",
"validated_run_dir": "artifacts\\domain_runs\\phase96_inventory_reserve_liquidation_quality_rerun",
"final_status": "accepted",
"review_overall_status": "pass",
"business_overall_status": "pass",
"steps_total": 2,
"steps_passed": 2,
"steps_failed": 0,
"steps_with_business_failures": 0,
"steps_with_business_warnings": 0,
"acceptance_gate_passed": true,
"saved_after_validated_replay": true
}
}
}
}

View File

@ -0,0 +1,31 @@
{
"suite_id": "assistant_saved_session_gen-ag05122057-c9786e",
"suite_version": "0.1.0",
"schema_version": "assistant_saved_session_suite_v0_1",
"generated_at": "2026-05-12T20:57:28+00:00",
"generation_id": "gen-ag05122057-c9786e",
"mode": "saved_user_sessions",
"title": "AGENT | Phase 96 inventory reserve/liquidation quality-events",
"domain": "address_phase96_inventory_reserve_liquidation_quality",
"scenario_count": 1,
"case_ids": [
"SAVED-001"
],
"cases": [
{
"case_id": "SAVED-001",
"scenario_tag": "agent_saved_user_sessions",
"title": "AGENT | Phase 96 inventory reserve/liquidation quality-events",
"question_type": "followup",
"broadness_level": "medium",
"turns": [
{
"user_message": "По ООО Альтернатива Плюс за 2020 год проверь склад: были ли списания товаров, оприходования/корректировки, инвентаризации, переоценки, резервы под неликвиды или ликвидационная стоимость? Скажи коротко и честно, что подтверждено 1С, а что нельзя утверждать."
},
{
"user_message": "А по этим же данным можно сказать, что склад ликвидный и неликвидов нет?"
}
]
}
]
}