Какво се случва
packages/db/migrations/0000_init.sql е миграция, която отдавна е приложена, но оттогава е редактирана на място многократно. D1 отчита миграциите по име на файл в таблицата d1_migrations и никога не преприлага файл, който вече е записан там. Затова всяка среда, създадена преди дадена редакция, остава без добавеното от нея — а wrangler d1 migrations apply отчита всички миграции като приложени.
Резултатът е схема, която се разминава мълчаливо. На база данни, създадена на 17.06.2026 г. и след това доведена „до последна версия“, липсват:
- таблиците
authority_joint_participation и contract_co_authorities;
- колоните
contracts.ordering_unit_name и tenders.ordering_unit_name (добавени с af4917e).
Заради липсващата колона GET /contracts/:id и GET /contracts/:id.json връщат HTTP 500:
D1_ERROR: no such column: c.ordering_unit_name at offset 557: SQLITE_ERROR
Нито една номерирана миграция не добавя тази колона — тя съществува само в редактирания 0000_init.sql.
Защо CI не го хваща
packages/db/src/migrations.test.ts изгражда схемата, като преиграва цялата верига върху нова база данни — това винаги минава. Няма проверка за целостта (checksum) на вече приложените миграции и никъде в AGENTS.md или CONTRIBUTING.md не е записано, че приложена миграция не се редактира.
Стъпки за възпроизвеждане
- Създайте D1 по
0000_init.sql отпреди комита af4917e (или използвайте среда, създадена преди него).
- Изпълнете
wrangler d1 migrations apply sigma --local → отчита всички миграции с ✅.
- Отворете страницата на кой да е договор → HTTP 500.
Проверка кои среди са засегнати
За всяка среда поотделно (sigma-blue, sigma-green, staging):
SELECT (SELECT COUNT(*) FROM pragma_table_info('contracts') WHERE name='ordering_unit_name') AS c_col,
(SELECT COUNT(*) FROM pragma_table_info('tenders') WHERE name='ordering_unit_name') AS t_col,
(SELECT COUNT(*) FROM sqlite_master WHERE type='table'
AND name IN ('authority_joint_participation','contract_co_authorities')) AS tables_present;
При изправна схема очакваният резултат е 1, 1, 2. По-малко означава разминаване.
Предложение за поправка
- Поправка на всяка засегната среда поотделно, а не чрез миграция. SQLite няма
ADD COLUMN IF NOT EXISTS, а повторното добавяне на съществуваща колона е грешка (duplicate column name). Затова обща миграция 0006 би счупила всяка среда, създадена след af4917e — включително всяка нова. Проверете схемата и добавете само липсващото; CREATE TABLE IF NOT EXISTS за двете таблици е безопасно, ALTER TABLE … ADD COLUMN не е.
- Миграциите да станат само за добавяне (append-only) — правилото да се запише в
AGENTS.md и CONTRIBUTING.md.
migrations.lock с checksum на всеки файл и тест, който пада при промяна на вече записан хеш. Това е контролът, който реално налага т. 2 — в духа на съществуващата проверка за разминаване при CANONICAL_QUERY_PARAMS.
- Проверка за разминаване на схемата при деплой срещу целевата D1.
scripts/compare-served-sqlite.mjs вече съдържа повечето от нужното.
Проверено: добавянето на липсващите две колони и две таблици възстановява коректното поведение — GET /contracts/:id отговаря с 404 вместо 500.
Къде се проявява
Уеб (sigma.midt.bg) и @sigma/db / миграциите. Проявява се само в среди, създадени преди съответната редакция; нова среда е изрядна, което и прикрива проблема.
Среда
Открито при преглед на кода и възпроизведено локално върху f72306c (production build през wrangler dev --local, D1 със 100 002 договора). Не е проверявано срещу разгърнатите среди — заявката по-горе отговаря на този въпрос.
Какво се случва
packages/db/migrations/0000_init.sqlе миграция, която отдавна е приложена, но оттогава е редактирана на място многократно. D1 отчита миграциите по име на файл в таблицатаd1_migrationsи никога не преприлага файл, който вече е записан там. Затова всяка среда, създадена преди дадена редакция, остава без добавеното от нея — аwrangler d1 migrations applyотчита всички миграции като приложени.Резултатът е схема, която се разминава мълчаливо. На база данни, създадена на 17.06.2026 г. и след това доведена „до последна версия“, липсват:
authority_joint_participationиcontract_co_authorities;contracts.ordering_unit_nameиtenders.ordering_unit_name(добавени сaf4917e).Заради липсващата колона
GET /contracts/:idиGET /contracts/:id.jsonвръщат HTTP 500:Нито една номерирана миграция не добавя тази колона — тя съществува само в редактирания
0000_init.sql.Защо CI не го хваща
packages/db/src/migrations.test.tsизгражда схемата, като преиграва цялата верига върху нова база данни — това винаги минава. Няма проверка за целостта (checksum) на вече приложените миграции и никъде вAGENTS.mdилиCONTRIBUTING.mdне е записано, че приложена миграция не се редактира.Стъпки за възпроизвеждане
0000_init.sqlотпреди комитаaf4917e(или използвайте среда, създадена преди него).wrangler d1 migrations apply sigma --local→ отчита всички миграции с ✅.Проверка кои среди са засегнати
За всяка среда поотделно (
sigma-blue,sigma-green, staging):При изправна схема очакваният резултат е
1, 1, 2. По-малко означава разминаване.Предложение за поправка
ADD COLUMN IF NOT EXISTS, а повторното добавяне на съществуваща колона е грешка (duplicate column name). Затова обща миграция0006би счупила всяка среда, създадена следaf4917e— включително всяка нова. Проверете схемата и добавете само липсващото;CREATE TABLE IF NOT EXISTSза двете таблици е безопасно,ALTER TABLE … ADD COLUMNне е.AGENTS.mdиCONTRIBUTING.md.migrations.lockс checksum на всеки файл и тест, който пада при промяна на вече записан хеш. Това е контролът, който реално налага т. 2 — в духа на съществуващата проверка за разминаване приCANONICAL_QUERY_PARAMS.scripts/compare-served-sqlite.mjsвече съдържа повечето от нужното.Проверено: добавянето на липсващите две колони и две таблици възстановява коректното поведение —
GET /contracts/:idотговаря с 404 вместо 500.Къде се проявява
Уеб (
sigma.midt.bg) и@sigma/db/ миграциите. Проявява се само в среди, създадени преди съответната редакция; нова среда е изрядна, което и прикрива проблема.Среда
Открито при преглед на кода и възпроизведено локално върху
f72306c(production build презwrangler dev --local, D1 със 100 002 договора). Не е проверявано срещу разгърнатите среди — заявката по-горе отговаря на този въпрос.