Files
QuoteForge/bible-local/decisions/2026-09-01-article-category-from-component-universe.md
Mikhail ChusavitinandClaude Sonnet 5 d7d4ea74b6 fix: артикул — категории из component universe + видимость нераспознанных токенов
Генерация артикула резолвила lot_category из одного прайслиста конфигурации
(GetLocalLotCategoriesByServerPricelistID), поэтому world-only LOT (напр.
GPU_NV_RTX_PRO_6000D_SERVER_84GB_PCIE, есть только в world) молча выпадал из
артикула. Теперь категории берутся через GetLocalComponentCategoriesByLotNames
(тот же world ∪ estimate, что и весь конфигуратор). BuildOptions.ServerPricelist
убран; preview-article принимает pricelist_id, но игнорирует.

Нераспознанные токены больше не пишутся как UNK: в артикул идёт lot_category как
плейсхолдер (4xGPU, 2xCPU), сегмент помечается Recognized=false, добавляется
warning с именем LOT. Конфигуратор подсвечивает такие сегменты (amber) и выводит
список предупреждений; сохранение/обновление/откат логируют WARN. Без каталога
вендоров в репо детектируется только структурный сбой формы имени.

parseGPUModel: принимает суффикс-букву в номере модели (6000D → RTX6000D),
раньше терял её и схлопывал до RTX_84GB.

ADL bible-local/decisions/2026-09-01-article-category-from-component-universe.md,
2026-09-01-article-degraded-token-visibility.md; раздел «Article generation» в
02-architecture.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RAhfF4P1ySRZ67yyUUeVw2
2026-09-01 09:01:56 +03:00

63 lines
3.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Decision: article generation reads lot_category from the component universe, not one pricelist
**Date:** 2026-09-01
**Status:** active
## Context
`internal/article/generator.go` classifies each cart LOT into an article segment
(CPU / MEM / GPU / DISK / NET / PSU) by its `lot_category`. That category was
resolved by `ResolveLotCategories``GetLocalLotCategoriesByServerPricelistID`,
which queries exactly **one** pricelist — the configuration's pinned
`pricelist_id` (an `estimate` pricelist). A segment builder skips any LOT whose
category is unknown, so a LOT absent from that one pricelist vanished from the
article with no warning.
`GPU_NV_RTX_PRO_6000D_SERVER_84GB_PCIE` exposed this: it exists only in `world`
pricelists (where it correctly carries `lot_category = GPU`) and in no `estimate`
pricelist. It was priced (world fallback), exported, and shown in the cart, but
dropped from the article.
This is the same class of bug as
[2026-07-24-component-universe-world-union.md](2026-07-24-component-universe-world-union.md):
the cart's LOT set is `world` `estimate`, and no read path may silently drop a
LOT that only lives in `world`. Article generation was the one component-reading
path still scoped to a single pricelist.
Deriving the category from the `lot_name` prefix was rejected — it is forbidden by
the pricelist-contract rules (`02-architecture.md`, `bible/rules/patterns/`):
category comes only from real synced pricelist columns.
## Decision
`ResolveLotCategories(local, lotNames)` now calls
`LocalDB.GetLocalComponentCategoriesByLotNames`, which reads the component universe
(`componentUniverse()` — latest active `world` `estimate`, estimate wins a
collision). The server-pricelist parameter is gone.
- `article.BuildOptions` no longer has `ServerPricelist`.
- `POST /api/configs/preview-article` and the create/update paths still accept
`pricelist_id` but ignore it for article purposes.
- The `lot_name`-token parsing inside a segment (CPU model, memory size, GPU model,
disk capacity, port speed, wattage) still parses the token from the `lot_name`
that produces the article text, it does not categorize. One parser bug was fixed
alongside: `parseGPUModel` required the model-number token to be all digits
(`isNumeric`), so `RTX_PRO_6000D` lost its `D` and collapsed to `RTX_84GB`
indistinguishable from a real `RTX` card and different from its sibling
`RTX_PRO_6000``RTX6000_96GB`. It now accepts a digit-first alphanumeric token
(`isModelNumber`), giving `RTX6000D_84GB`.
## Consequences
- A world-only LOT lands in the article with its real category. Article generation
no longer depends on which pricelist a LOT was added to.
- The article can shift if the latest active `world`/`estimate` pricelist
recategorizes a LOT — acceptable, and consistent with how the configurator's
category tabs already behave.
- `GetLocalLotCategoriesByServerPricelistID` still exists and is still used by
`services/rental.go` and `services/export.go`. Those paths have the same
single-pricelist blind spot for world-only LOTs; migrating them is out of scope
here but should follow the same direction.
- Covered by `TestResolveLotCategories_WorldOnlyLot` in
`internal/article/categories_test.go`.