Files
QuoteForge/bible-local/decisions/2026-09-01-article-degraded-token-visibility.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

3.5 KiB

Decision: unparsed article tokens degrade to a visible category placeholder, never "UNK"

Date: 2026-09-01 Status: active

Context

internal/article builds each segment by parsing a spec token out of the lot_name (CPU model, memory size, GPU model, disk capacity, port speed, wattage). When a lot_name did not match the expected shape the generator failed silently or misleadingly:

  • parseCPUModel / parseGPUModel fell back to normalizeModelToken or a literal "UNK" with no signal — and a partial parse (RTX_PRO_6000DRTX_84GB) produced a plausible but wrong token indistinguishable from a real result;
  • NET / PSU emitted UNKNET / UNKPSU;
  • MEM / DISK dropped the segment or the capacity and emitted only a terse mem_unknown / disk_unknown warning;
  • BuildResult.Warnings existed but nothing consumed it: the preview-article frontend ignored the field and the create/update paths discarded it.

So a new naming shape (a vendor renames a card family, moves memory ahead of the model, glues an architecture onto the token) would silently produce a wrong or truncated article and no one would notice.

A vendor/model catalog in the repo to validate tokens against was rejected — it violates bible/rules/patterns/no-hardcoded-vendors and needs constant upkeep. Detection is therefore limited to structural failure (the name doesn't fit {GROUP}_{VENDOR}_{MODEL}[_{SPEC}…]); semantic drift where a wrong token still parses cleanly cannot be caught automatically and is out of scope.

Decision

  1. "UNK" / "UNKNET" / "UNKPSU" are gone. When a spec token can't be parsed the article carries the LOT's lot_category as the token (4xGPU, 2xCPU, 768G+MEM, …) via placeholderToken.
  2. Each build*Segment returns a segmentResult{value, degraded, warnings}. Every degraded segment produces a warning that names the offending lot_name and states which category was written instead.
  3. BuildResult gains Segments []ResultSegment{Group, Text, Recognized}. Recognized == false marks a segment whose token is a placeholder.
  4. parseCPUModel / parseGPUModel return (token, ok); ok == false on the last-ditch fallback path.
  5. Consumers surface it:
    • POST /api/configs/preview-article returns segments and warnings; the configurator's article line renders each not-recognized segment highlighted (amber) and lists the warnings beneath it (renderArticleDisplay in web/templates/index.html);
    • create / update / rollback log WARN "article generation degraded" with the server_model, article and warnings.
  6. No list of real lot_names or model names is committed to the repo — not as a fixture, not as a golden table. Tests use synthetic names only.

Consequences

  • An unrecognised LOT is always visible in the UI (highlighted token + warning) and in server logs — never a silent drop or a bare UNK.
  • The stored article string still contains only the placeholder token; the recognized flags are not persisted (regenerated on next save / preview).
  • compressArticle now returns []namedSeg (the caller re-joins) so the degraded flags survive compression.
  • Structural detection only: a wrong-but-well-formed token (e.g. a future GTX where RTX was expected) still passes. Closing that needs a validation source, deferred pending a server-side structured-attribute contract.
  • Covered by TestBuild_UnparseableModel_CategoryPlaceholder and TestParseGPUModel_VendorLetterSuffix.