Закрыть 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: `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: `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. - 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`. - 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 accepted autorun: `AGENT | Phase 95 vendor/procurement quality reviewed route` (`gen-ag05121357-9ea5d6`). - 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)`. - 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)`. - Active module progress: `~99% (Agentic Semantic Development Loop, accepted dogfood loop + autorun hygiene; manual GUI confirmation still required)`.
## Reporting Rule ## 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% (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. - `Прогресс модуля: 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. - `Прогресс модуля: 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. - `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. - `Прогресс модуля: 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; - 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; - 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; - 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; - broader dynamic schema traversal for unfamiliar 1C asks;
- more primitive descriptors where live evidence proves a real gap; - 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; - 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` 1. `README.md`
2. this document 2. this document
3. `29 - debt_due_date_aging_reviewed_route_2026-05-10.md` 3. `31 - inventory_reserve_liquidation_quality_reviewed_route_2026-05-12.md`
4. `28 - accounting_profit_margin_reviewed_route_2026-05-10.md` 4. `30 - vendor_procurement_quality_reviewed_route_2026-05-12.md`
5. `27 - proof_family_enablement_candidates_2026-05-10.md` 5. `29 - debt_due_date_aging_reviewed_route_2026-05-10.md`
6. `26 - route_candidate_driven_enablement_loop_2026-05-10.md` 6. `28 - accounting_profit_margin_reviewed_route_2026-05-10.md`
7. `25 - open_world_route_candidate_promotion_2026-05-10.md` 7. `27 - proof_family_enablement_candidates_2026-05-10.md`
8. `24 - agentic_semantic_development_loop_and_autorun_hygiene_2026-05-10.md` 8. `26 - route_candidate_driven_enablement_loop_2026-05-10.md`
9. `23 - current_execution_spine_and_semantic_control_gate_2026-05-05.md` 9. `25 - open_world_route_candidate_promotion_2026-05-10.md`
10. `22 - open_world_bounded_autonomy_breadth_2026-05-01.md` 10. `24 - agentic_semantic_development_loop_and_autorun_hygiene_2026-05-10.md`
11. `20 - planner_autonomy_consolidation_2026-05-01.md` 11. `23 - current_execution_spine_and_semantic_control_gate_2026-05-05.md`
12. `19 - inventory_stock_open_world_breadth_proof_2026-05-01.md` 12. `22 - open_world_bounded_autonomy_breadth_2026-05-01.md`
13. `17 - post_f_semantic_integrity_hardening_2026-04-23.md` 13. `20 - planner_autonomy_consolidation_2026-05-01.md`
14. `16 - data_need_graph_and_open_world_mcp_plan_2026-04-22.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. 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; - 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; - 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; - 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. - risk: medium, because the loop is now infrastructure for future acceptance decisions, not just a local route fix.
Recommended reporting line: Recommended reporting line:
@ -113,16 +113,16 @@ Still open:
- the first accepted dogfood loop proves the mechanism, not all future stage packs; - 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; - 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; - 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. - manual GUI confirmation remains required before declaring a fat AGENT pack fully accepted.
## Next Work ## Next Work
Next operational pass: 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. 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. 4. Keep Post-F, phase83, inventory, business-overview, and mojibake autorun cases as regression canaries.
## Canonical Reading Order Update ## Canonical Reading Order Update
@ -131,13 +131,15 @@ For current planning, read:
1. `README.md` 1. `README.md`
2. `21 - current_status_canon_2026-05-01.md` 2. `21 - current_status_canon_2026-05-01.md`
3. `29 - debt_due_date_aging_reviewed_route_2026-05-10.md` 3. `31 - inventory_reserve_liquidation_quality_reviewed_route_2026-05-12.md`
4. `28 - accounting_profit_margin_reviewed_route_2026-05-10.md` 4. `30 - vendor_procurement_quality_reviewed_route_2026-05-12.md`
5. `27 - proof_family_enablement_candidates_2026-05-10.md` 5. `29 - debt_due_date_aging_reviewed_route_2026-05-10.md`
6. `26 - route_candidate_driven_enablement_loop_2026-05-10.md` 6. `28 - accounting_profit_margin_reviewed_route_2026-05-10.md`
7. `25 - open_world_route_candidate_promotion_2026-05-10.md` 7. `27 - proof_family_enablement_candidates_2026-05-10.md`
8. this document 8. `26 - route_candidate_driven_enablement_loop_2026-05-10.md`
9. `23 - current_execution_spine_and_semantic_control_gate_2026-05-05.md` 9. `25 - open_world_route_candidate_promotion_2026-05-10.md`
10. `22 - open_world_bounded_autonomy_breadth_2026-05-01.md` 10. this document
11. `20 - planner_autonomy_consolidation_2026-05-01.md` 11. `23 - current_execution_spine_and_semantic_control_gate_2026-05-05.md`
12. `17 - post_f_semantic_integrity_hardening_2026-04-23.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`). - 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. - 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`). - 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: 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 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 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 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 ## Status
Current module wording: 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 Work
Next slices: Next slices:
1. Pick the final phase92 proof family: `inventory_reserve_liquidation_quality`. 1. Treat this module as closed and keep phase91/92/93/94/95/96 as regression canaries.
2. Implement or explicitly bound that final family only if reliable 1C evidence is reachable. 2. Start the next broader open-world autonomy slice: schema/primitive discovery beyond the selected phase92 proof families.
3. Rerun phase95 as a canary plus the focused inventory route-specific pack. 3. Keep save-after-acceptance discipline for any new AGENT packs.
4. Save the accepted pack into autoruns only after live replay and semantic review pass.
See also: 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) - [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) - [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) - [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: 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 Work
Next slices: Next slices:
1. Wire or explicitly bound the final remaining family: `inventory_reserve_liquidation_quality`. 1. Use phase91-phase96 as regression canaries.
2. Keep proxy-only inventory reserve/liquidation wording bounded until reviewed evidence exists. 2. Start the next broader open-world schema/primitive discovery module.
3. Rerun phase95 as a canary plus the focused inventory route-specific pack. 3. Keep saving AGENT autoruns only after live replay and semantic review pass.
4. Save the next AGENT autorun only after live replay and semantic review pass.
See also: See also:
- [28 - accounting_profit_margin_reviewed_route_2026-05-10.md](./28%20-%20accounting_profit_margin_reviewed_route_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) - [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) - [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: 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 Work
Next slices: Next slices:
1. Select the remaining proof family: `inventory_reserve_liquidation_quality`. 1. Use this route as a regression canary during broader autonomy work.
2. Wire only the smallest reliable reviewed route, not a broad heuristic. 2. Continue the next module through open-world schema/primitive discovery rather than more phase92 proof-family closure.
3. Keep proxy-only reserve/liquidation wording bounded until the route is accepted. 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: 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 Work
Next slices: Next slices:
1. Identify whether reliable 1C evidence exists for `inventory_reserve_liquidation_quality`. 1. Use phase94, phase95, and phase96 as canaries for due-date, vendor, and inventory-quality continuity.
2. Wire the smallest reviewed route only if it can prove the inventory business claim without overreach. 2. Move the active plan to broader open-world schema/primitive discovery.
3. Keep proxy-only evidence bounded if exact proof is not reachable. 3. Keep proxy-only evidence bounded when a reviewed route is not available.
4. Rerun phase95 as a canary plus the focused inventory route-specific pack.

View File

@ -62,29 +62,29 @@ Semantic/live replay:
Current module wording: 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; - `accounting_profit_margin` accepted live by phase93;
- `debt_due_date_aging_quality` accepted live by phase94; - `debt_due_date_aging_quality` accepted live by phase94;
- `vendor_risk_procurement_quality` accepted live through procurement-concentration evidence by phase95. - `vendor_risk_procurement_quality` accepted live through procurement-concentration evidence by phase95.
- `inventory_reserve_liquidation_quality` accepted live through inventory quality-event evidence by phase96.
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.
## Next Work ## Next Work
Next slices: Next slices:
1. Select the final remaining phase92 family: `inventory_reserve_liquidation_quality`. 1. Use phase95 as a vendor/procurement regression canary during broader autonomy work.
2. Determine whether reachable 1C evidence can prove reserve/write-off/liquidation quality without overreach. 2. See phase96 for the final inventory reserve/liquidation route and module closure.
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.
See also: See also:
- [26 - route_candidate_driven_enablement_loop_2026-05-10.md](./26%20-%20route_candidate_driven_enablement_loop_2026-05-10.md) - [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) - [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) - [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) 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) 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) 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. 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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: 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%` - pre-multidomain readiness: `~90%`
- bounded-autonomy foundation readiness: `~89%` - bounded-autonomy foundation readiness: `~89%`
- open-world bounded-autonomy readiness: `~87%` - 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 - 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 - 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 - 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 - 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 - 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` - graph snapshot after latest rebuild: see `graphify-out/GRAPH_REPORT.md`
- current regression-gate breakpoint: - current regression-gate breakpoint:
- the validated hot paths are no longer structurally broken; - 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`). - 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`). - 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`). - 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: Current architectural reading:
@ -351,6 +356,7 @@ Read in this order:
29. `28 - accounting_profit_margin_reviewed_route_2026-05-10.md` 29. `28 - accounting_profit_margin_reviewed_route_2026-05-10.md`
30. `29 - debt_due_date_aging_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` 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 ## 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__ Количество __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 = ` const BANK_DOCS_QUERY_TEMPLATE = `
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__ ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
БанкСписание.Дата КАК Период, БанкСписание.Дата КАК Период,
@ -933,6 +988,16 @@ const BASE_RECIPES = [
account_scope_mode: "strict", account_scope_mode: "strict",
query_template: "inventory_aging_by_purchase_date_profile" 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", recipe_id: "address_open_contracts_confirmed_as_of_date_v1",
intent: "open_contracts_confirmed_as_of_date", 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)))) .replace("__WHERE_CLAUSE__", buildWhereClause(filters, "Движения.Период", [inventoryCondition, itemCondition].filter((item) => Boolean(item))))
.replaceAll("__ORDER_DIRECTION__", resolveOrderDirection(filters.sort)); .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) { function buildInventoryItemReferenceCondition(filters, fieldPaths) {
const item = typeof filters.item === "string" ? filters.item.trim() : ""; const item = typeof filters.item === "string" ? filters.item.trim() : "";
if (!item) { if (!item) {
@ -1599,6 +1706,7 @@ function maxLimitForIntent(intent) {
intent === "inventory_profitability_for_item" || intent === "inventory_profitability_for_item" ||
intent === "inventory_purchase_to_sale_chain" || intent === "inventory_purchase_to_sale_chain" ||
intent === "inventory_aging_by_purchase_date" || intent === "inventory_aging_by_purchase_date" ||
intent === "inventory_quality_events_for_organization" ||
intent === "open_contracts_confirmed_as_of_date" || intent === "open_contracts_confirmed_as_of_date" ||
intent === "list_contracts_by_counterparty" || intent === "list_contracts_by_counterparty" ||
intent === "list_documents_by_counterparty" || intent === "list_documents_by_counterparty" ||
@ -1790,6 +1898,8 @@ function buildAddressRecipePlan(recipe, filters) {
? buildInventoryPurchaseToSaleDocumentQuery(filters, resolvedLimit) ? buildInventoryPurchaseToSaleDocumentQuery(filters, resolvedLimit)
: recipe.query_template === "inventory_aging_by_purchase_date_profile" : recipe.query_template === "inventory_aging_by_purchase_date_profile"
? buildInventoryMovementQuery(filters, resolvedLimit, "dt") ? buildInventoryMovementQuery(filters, resolvedLimit, "dt")
: recipe.query_template === "inventory_quality_events_profile"
? buildInventoryQualityEventsQuery(filters, resolvedLimit)
: recipe.query_template === "contracts_by_counterparty_profile" : recipe.query_template === "contracts_by_counterparty_profile"
? CONTRACTS_BY_COUNTERPARTY_QUERY_TEMPLATE.replaceAll("__LIMIT__", String(resolvedLimit)) ? CONTRACTS_BY_COUNTERPARTY_QUERY_TEMPLATE.replaceAll("__LIMIT__", String(resolvedLimit))
: recipe.query_template === "open_contracts_confirmed_as_of_balance_profile" : recipe.query_template === "open_contracts_confirmed_as_of_balance_profile"

View File

@ -393,6 +393,9 @@ function isVendorRiskBoundaryTurn(pilot) {
return action === "vendor_risk_procurement_boundary" || unsupported === "vendor_risk_procurement_boundary"; return action === "vendor_risk_procurement_boundary" || unsupported === "vendor_risk_procurement_boundary";
} }
function businessOverviewInventoryUnknownLabel(overview) { function businessOverviewInventoryUnknownLabel(overview) {
if (overview.inventory_quality_events) {
return "рыночная ликвидационная стоимость и управленческий резерв склада";
}
if (overview.inventory_staleness_risk_proxy) { if (overview.inventory_staleness_risk_proxy) {
return "резервы/списания/ликвидационная стоимость склада"; return "резервы/списания/ликвидационная стоимость склада";
} }
@ -621,6 +624,24 @@ function businessOverviewVendorProcurementQualityText(overview) {
} }
return `Procurement-concentration route за ${period} отработал по исходящим платежам на ${total}, но надежной небанковской концентрации поставщика по найденным строкам не хватает.${contractText} Полный vendor-risk аудит не подтвержден.`; 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) { function headlineFor(mode, pilot) {
const askedMonthlyBreakdown = pilot.derived_bidirectional_value_flow?.aggregation_axis === "month" || const askedMonthlyBreakdown = pilot.derived_bidirectional_value_flow?.aggregation_axis === "month" ||
pilot.derived_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 маршрута по договорам, срокам оплаты и погашению расчетов."; return "Нельзя точно определить, какая дебиторка просрочена, по текущему срезу 1С; есть только debt-quality proxy, но нет проверенного due-date маршрута по договорам, срокам оплаты и погашению расчетов.";
} }
if (isInventoryReserveBoundaryTurn(pilot)) { if (isInventoryReserveBoundaryTurn(pilot)) {
const inventoryQualityEventsText = businessOverviewInventoryQualityEventsText(overview);
if (inventoryQualityEventsText) {
return inventoryQualityEventsText;
}
const inventoryBasis = overview.inventory_staleness_risk_proxy const inventoryBasis = overview.inventory_staleness_risk_proxy
? "есть только складской staleness-risk proxy по найденным строкам" ? "есть только складской staleness-risk proxy по найденным строкам"
: overview.inventory_position || overview.inventory_turnover_proxy : overview.inventory_position || overview.inventory_turnover_proxy
@ -724,6 +749,9 @@ function headlineFor(mode, pilot) {
if (overview.inventory_staleness_risk_proxy) { if (overview.inventory_staleness_risk_proxy) {
families.push("staleness risk proxy склада"); families.push("staleness risk proxy склада");
} }
if (overview.inventory_quality_events) {
families.push("складские quality-события");
}
const unknownFamilies = overview.accounting_financial_result const unknownFamilies = overview.accounting_financial_result
? ["аудированная/юридически подтвержденная прибыль"] ? ["аудированная/юридически подтвержденная прибыль"]
: [overview.trading_margin_proxy ? "чистая прибыль/точная маржа" : "прибыль/маржа"]; : [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 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 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."); 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) || if (pilot.derived_business_overview?.top_customers?.some(isFinancialInstitutionBucket) ||
pilot.derived_business_overview?.top_suppliers?.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."); 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; 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)}. Это не подтвержденная неликвидность, не резерв и не ликвидационная стоимость.`); 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; return lines;
} }
function businessOverviewCashSynthesisLine(overview) { function businessOverviewCashSynthesisLine(overview) {
@ -1603,6 +1638,15 @@ function businessOverviewRiskSynthesisLine(overview) {
if (overview.inventory_staleness_risk_proxy) { 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} дн.`); 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 return signals.length > 0
? `Риски и контуры внимания по подтвержденным данным: ${signals.join("; ")}.` ? `Риски и контуры внимания по подтвержденным данным: ${signals.join("; ")}.`
: null; : null;
@ -1616,7 +1660,8 @@ function businessOverviewExecutiveVerdictLine(overview) {
overview.debt_staleness_risk_proxy || overview.debt_staleness_risk_proxy ||
overview.inventory_position || overview.inventory_position ||
overview.inventory_turnover_proxy || 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 hasOperationalProfileSignal = Boolean(overview.document_activity_profile || overview.counterparty_profile || overview.contract_usage_profile);
const hasExtraSignals = hasTaxDebtInventorySignals || hasOperationalProfileSignal; const hasExtraSignals = hasTaxDebtInventorySignals || hasOperationalProfileSignal;
if (!hasCash && !hasExtraSignals) { if (!hasCash && !hasExtraSignals) {
@ -1750,6 +1795,10 @@ function buildAssistantMcpDiscoveryAnswerDraft(pilot) {
if (pilot.derived_business_overview?.inventory_staleness_risk_proxy) { if (pilot.derived_business_overview?.inventory_staleness_risk_proxy) {
pushReason(reasonCodes, "answer_contains_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) { if (pilot.derived_business_overview?.missing_proof_families?.length) {
pushReason(reasonCodes, "answer_contains_business_overview_missing_proof_ledger"); pushReason(reasonCodes, "answer_contains_business_overview_missing_proof_ledger");
} }

View File

@ -211,6 +211,16 @@ function shouldRunDebtDueDateAgingProbe(planner) {
.join(" "); .join(" ");
return /(?:debt_due_date_boundary|due[-_ ]?date|overdue|aging|просроч|срок\s+оплат|дебиторк|кредиторск)/iu.test(combined); 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) { function buildBusinessOverviewInventoryFilters(planner) {
const meaning = planner.discovery_plan.turn_meaning_ref; const meaning = planner.discovery_plan.turn_meaning_ref;
const organization = toNonEmptyString(meaning?.explicit_organization_scope); 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" 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) { function deriveBusinessOverviewVendorProcurementQuality(input) {
if (!input.rankedOutgoing || if (!input.rankedOutgoing ||
input.rankedOutgoing.ranked_values.length <= 0 || 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" 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_turnover_quality") ||
missing.has("inventory_liquidity_quality") || missing.has("inventory_liquidity_quality") ||
missing.has("inventory_reserve_liquidation_quality")) { missing.has("inventory_reserve_liquidation_quality")) && !input.inventoryQualityEvents) {
pushUnique({ pushUnique({
family: "inventory_reserve_liquidation_quality", family: "inventory_reserve_liquidation_quality",
current_status: input.inventoryStalenessRiskProxy current_status: input.inventoryStalenessRiskProxy
@ -3195,6 +3268,10 @@ function deriveBusinessOverview(input) {
inventoryPosition, inventoryPosition,
inventoryTurnoverProxy inventoryTurnoverProxy
}); });
const inventoryQualityEvents = deriveBusinessOverviewInventoryQualityEvents({
inventoryQualityEventsResult: input.inventoryQualityEventsResult,
periodScope: input.periodScope
});
const vendorProcurementQuality = deriveBusinessOverviewVendorProcurementQuality({ const vendorProcurementQuality = deriveBusinessOverviewVendorProcurementQuality({
rankedOutgoing, rankedOutgoing,
outgoing, outgoing,
@ -3219,6 +3296,7 @@ function deriveBusinessOverview(input) {
Boolean(inventoryPosition), Boolean(inventoryPosition),
Boolean(inventoryTurnoverProxy), Boolean(inventoryTurnoverProxy),
Boolean(inventoryStalenessRiskProxy), Boolean(inventoryStalenessRiskProxy),
Boolean(inventoryQualityEvents),
Boolean(vendorProcurementQuality) Boolean(vendorProcurementQuality)
].filter(Boolean).length; ].filter(Boolean).length;
if (checkedSignalCount <= 0) { if (checkedSignalCount <= 0) {
@ -3232,7 +3310,9 @@ function deriveBusinessOverview(input) {
debtDueDateAging ? null : debtOpenSettlementQuality ? "debt_due_date_aging_quality" : "debt_open_settlement_quality", debtDueDateAging ? null : debtOpenSettlementQuality ? "debt_due_date_aging_quality" : "debt_open_settlement_quality",
taxPosition ? null : "tax_position", taxPosition ? null : "tax_position",
inventoryPosition inventoryPosition
? inventoryStalenessRiskProxy ? inventoryQualityEvents
? null
: inventoryStalenessRiskProxy
? "inventory_reserve_liquidation_quality" ? "inventory_reserve_liquidation_quality"
: inventoryTurnoverProxy : inventoryTurnoverProxy
? "inventory_liquidity_quality" ? "inventory_liquidity_quality"
@ -3250,6 +3330,7 @@ function deriveBusinessOverview(input) {
inventoryPosition, inventoryPosition,
inventoryTurnoverProxy, inventoryTurnoverProxy,
inventoryStalenessRiskProxy, inventoryStalenessRiskProxy,
inventoryQualityEvents,
vendorProcurementQuality, vendorProcurementQuality,
hasSupplierConcentrationSignal: (rankedOutgoing?.ranked_values.length ?? 0) > 0 hasSupplierConcentrationSignal: (rankedOutgoing?.ranked_values.length ?? 0) > 0
}); });
@ -3275,6 +3356,7 @@ function deriveBusinessOverview(input) {
inventory_position: inventoryPosition, inventory_position: inventoryPosition,
inventory_turnover_proxy: inventoryTurnoverProxy, inventory_turnover_proxy: inventoryTurnoverProxy,
inventory_staleness_risk_proxy: inventoryStalenessRiskProxy, inventory_staleness_risk_proxy: inventoryStalenessRiskProxy,
inventory_quality_events: inventoryQualityEvents,
document_activity_profile: documentActivityProfile, document_activity_profile: documentActivityProfile,
counterparty_profile: counterpartyProfile, counterparty_profile: counterpartyProfile,
contract_usage_profile: contractUsageProfile, contract_usage_profile: contractUsageProfile,
@ -3283,7 +3365,7 @@ function deriveBusinessOverview(input) {
checked_signal_count: checkedSignalCount, checked_signal_count: checkedSignalCount,
missing_signal_families: missingSignalFamilies, missing_signal_families: missingSignalFamilies,
missing_proof_families: missingProofFamilies, missing_proof_families: missingProofFamilies,
inference_basis: hasBusinessOverviewProfileSignal || inventoryPosition || accountingFinancialResult inference_basis: hasBusinessOverviewProfileSignal || inventoryPosition || inventoryQualityEvents || accountingFinancialResult
? "business_overview_from_confirmed_1c_multi_family_rows" ? "business_overview_from_confirmed_1c_multi_family_rows"
: debtOpenSettlementQuality || debtDueDateAging : debtOpenSettlementQuality || debtDueDateAging
? "business_overview_from_confirmed_1c_multi_family_rows" ? "business_overview_from_confirmed_1c_multi_family_rows"
@ -3343,6 +3425,9 @@ function summarizeBusinessOverviewRows(input) {
if (input.inventoryAgingResult && !input.inventoryAgingResult.error) { if (input.inventoryAgingResult && !input.inventoryAgingResult.error) {
parts.push(`${input.inventoryAgingResult.fetched_rows} inventory aging rows fetched, ${input.inventoryAgingResult.matched_rows} matched`); 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; return parts.length > 0 ? parts.join("; ") : null;
} }
function buildBusinessOverviewConfirmedFacts(derived) { function buildBusinessOverviewConfirmedFacts(derived) {
@ -3511,6 +3596,18 @@ function buildBusinessOverviewConfirmedFacts(derived) {
const proxy = derived.inventory_staleness_risk_proxy; 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)}. Это не подтвержденная неликвидность, не резерв и не ликвидационная стоимость.`); 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; return facts;
} }
function buildBusinessOverviewInferredFacts(derived) { function buildBusinessOverviewInferredFacts(derived) {
@ -4218,6 +4315,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
let contractUsageProfileResult = null; let contractUsageProfileResult = null;
let inventoryOnHandResult = null; let inventoryOnHandResult = null;
let inventoryAgingResult = null; let inventoryAgingResult = null;
let inventoryQualityEventsResult = null;
const valueFilters = buildValueFlowFilters(planner); const valueFilters = buildValueFlowFilters(planner);
const lifecycleFilters = buildLifecycleFilters(planner); const lifecycleFilters = buildLifecycleFilters(planner);
const profileFilters = buildBusinessOverviewProfileFilters(planner); const profileFilters = buildBusinessOverviewProfileFilters(planner);
@ -4227,6 +4325,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
const debtFilters = buildBusinessOverviewDebtFilters(planner); const debtFilters = buildBusinessOverviewDebtFilters(planner);
const debtDueDateAgingProbeEnabled = shouldRunDebtDueDateAgingProbe(planner); const debtDueDateAgingProbeEnabled = shouldRunDebtDueDateAgingProbe(planner);
const inventoryFilters = buildBusinessOverviewInventoryFilters(planner); const inventoryFilters = buildBusinessOverviewInventoryFilters(planner);
const inventoryQualityEventsProbeEnabled = shouldRunInventoryQualityEventsProbe(planner);
const debtAsOfDate = toNonEmptyString(debtFilters?.as_of_date); const debtAsOfDate = toNonEmptyString(debtFilters?.as_of_date);
const inventoryAsOfDate = toNonEmptyString(inventoryFilters?.as_of_date); const inventoryAsOfDate = toNonEmptyString(inventoryFilters?.as_of_date);
const incomingSelection = (0, addressRecipeCatalog_1.selectAddressRecipe)("customer_revenue_and_payments", valueFilters); 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 const inventoryAgingSelection = inventoryFilters
? (0, addressRecipeCatalog_1.selectAddressRecipe)("inventory_aging_by_purchase_date", inventoryFilters) ? (0, addressRecipeCatalog_1.selectAddressRecipe)("inventory_aging_by_purchase_date", inventoryFilters)
: null; : 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) { if (!incomingSelection.selected_recipe || !outgoingSelection.selected_recipe || !lifecycleSelection.selected_recipe) {
pushReason(reasonCodes, "pilot_business_overview_recipe_not_available"); pushReason(reasonCodes, "pilot_business_overview_recipe_not_available");
const missing = [ const missing = [
@ -4389,6 +4491,16 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
pushReason(reasonCodes, "pilot_business_overview_inventory_recipe_not_available"); 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"); 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) { for (const step of dryRun.execution_steps) {
if (step.primitive_id === "query_movements") { if (step.primitive_id === "query_movements") {
const incomingExecution = await executeCoverageAwareValueFlowQuery({ const incomingExecution = await executeCoverageAwareValueFlowQuery({
@ -4622,6 +4734,16 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
}); });
probeResults.push(queryResultToProbeResult(step.primitive_id, contractUsageProfileResult)); 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) { if (lifecycleResult.error) {
pushUnique(queryLimitations, lifecycleResult.error); pushUnique(queryLimitations, lifecycleResult.error);
pushReason(reasonCodes, "pilot_business_overview_query_documents_mcp_error"); pushReason(reasonCodes, "pilot_business_overview_query_documents_mcp_error");
@ -4657,6 +4779,13 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
else if (contractUsageProfileResult) { else if (contractUsageProfileResult) {
pushReason(reasonCodes, "pilot_business_overview_contract_usage_profile_query_mcp_executed"); 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; continue;
} }
skippedPrimitives.push(step.primitive_id); skippedPrimitives.push(step.primitive_id);
@ -4679,6 +4808,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
debtAsOfDate, debtAsOfDate,
inventoryOnHandResult, inventoryOnHandResult,
inventoryAgingResult, inventoryAgingResult,
inventoryQualityEventsResult,
inventoryAsOfDate, inventoryAsOfDate,
organizationScope, organizationScope,
periodScope: dateScope periodScope: dateScope
@ -4744,6 +4874,10 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
if (derivedBusinessOverview.inventory_staleness_risk_proxy) { if (derivedBusinessOverview.inventory_staleness_risk_proxy) {
pushReason(reasonCodes, "pilot_derived_business_overview_inventory_staleness_risk_proxy_from_confirmed_rows"); 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) { if (derivedBusinessOverview.missing_proof_families.length > 0) {
pushReason(reasonCodes, "pilot_business_overview_missing_proof_families_recorded"); pushReason(reasonCodes, "pilot_business_overview_missing_proof_families_recorded");
} }
@ -4763,7 +4897,8 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
counterpartyProfileResult, counterpartyProfileResult,
contractUsageProfileResult, contractUsageProfileResult,
inventoryOnHandResult, inventoryOnHandResult,
inventoryAgingResult inventoryAgingResult,
inventoryQualityEventsResult
}); });
const evidence = (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({ const evidence = (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({
plan: planner.discovery_plan, plan: planner.discovery_plan,

View File

@ -830,10 +830,18 @@ function buildCompactBusinessOverviewReply(entryPoint, draft) {
} }
if (inventoryReserveBoundary) { if (inventoryReserveBoundary) {
const headline = toNonEmptyString(draft.headline); const headline = toNonEmptyString(draft.headline);
const inventoryQualityEvents = toRecordObject(overview.inventory_quality_events);
const cleanHeadline = headline?.replace(/^Коротко:\s*/iu, "").trim(); const cleanHeadline = headline?.replace(/^Коротко:\s*/iu, "").trim();
lines.push(cleanHeadline lines.push(cleanHeadline
? `Коротко: ${localizeLine(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([ const boundaryLines = userFacingLines([
...toStringList(draft.unknown_lines), ...toStringList(draft.unknown_lines),
...toStringList(draft.limitation_lines) ...toStringList(draft.limitation_lines)

View File

@ -296,6 +296,62 @@ const INVENTORY_ON_HAND_AS_OF_QUERY_TEMPLATE = `
Количество __ORDER_DIRECTION__ Количество __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 = ` const BANK_DOCS_QUERY_TEMPLATE = `
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__ ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
БанкСписание.Дата КАК Период, БанкСписание.Дата КАК Период,
@ -958,6 +1014,16 @@ const BASE_RECIPES: AddressRecipeDefinition[] = [
account_scope_mode: "strict", account_scope_mode: "strict",
query_template: "inventory_aging_by_purchase_date_profile" 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", recipe_id: "address_open_contracts_confirmed_as_of_date_v1",
intent: "open_contracts_confirmed_as_of_date", intent: "open_contracts_confirmed_as_of_date",
@ -1432,6 +1498,77 @@ function buildInventoryMovementQuery(
.replaceAll("__ORDER_DIRECTION__", resolveOrderDirection(filters.sort)); .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 { function buildInventoryItemReferenceCondition(filters: AddressFilterSet, fieldPaths: string[]): string | null {
const item = typeof filters.item === "string" ? filters.item.trim() : ""; const item = typeof filters.item === "string" ? filters.item.trim() : "";
if (!item) { if (!item) {
@ -1783,6 +1920,7 @@ function maxLimitForIntent(intent: AddressIntent): number {
intent === "inventory_profitability_for_item" || intent === "inventory_profitability_for_item" ||
intent === "inventory_purchase_to_sale_chain" || intent === "inventory_purchase_to_sale_chain" ||
intent === "inventory_aging_by_purchase_date" || intent === "inventory_aging_by_purchase_date" ||
intent === "inventory_quality_events_for_organization" ||
intent === "open_contracts_confirmed_as_of_date" || intent === "open_contracts_confirmed_as_of_date" ||
intent === "list_contracts_by_counterparty" || intent === "list_contracts_by_counterparty" ||
intent === "list_documents_by_counterparty" || intent === "list_documents_by_counterparty" ||
@ -2037,6 +2175,8 @@ export function buildAddressRecipePlan(
? buildInventoryPurchaseToSaleDocumentQuery(filters, resolvedLimit) ? buildInventoryPurchaseToSaleDocumentQuery(filters, resolvedLimit)
: recipe.query_template === "inventory_aging_by_purchase_date_profile" : recipe.query_template === "inventory_aging_by_purchase_date_profile"
? buildInventoryMovementQuery(filters, resolvedLimit, "dt") ? buildInventoryMovementQuery(filters, resolvedLimit, "dt")
: recipe.query_template === "inventory_quality_events_profile"
? buildInventoryQualityEventsQuery(filters, resolvedLimit)
: recipe.query_template === "contracts_by_counterparty_profile" : recipe.query_template === "contracts_by_counterparty_profile"
? CONTRACTS_BY_COUNTERPARTY_QUERY_TEMPLATE.replaceAll("__LIMIT__", String(resolvedLimit)) ? CONTRACTS_BY_COUNTERPARTY_QUERY_TEMPLATE.replaceAll("__LIMIT__", String(resolvedLimit))
: recipe.query_template === "open_contracts_confirmed_as_of_balance_profile" : 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 { function businessOverviewInventoryUnknownLabel(overview: BusinessOverview): string {
if (overview.inventory_quality_events) {
return "рыночная ликвидационная стоимость и управленческий резерв склада";
}
if (overview.inventory_staleness_risk_proxy) { if (overview.inventory_staleness_risk_proxy) {
return "резервы/списания/ликвидационная стоимость склада"; return "резервы/списания/ликвидационная стоимость склада";
} }
@ -764,6 +767,26 @@ function businessOverviewVendorProcurementQualityText(overview: BusinessOverview
return `Procurement-concentration route за ${period} отработал по исходящим платежам на ${total}, но надежной небанковской концентрации поставщика по найденным строкам не хватает.${contractText} Полный vendor-risk аудит не подтвержден.`; 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 { function headlineFor(mode: AssistantMcpDiscoveryAnswerMode, pilot: AssistantMcpDiscoveryPilotExecutionContract): string {
const askedMonthlyBreakdown = const askedMonthlyBreakdown =
pilot.derived_bidirectional_value_flow?.aggregation_axis === "month" || 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 маршрута по договорам, срокам оплаты и погашению расчетов."; return "Нельзя точно определить, какая дебиторка просрочена, по текущему срезу 1С; есть только debt-quality proxy, но нет проверенного due-date маршрута по договорам, срокам оплаты и погашению расчетов.";
} }
if (isInventoryReserveBoundaryTurn(pilot)) { if (isInventoryReserveBoundaryTurn(pilot)) {
const inventoryQualityEventsText = businessOverviewInventoryQualityEventsText(overview);
if (inventoryQualityEventsText) {
return inventoryQualityEventsText;
}
const inventoryBasis = overview.inventory_staleness_risk_proxy const inventoryBasis = overview.inventory_staleness_risk_proxy
? "есть только складской staleness-risk proxy по найденным строкам" ? "есть только складской staleness-risk proxy по найденным строкам"
: overview.inventory_position || overview.inventory_turnover_proxy : overview.inventory_position || overview.inventory_turnover_proxy
@ -870,6 +897,9 @@ function headlineFor(mode: AssistantMcpDiscoveryAnswerMode, pilot: AssistantMcpD
if (overview.inventory_staleness_risk_proxy) { if (overview.inventory_staleness_risk_proxy) {
families.push("staleness risk proxy склада"); families.push("staleness risk proxy склада");
} }
if (overview.inventory_quality_events) {
families.push("складские quality-события");
}
const unknownFamilies = overview.accounting_financial_result const unknownFamilies = overview.accounting_financial_result
? ["аудированная/юридически подтвержденная прибыль"] ? ["аудированная/юридически подтвержденная прибыль"]
: [overview.trading_margin_proxy ? "чистая прибыль/точная маржа" : "прибыль/маржа"]; : [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 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 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."); 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 ( if (
pilot.derived_business_overview?.top_customers?.some(isFinancialInstitutionBucket) || pilot.derived_business_overview?.top_customers?.some(isFinancialInstitutionBucket) ||
pilot.derived_business_overview?.top_suppliers?.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)}. Это не подтвержденная неликвидность, не резерв и не ликвидационная стоимость.` `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; 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} дн.` `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 return signals.length > 0
? `Риски и контуры внимания по подтвержденным данным: ${signals.join("; ")}.` ? `Риски и контуры внимания по подтвержденным данным: ${signals.join("; ")}.`
: null; : null;
@ -1873,7 +1920,8 @@ function businessOverviewExecutiveVerdictLine(overview: BusinessOverview): strin
overview.debt_staleness_risk_proxy || overview.debt_staleness_risk_proxy ||
overview.inventory_position || overview.inventory_position ||
overview.inventory_turnover_proxy || overview.inventory_turnover_proxy ||
overview.inventory_staleness_risk_proxy overview.inventory_staleness_risk_proxy ||
overview.inventory_quality_events
); );
const hasOperationalProfileSignal = Boolean( const hasOperationalProfileSignal = Boolean(
overview.document_activity_profile || overview.counterparty_profile || overview.contract_usage_profile 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) { if (pilot.derived_business_overview?.inventory_staleness_risk_proxy) {
pushReason(reasonCodes, "answer_contains_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) { if (pilot.derived_business_overview?.missing_proof_families?.length) {
pushReason(reasonCodes, "answer_contains_business_overview_missing_proof_ledger"); pushReason(reasonCodes, "answer_contains_business_overview_missing_proof_ledger");
} }

View File

@ -265,6 +265,7 @@ export interface AssistantMcpDiscoveryDerivedBusinessOverview {
inventory_position: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryPosition | null; inventory_position: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryPosition | null;
inventory_turnover_proxy: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryTurnoverProxy | null; inventory_turnover_proxy: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryTurnoverProxy | null;
inventory_staleness_risk_proxy: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryStalenessRiskProxy | null; inventory_staleness_risk_proxy: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryStalenessRiskProxy | null;
inventory_quality_events: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryQualityEvents | null;
document_activity_profile: AssistantMcpDiscoveryDerivedBusinessOverviewDocumentActivityProfile | null; document_activity_profile: AssistantMcpDiscoveryDerivedBusinessOverviewDocumentActivityProfile | null;
counterparty_profile: AssistantMcpDiscoveryDerivedBusinessOverviewCounterpartyProfile | null; counterparty_profile: AssistantMcpDiscoveryDerivedBusinessOverviewCounterpartyProfile | null;
contract_usage_profile: AssistantMcpDiscoveryDerivedBusinessOverviewContractUsageProfile | 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"; 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 { export interface AssistantMcpDiscoveryDerivedMetadataSurface {
metadata_scope: string | null; metadata_scope: string | null;
requested_meta_types: string[]; 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); 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 { function buildBusinessOverviewInventoryFilters(planner: AssistantMcpDiscoveryPlannerContract): AddressFilterSet | null {
const meaning = planner.discovery_plan.turn_meaning_ref; const meaning = planner.discovery_plan.turn_meaning_ref;
const organization = toNonEmptyString(meaning?.explicit_organization_scope); 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: { function deriveBusinessOverviewVendorProcurementQuality(input: {
rankedOutgoing: AssistantMcpDiscoveryDerivedRankedValueFlow | null; rankedOutgoing: AssistantMcpDiscoveryDerivedRankedValueFlow | null;
outgoing: AssistantMcpDiscoveryValueFlowSideSummary; outgoing: AssistantMcpDiscoveryValueFlowSideSummary;
@ -4226,6 +4331,7 @@ function buildBusinessOverviewMissingProofFamilies(input: {
inventoryPosition: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryPosition | null; inventoryPosition: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryPosition | null;
inventoryTurnoverProxy: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryTurnoverProxy | null; inventoryTurnoverProxy: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryTurnoverProxy | null;
inventoryStalenessRiskProxy: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryStalenessRiskProxy | null; inventoryStalenessRiskProxy: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryStalenessRiskProxy | null;
inventoryQualityEvents: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryQualityEvents | null;
vendorProcurementQuality: AssistantMcpDiscoveryDerivedBusinessOverviewVendorProcurementQuality | null; vendorProcurementQuality: AssistantMcpDiscoveryDerivedBusinessOverviewVendorProcurementQuality | null;
hasSupplierConcentrationSignal: boolean; hasSupplierConcentrationSignal: boolean;
}): AssistantMcpDiscoveryBusinessOverviewMissingProofFamily[] { }): AssistantMcpDiscoveryBusinessOverviewMissingProofFamily[] {
@ -4267,12 +4373,12 @@ function buildBusinessOverviewMissingProofFamilies(input: {
}); });
} }
if ( if ((
missing.has("inventory_position") || missing.has("inventory_position") ||
missing.has("inventory_turnover_quality") || missing.has("inventory_turnover_quality") ||
missing.has("inventory_liquidity_quality") || missing.has("inventory_liquidity_quality") ||
missing.has("inventory_reserve_liquidation_quality") missing.has("inventory_reserve_liquidation_quality")
) { ) && !input.inventoryQualityEvents) {
pushUnique({ pushUnique({
family: "inventory_reserve_liquidation_quality", family: "inventory_reserve_liquidation_quality",
current_status: input.inventoryStalenessRiskProxy current_status: input.inventoryStalenessRiskProxy
@ -4322,6 +4428,7 @@ function deriveBusinessOverview(input: {
debtAsOfDate: string | null; debtAsOfDate: string | null;
inventoryOnHandResult: AddressMcpQueryExecutorResult | null; inventoryOnHandResult: AddressMcpQueryExecutorResult | null;
inventoryAgingResult: AddressMcpQueryExecutorResult | null; inventoryAgingResult: AddressMcpQueryExecutorResult | null;
inventoryQualityEventsResult: AddressMcpQueryExecutorResult | null;
inventoryAsOfDate: string | null; inventoryAsOfDate: string | null;
organizationScope: string | null; organizationScope: string | null;
periodScope: string | null; periodScope: string | null;
@ -4390,6 +4497,10 @@ function deriveBusinessOverview(input: {
inventoryPosition, inventoryPosition,
inventoryTurnoverProxy inventoryTurnoverProxy
}); });
const inventoryQualityEvents = deriveBusinessOverviewInventoryQualityEvents({
inventoryQualityEventsResult: input.inventoryQualityEventsResult,
periodScope: input.periodScope
});
const vendorProcurementQuality = deriveBusinessOverviewVendorProcurementQuality({ const vendorProcurementQuality = deriveBusinessOverviewVendorProcurementQuality({
rankedOutgoing, rankedOutgoing,
outgoing, outgoing,
@ -4414,6 +4525,7 @@ function deriveBusinessOverview(input: {
Boolean(inventoryPosition), Boolean(inventoryPosition),
Boolean(inventoryTurnoverProxy), Boolean(inventoryTurnoverProxy),
Boolean(inventoryStalenessRiskProxy), Boolean(inventoryStalenessRiskProxy),
Boolean(inventoryQualityEvents),
Boolean(vendorProcurementQuality) Boolean(vendorProcurementQuality)
].filter(Boolean).length; ].filter(Boolean).length;
if (checkedSignalCount <= 0) { if (checkedSignalCount <= 0) {
@ -4430,7 +4542,9 @@ function deriveBusinessOverview(input: {
debtDueDateAging ? null : debtOpenSettlementQuality ? "debt_due_date_aging_quality" : "debt_open_settlement_quality", debtDueDateAging ? null : debtOpenSettlementQuality ? "debt_due_date_aging_quality" : "debt_open_settlement_quality",
taxPosition ? null : "tax_position", taxPosition ? null : "tax_position",
inventoryPosition inventoryPosition
? inventoryStalenessRiskProxy ? inventoryQualityEvents
? null
: inventoryStalenessRiskProxy
? "inventory_reserve_liquidation_quality" ? "inventory_reserve_liquidation_quality"
: inventoryTurnoverProxy : inventoryTurnoverProxy
? "inventory_liquidity_quality" ? "inventory_liquidity_quality"
@ -4448,6 +4562,7 @@ function deriveBusinessOverview(input: {
inventoryPosition, inventoryPosition,
inventoryTurnoverProxy, inventoryTurnoverProxy,
inventoryStalenessRiskProxy, inventoryStalenessRiskProxy,
inventoryQualityEvents,
vendorProcurementQuality, vendorProcurementQuality,
hasSupplierConcentrationSignal: (rankedOutgoing?.ranked_values.length ?? 0) > 0 hasSupplierConcentrationSignal: (rankedOutgoing?.ranked_values.length ?? 0) > 0
}); });
@ -4473,6 +4588,7 @@ function deriveBusinessOverview(input: {
inventory_position: inventoryPosition, inventory_position: inventoryPosition,
inventory_turnover_proxy: inventoryTurnoverProxy, inventory_turnover_proxy: inventoryTurnoverProxy,
inventory_staleness_risk_proxy: inventoryStalenessRiskProxy, inventory_staleness_risk_proxy: inventoryStalenessRiskProxy,
inventory_quality_events: inventoryQualityEvents,
document_activity_profile: documentActivityProfile, document_activity_profile: documentActivityProfile,
counterparty_profile: counterpartyProfile, counterparty_profile: counterpartyProfile,
contract_usage_profile: contractUsageProfile, contract_usage_profile: contractUsageProfile,
@ -4483,7 +4599,7 @@ function deriveBusinessOverview(input: {
missing_signal_families: missingSignalFamilies, missing_signal_families: missingSignalFamilies,
missing_proof_families: missingProofFamilies, missing_proof_families: missingProofFamilies,
inference_basis: inference_basis:
hasBusinessOverviewProfileSignal || inventoryPosition || accountingFinancialResult hasBusinessOverviewProfileSignal || inventoryPosition || inventoryQualityEvents || accountingFinancialResult
? "business_overview_from_confirmed_1c_multi_family_rows" ? "business_overview_from_confirmed_1c_multi_family_rows"
: debtOpenSettlementQuality || debtDueDateAging : debtOpenSettlementQuality || debtDueDateAging
? "business_overview_from_confirmed_1c_multi_family_rows" ? "business_overview_from_confirmed_1c_multi_family_rows"
@ -4513,6 +4629,7 @@ function summarizeBusinessOverviewRows(input: {
contractUsageProfileResult: AddressMcpQueryExecutorResult | null; contractUsageProfileResult: AddressMcpQueryExecutorResult | null;
inventoryOnHandResult: AddressMcpQueryExecutorResult | null; inventoryOnHandResult: AddressMcpQueryExecutorResult | null;
inventoryAgingResult: AddressMcpQueryExecutorResult | null; inventoryAgingResult: AddressMcpQueryExecutorResult | null;
inventoryQualityEventsResult: AddressMcpQueryExecutorResult | null;
}): string | null { }): string | null {
const parts: string[] = []; const parts: string[] = [];
if (input.incomingResult && !input.incomingResult.error) { if (input.incomingResult && !input.incomingResult.error) {
@ -4560,6 +4677,9 @@ function summarizeBusinessOverviewRows(input: {
if (input.inventoryAgingResult && !input.inventoryAgingResult.error) { if (input.inventoryAgingResult && !input.inventoryAgingResult.error) {
parts.push(`${input.inventoryAgingResult.fetched_rows} inventory aging rows fetched, ${input.inventoryAgingResult.matched_rows} matched`); 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; 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)}. Это не подтвержденная неликвидность, не резерв и не ликвидационная стоимость.` `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; return facts;
} }
@ -5616,6 +5752,7 @@ export async function executeAssistantMcpDiscoveryPilot(
let contractUsageProfileResult: AddressMcpQueryExecutorResult | null = null; let contractUsageProfileResult: AddressMcpQueryExecutorResult | null = null;
let inventoryOnHandResult: AddressMcpQueryExecutorResult | null = null; let inventoryOnHandResult: AddressMcpQueryExecutorResult | null = null;
let inventoryAgingResult: AddressMcpQueryExecutorResult | null = null; let inventoryAgingResult: AddressMcpQueryExecutorResult | null = null;
let inventoryQualityEventsResult: AddressMcpQueryExecutorResult | null = null;
const valueFilters = buildValueFlowFilters(planner); const valueFilters = buildValueFlowFilters(planner);
const lifecycleFilters = buildLifecycleFilters(planner); const lifecycleFilters = buildLifecycleFilters(planner);
const profileFilters = buildBusinessOverviewProfileFilters(planner); const profileFilters = buildBusinessOverviewProfileFilters(planner);
@ -5625,6 +5762,7 @@ export async function executeAssistantMcpDiscoveryPilot(
const debtFilters = buildBusinessOverviewDebtFilters(planner); const debtFilters = buildBusinessOverviewDebtFilters(planner);
const debtDueDateAgingProbeEnabled = shouldRunDebtDueDateAgingProbe(planner); const debtDueDateAgingProbeEnabled = shouldRunDebtDueDateAgingProbe(planner);
const inventoryFilters = buildBusinessOverviewInventoryFilters(planner); const inventoryFilters = buildBusinessOverviewInventoryFilters(planner);
const inventoryQualityEventsProbeEnabled = shouldRunInventoryQualityEventsProbe(planner);
const debtAsOfDate = toNonEmptyString(debtFilters?.as_of_date); const debtAsOfDate = toNonEmptyString(debtFilters?.as_of_date);
const inventoryAsOfDate = toNonEmptyString(inventoryFilters?.as_of_date); const inventoryAsOfDate = toNonEmptyString(inventoryFilters?.as_of_date);
const incomingSelection = selectAddressRecipe("customer_revenue_and_payments", valueFilters); const incomingSelection = selectAddressRecipe("customer_revenue_and_payments", valueFilters);
@ -5660,6 +5798,9 @@ export async function executeAssistantMcpDiscoveryPilot(
const inventoryAgingSelection = inventoryFilters const inventoryAgingSelection = inventoryFilters
? selectAddressRecipe("inventory_aging_by_purchase_date", inventoryFilters) ? selectAddressRecipe("inventory_aging_by_purchase_date", inventoryFilters)
: null; : null;
const inventoryQualityEventsSelection = inventoryQualityEventsProbeEnabled
? selectAddressRecipe("inventory_quality_events_for_organization", inventoryFilters ?? buildBusinessOverviewProfileFilters(planner))
: null;
if (!incomingSelection.selected_recipe || !outgoingSelection.selected_recipe || !lifecycleSelection.selected_recipe) { if (!incomingSelection.selected_recipe || !outgoingSelection.selected_recipe || !lifecycleSelection.selected_recipe) {
pushReason(reasonCodes, "pilot_business_overview_recipe_not_available"); 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"); 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"); 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) { for (const step of dryRun.execution_steps) {
if (step.primitive_id === "query_movements") { if (step.primitive_id === "query_movements") {
const incomingExecution = await executeCoverageAwareValueFlowQuery({ const incomingExecution = await executeCoverageAwareValueFlowQuery({
@ -6007,6 +6156,19 @@ export async function executeAssistantMcpDiscoveryPilot(
}); });
probeResults.push(queryResultToProbeResult(step.primitive_id, contractUsageProfileResult)); 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) { if (lifecycleResult.error) {
pushUnique(queryLimitations, lifecycleResult.error); pushUnique(queryLimitations, lifecycleResult.error);
pushReason(reasonCodes, "pilot_business_overview_query_documents_mcp_error"); pushReason(reasonCodes, "pilot_business_overview_query_documents_mcp_error");
@ -6037,6 +6199,12 @@ export async function executeAssistantMcpDiscoveryPilot(
} else if (contractUsageProfileResult) { } else if (contractUsageProfileResult) {
pushReason(reasonCodes, "pilot_business_overview_contract_usage_profile_query_mcp_executed"); 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; continue;
} }
@ -6061,6 +6229,7 @@ export async function executeAssistantMcpDiscoveryPilot(
debtAsOfDate, debtAsOfDate,
inventoryOnHandResult, inventoryOnHandResult,
inventoryAgingResult, inventoryAgingResult,
inventoryQualityEventsResult,
inventoryAsOfDate, inventoryAsOfDate,
organizationScope, organizationScope,
periodScope: dateScope periodScope: dateScope
@ -6126,6 +6295,10 @@ export async function executeAssistantMcpDiscoveryPilot(
if (derivedBusinessOverview.inventory_staleness_risk_proxy) { if (derivedBusinessOverview.inventory_staleness_risk_proxy) {
pushReason(reasonCodes, "pilot_derived_business_overview_inventory_staleness_risk_proxy_from_confirmed_rows"); 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) { if (derivedBusinessOverview.missing_proof_families.length > 0) {
pushReason(reasonCodes, "pilot_business_overview_missing_proof_families_recorded"); pushReason(reasonCodes, "pilot_business_overview_missing_proof_families_recorded");
} }
@ -6145,7 +6318,8 @@ export async function executeAssistantMcpDiscoveryPilot(
counterpartyProfileResult, counterpartyProfileResult,
contractUsageProfileResult, contractUsageProfileResult,
inventoryOnHandResult, inventoryOnHandResult,
inventoryAgingResult inventoryAgingResult,
inventoryQualityEventsResult
}); });
const evidence = resolveAssistantMcpDiscoveryEvidence({ const evidence = resolveAssistantMcpDiscoveryEvidence({
plan: planner.discovery_plan, plan: planner.discovery_plan,

View File

@ -997,12 +997,20 @@ function buildCompactBusinessOverviewReply(
if (inventoryReserveBoundary) { if (inventoryReserveBoundary) {
const headline = toNonEmptyString(draft.headline); const headline = toNonEmptyString(draft.headline);
const inventoryQualityEvents = toRecordObject(overview.inventory_quality_events);
const cleanHeadline = headline?.replace(/^Коротко:\s*/iu, "").trim(); const cleanHeadline = headline?.replace(/^Коротко:\s*/iu, "").trim();
lines.push( lines.push(
cleanHeadline cleanHeadline
? `Коротко: ${localizeLine(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([ const boundaryLines = userFacingLines([
...toStringList(draft.unknown_lines), ...toStringList(draft.unknown_lines),
...toStringList(draft.limitation_lines) ...toStringList(draft.limitation_lines)

View File

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

View File

@ -710,10 +710,14 @@ describe("assistant MCP discovery answer adapter", () => {
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot); const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
expect(draft.headline).toContain( 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.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."); 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"); 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([ const deps = buildSequentialDeps([
{ rows: [{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Client A" }] }, { rows: [{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Client A" }] },
{ rows: [{ Period: "2020-01-20T00:00:00", Amount: 50000, Counterparty: "Supplier 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.bridge_status).toBe("answer_draft_ready");
expect(result.business_fact_answer_allowed).toBe(true); expect(result.business_fact_answer_allowed).toBe(true);
expect(result.route_candidate).toMatchObject({ expect(result.route_candidate).toMatchObject({
candidate_status: "needs_route_enablement", candidate_status: "ready_for_reviewed_execution",
selected_chain_id: "business_overview", selected_chain_id: "business_overview",
business_fact_family: "business_overview", business_fact_family: "business_overview",
action_family: "inventory_reserve_boundary", 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.pilot.derived_business_overview?.inventory_quality_events?.evidence_status).toBe(
expect(result.route_candidate.enablement_reason).toContain( "reviewed_no_quality_events_found"
"reviewed_inventory_quality_route_with_reserves_writeoffs_obsolescence_and_liquidation_value"
); );
expect(result.route_candidate.forbidden_overclaim_flags).toContain( expect(result.pilot.derived_business_overview?.missing_proof_families.map((item) => item.family)).not.toContain(
"confirmed_obsolete_stock_reserve_writeoff_or_liquidation_value" "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 () => { 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", "generation_id": "gen-ag05121628-50ea6c",
"created_at": "2026-05-12T16:28:41+00:00", "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": "А по этим же данным можно сказать, что склад ликвидный и неликвидов нет?"
}
]
}
]
}