Техническо състояние на проекта

последна актуализация: 21.09.2026
136 изградени функционалности
3.08M знака в индекса на чатбота
27 895 лица в родословната база
40 задачи в план за развитие

Изградено

136 функционалности · 9 модула
Сайт
✓
Основен стек
WordPress + тема Blocksy, редакция през Gutenberg + Custom HTML блокове.
✓
Начална страница
Hero секция, шорткод за последни новини, преглед на галерия, weather widget (Open-Meteo API, без ключ), CTA-та за дарения.
✓
Раздел „История“
Отделна категория в блога с дедикирана display страница, изключена от общия поток новини чрез pre_get_posts hook.
✓
Странична лента (sidebar)
Widget за дарения и widget за кампанията „Дари спомен“.
✓
Контакти
Google Maps вграждане + контактна форма (WPForms Lite).
✓
Интерактивна викторина в „История“
Fisher-Yates разбъркване на отговорите, обяснение след всеки въпрос, прогрес лента с точки, финален екран с резултат; масив QUIZ_DATA с 8 въпроса (за разширяване).
✓
Дизайн система
Фиксирана цветова палитра, CSS класове с префикс .chk-, override-и на Blocksy defaults там, където темата налага собствени стилове.
Архив (Tainacan)
✓
Интеграция с Blocksy
Плъгин Tainacan Support for Blocksy — стандартният Tainacan Interface не пасваше на темата.
✓
Колекция „Пленерни картини“
Обединена колекция (ID 153, slug pleneri) с таксономия „Пленер / Година“ вместо отделни колекции по година.
✓
Колекция „Архив фотографии“
Основна колекция (ID 961) с каталогизирани стари фотографии и документи от селото; цел на релационните полета в останалите колекции.
✓
Проверка за дублирани елементи
Дедупликация по файлово име, с флагване на несигурни съвпадения (thumbnail за ръчен преглед) при ръчно преименувани елементи.
✓
Колекция „Дари спомен“
Колекция (ID 3636, slug dari-spomen) за анонимно публично качване на спомени; нови елементи влизат като Pending. Наследява таксономиите от колекция 961 (Вид на снимката, Десетилетие/Период, Източник — с нов термин по подразбиране „Дари спомен“).
✓
Отключен анонимен достъп до „Дари спомен“, пазен от индексиране
Колекцията мина от private на publish, за да може формата изобщо да се зареди за нелогнат посетител — Tainacan проверява само WP core публичния флаг на статуса, независимо от настройките за анонимно подаване. Пазена от търсачките с публичен noindex, nofollow през WPCode wp_head снипет — покрива страницата на колекцията, /dari-spomen/ и страницата за подаване.
✓
Заобикаляне на Tainacan 1.2.0 bug с default стойности
Счупеният set_default_value() е заобиколен с директно задаване през update_post_meta() при нови подадени елементи.
✓
Поле „Съгласие“ и скрито поле за свързана личност
„Съгласие“ като Selectbox с една опция (Tainacan няма нативен checkbox тип). Полето „Свързана личност/снимка“ (връзка към колекция 961) е скрито от публичната форма през enabledMetadata на submission блока — вижда се само от админ.
Местности (Tainacan)
✓
Колекция „Местности“
ID 4180, slug mestnosti, статус publish; анонимната REST заявка връща 200. Създадена скриптово срещу живия сайт, ID-тата са записани в mestnosti_state.json.
✓
Таксономия „Тип местност“
ID 4186, 48 термина в 8 родителски групи.
✓
Схема със 17 собствени полета
Варианти на името, polygon GeoJSON, confidence (точна / приблизителна / неизвестна), name_class, status (жива / изчезнала по документи / извън землището), first_attested, etym_source, part_of (връзка към самата колекция), person_origin, owner_hist, източник и връзка към колекция 961.
✓
Нативни координати
Полето coordinates използва вградения тип GeoCoordinate на Tainacan.
✓
Премахнато поле за надморска височина
elevation отпадна след извличането — в източниците няма данни за височина.
✓
Импорт на 190 местности
Собствен PHP скрипт вместо Tainacan CSV importer, чийто insert_hierarchy() игнорира allow_new_terms и който няма сух пробег. ID-тата на създадените елементи са в created_items.json.
✓
Rollback скрипт за импорта
Връща колекцията в изходно състояние по списъка в created_items.json.
✓
Правилен ред при създаване на елементи
Item::validate() проверява задължителните полета спрямо записаното в базата, затова нов елемент директно в publish пада. Ред: draft → Item_Metadata → publish + update(), както в самия CSV importer.
✓
Заобикаляне на slug bug в Tainacan 1.2.0
fetch([‘slug’ => …]) връща всички записи от типа, защото parse_fetch_args() подава невалидния аргумент post_name. Проверките за идемпотентност ползват get_posts([‘name’ => …]) и get_term_by().
✓
Коректни опции на Selectbox полета
Опциите се разделят по \n при рендиране, не по options_separator. Скриптовият запис е implode(„\n“, $values), без options_separator.
✓
Незадължителни координати
coordinates е трайно незадължително — 16 изчезнали местности от стари нотариални актове никога няма да имат координати. Напредъкът по геолокация се следи през confidence = „неизвестна“.
✓
Поле за разказа от том 1
Отделно Textarea поле narrative (ID 6296) с резюмета за 52 от 69-те местности, споменати в „Чокманово том 1“. location_desc остава дословната речникова статия от том 2.
✓
Проверка преди масов импорт
Скриптово създадените полета се проверяват визуално и през REST, не само по успешен insert. Праговете в скриптовете отчитат двете core полета (колекцията отчита 19, не 17).
Родословие
✓
Вход с 6-цифрен код
Удостоверяване без WordPress акаунт — 6-цифрен код за потвърждение + 30-дневна HMAC бисквитка, срещу JSON списък с разрешени имейли (premium_access.json).
✓
Корекция на имейли със знак „+“
+ в имейл адреса се декодираше като интервал в redirect параметрите. Поправено с rawurlencode() на всички места с add_query_arg(), които носят chk_email.
✓
Обработка на пълния износ от Agelong Tree
Парснати 27 895 лица в 164 рода в структурирани JSON файлове (people_core.json, families.json, notes.json, rod_tags_dict.json + overlay файлове).
✓
Филтриране на износа
~6 500 празни/служебни записа се отсяват; обхватът се ограничава до Чокманово по мястото в записа + разширяване по графа на връзките.
✓
Fingerprint валидация за overlay корекции
Agelong Tree преномерира ID-тата при всяко вмъкване, затова корекциите (corrections.json, moves.json, additions.json, local_families.json, family_additions.json) се съпоставят по отпечатък (име + фамилия + година на раждане), а не по ID.
✓
Твърда защита на съдържанието (hard gate)
Пропуска се през template_redirect hook, преди да се рендира каквото и да е съдържание — the_content филтърът се оказа ненадежден за Blocksy.
✓
Защита от индексиране в Google
Тъй като гейтваните страници не връщат съдържание на неавтентикирани заявки (включително ботовете на търсачките), личните и родословни данни не попадат в резултатите от търсене.
✓
Защита от привилегирован достъп (privilege escalation)
Формата за вход проверява съществуващата роля на потребителя, преди да зададе нова — за да не презапише случайно роля на администратор. Неотговарящи на условията акаунти получават общ отговор.
✓
Кеш изключения за защитените страници
LiteSpeed Cache no-cache сигнализация (litespeed_control_set_nocache + LSCACHE_NO_CACHE) и добавяне на URI-тата в списъка с изключения — за да не се показва кеширано съдържание на грешен потребител.
✓
Рамка за поверителност
Три нива: починали преди 1930 г. (свободно използваеми), починали в рамките на живата памет (с внимание), живи лица (никога не се показват).
✓
Търсене и разграничаване на лица
Search-first интерфейс с debounced AND-търсене и карти за разграничаване при съвпадащи имена (често срещано в малко село с повтарящи се имена през поколенията).
✓
Визуализация на родословното дърво
Интерактивно SVG дърво на предците (D3-style, ванилен JS) с bezier конектори, показване на братя/сестри по поколение, отделни render пътища за мобилно и десктоп.
✓
Timeline странична лента + zoom
Sticky timeline sidebar до дървото; мащабиране на изгледа през CSS custom property –chk-zoom.
✓
Печат и JSON износ
Възможност за печат на дървото и експорт на данните за лице/род в JSON формат.
✓
Коректно показване при родствени бракове
Поправен bug с потискане на легитимни повторения в дървото (pedigree collapse) — проследяващите масиви вече са локални за всеки клон, вместо споделени между двете родови линии.
✓
GEDCOM импорт и обновяване
Данни от родословната гора на Петър Камбарев (Agelong Tree), с работещ процес за периодично обновяване; парсерът поддържа структурни разлики между отделните export-и (наличие на SURN тагове, покритие на NOTE полета).
✓
Единен слой за връзките между поколенията
Всички конектори се чертаят в един SVG върху цялото дърво, след измервателен проход по реалните позиции на картите (getBoundingClientRect). Предишният вариант рисуваше отделен SVG на всяка лента с preserveAspectRatio=“none“, което деформираше кривите неравномерно при широки родове.
✓
Цветово кодиране на родовите линии
Бащината линия е зелена, майчината — теракота; и по конектора, и по горния ръб на картата. Легенда над дървото.
✓
Осветяване на пълната линия до корена
Посочване, фокус или клик върху карта осветява цялата верига до изходното лице и приглушава останалото дърво. Клик фиксира линията, Esc я освобождава.
✓
Фиксирана лента с текстовата родствена линия
Веригата се изписва в лента, закрепена в дъното на екрана (position:fixed) — извън потока на документа, за да не измества дървото при всяко минаване с мишката.
✓
Свиване и разгъване на клонове
Хапче на долния ръб на всяка карта скрива или връща предците ѝ; свитата карта показва броя скрити лица и получава пунктиран долен ръб. Бутонът „Разгъни всички“ се появява само когато има свити клонове, с брояч.
✓
Стабилни ключове по път на възлите
Всеки възел се идентифицира с път от корена (0, 0f, 0fm), а не с ID на лицето — така свитото състояние остава коректно и когато едно и също лице се появява в две родови линии.
✓
Запазване на позицията при пренареждане
Вертикалният скрол на страницата и хоризонталният скрол на дървото се възстановяват след всяко свиване, разгъване или смяна на мащаба.
✓
Автоматично побиране в екрана
Бутонът „Побери“ изчислява мащаба от броя колони и наличната ширина, вместо нивото да се търси на ръка със стъпки от 10%.
✓
Разделени режими: търсене и дърво
Панелът за търсене се скрива при отваряне на дърво и се заменя със sticky лента с името на лицето и бутон „Ново търсене“. Рестартът вече не изисква да се познае, че трябва да се пише в изчезналото поле.
✓
Приглушен изход от акаунта
„Изход“ беше най-видимият бутон на страницата и се бъркаше с рестарт на търсенето. Сега е второстепенен линк, а рестартът е основното действие в лентата.
✓
Йерархия на контролите в картата
Три конкуриращи се контрола (бележка, братя/сестри, свиване) са сведени до два с различен вид: подчертан текстов линк „подробности“ за съдържанието и хапче на ръба за структурата. Дългите бележки се отрязват с CSS line-clamp, а не с втори линк.
✓
Неутрализиране на Blocksy button стиловете
Темата боядисва всеки <button> в теракота, което правеше второстепенните контроли (×, „свий“) да изглеждат като главни действия. Всички собствени бутони в модула получават явен reset.
✓
Клавиатурна навигация в дървото
Всяка карта е фокусируема (tabIndex), Enter/Space фиксира линията, Esc я освобождава.
Дигитализация (OCR)
✓
OCR pipeline
Собствен pipeline: PyMuPDF за извличане на страници + Tesseract с поддръжка на български език (bul); преизползваем скрипт ocr_pipeline.py.
✓
„Чокманово том 2“ — напълно обработена
236 PDF страници → 470 книжни страници; 963 000+ разпознати знака; per-page JSON (текст, брой знаци, средна OCR увереност, флагнати страници).
✓
„Чокманово том 1“ — напълно обработена
267 страници, 740 чънка, 1 208 238 знака. Качеството е проверено преди качване: 78.5% кирилица, нула заместващи символи, нула лични данни; разликата спрямо източника е 9.8% умишлено припокриване между съседните чънкове.
✓
Две кратки книги — обработени
„Св. Архангел Михаил“ (4 стр.) и „Св. Пророк Илия“ (5 стр.), средна OCR увереност ~93.8–93.9%.
✓
Научна статия за райковските свещеници — обработена
12 страници на Златка Петрова (РИМ „Стою Шишков“ — Смолян) с бележки под линия и архивни препратки. Първият източник, който не е селищна монография.
✓
„Родопските комити разказват“ — обработена
204 страници, OCR + семантично нарязване през ragexport.py; 842 537 знака в 515 чънка с припокриване и запазени метаданни за книга и страница.
✓
Извличане на местностите от двата тома
190 местности от речниковата глава на том 2; 69 от тях са открити и в разказа на том 1, който няма собствен топонимен речник.
RAG чатбот (Cloudflare)
✓
Чатбот — демо версия
Статичен Custom HTML интерфейс с предварително написани въпроси и отговори — заменен от реалния бекенд във Фаза 4.
✓
RAG бекенд — инфраструктура (Фаза 0)
Проект chokmanovo-chatbot в Cloudflare Workers, D1 схема приложена (chokmanovo-db), Vectorize индекс създаден (chokmanovo-chunks, 1024-dim cosine), Workers AI binding за embeddings (@cf/baai/bge-m3).
✓
Защита на админ панела
/admin/* защитен с Cloudflare Zero Trust Access (One-time PIN); конфигурация в D1 с лимити daily_spend_limit_usd 2.0 и daily_ip_limit 20.
✓
Ingestion endpoint (Фаза 1)
POST /admin/ingest приема JSON от OCR инструмента, ембедва всеки чънк през Workers AI и записва вектора във Vectorize и реда в D1. Идемпотентен — повторно качване обновява, не дублира.
✓
Пилотната книга в индекса
Цялата книга заредена наведнъж: 515 чънка за ~36 s, 515 вектора × 1024 измерения. Проверено чрез сравнение D1 ↔ Vectorize, а не по код: 515 реда, 515 уникални id, сумата от знаците съвпада точно с износа.
✓
Единна идентичност на чънковете
${book_id}:${chunk_id} е едновременно primary key в D1 и vector id във Vectorize — това е връзката, която позволява на векторно съвпадение да върне текст и страница. 64-байтовият лимит на Vectorize се валидира преди запис.
✓
Проверено извличане от край до край
Заявка по vector id връща точно самосъвпадение 1.0000 с логични съседи, а върнатите id-та се разрешават обратно до текст и страници в D1.
✓
Публичен endpoint /chat (Фаза 2)
Отговор на български, стъпил единствено върху извлечения контекст, с цитати заглавие + страница. CORS ограничен само до digital-archive.site; /admin/* остава зад Access.
✓
Тристепенна защита на разходите
Глобален прекъсвач по дневен разход, дневен лимит на IP и твърд max_tokens — в този ред. Блокирана заявка не стига до Claude изобщо: отговаря се от константа на български.
✓
Атомарен брояч за лимита на IP
Проверката и увеличението са едно изявление (INSERT … ON CONFLICT … RETURNING), така че две едновременни заявки от един IP не могат и двете да минат под лимита.
✓
Отчитане на разхода по реални данни
cost_usd се смята от токените, които API-то само отчита, никога от оценка; цените на модела живеят в config до името му. Всяко повикване се записва в usage_log в същата заявка.
✓
Отказ без подвеждащи цитати
Извличането винаги връща съседи, затова честният отказ иначе би пристигнал със списък източници. Маркерът за отказ е в config до промпта; сравнението е устойчиво на форматиране, пренос на ред и главна буква.
✓
Чист текст без markdown
Правило в промпта против звездички и заглавия, които widget-ът би показал буквално. Проверено срещу девет markdown конструкции върху реален отговор.
✓
Устойчивост при смяна на модела
temperature се изпраща само на моделите, които я приемат — Opus 5, Sonnet 5 и 4.7/4.8 я отхвърлят с 400. Смяна на config.model вече не може да събори /chat.
✓
Отговор от константа при липса на контекст
Ако извличането не върне нищо, отговорът идва от константа — вместо да се плаща на Claude да каже, че не знае.
✓
По-подробни отговори
max_tokens 2048 и top_k 8, плюс пренаписано правило за изчерпателност (старото правило „пиши кратко“ беше истинската спирачка, не таванът). Измерено: ~$0.0101 на въпрос, ~198 въпроса на ден под непроменения таван от $2.
✓
Разходите в отговора на API-то
cost_usd, input_tokens и output_tokens се връщат заедно с отговора, от същите променливи, които се пишат в usage_log — мрежовият отговор и разходният регистър не могат да се разминат.
✓
Админ панел за чатбота (Фаза 3)
Страница /admin/panel с табло, дневник на заявките и редактор на config, зад същия Access. „Изхарченото днес“ чете същата константа TODAY_SQL, която ползва и прекъсвачът — двете не могат да се разминат за това къде започва денят.
✓
Предпазители при запис в config
config се чете при всяка заявка, затова лоша стойност важи за следващия посетител без деплой. Твърдите граници връщат 400; преценките — 409 с предупреждение на български, което трябва да се потвърди (сменен модел без цени, модел, който отхвърля temperature, липсващ маркер за отказ, лимит 0).
✓
Управление на архива от панела (Фаза 3.1)
Книги се качват и изтриват без терминал. DELETE /admin/api/books без confirm: true е сух пробег, който само отчита какво ще падне. Векторите се трият преди редовете в D1 — обратният ред би оставил сираци, които печелят top_k места и после не се разрешават до текст.
✓
Батчване по бюджет от знаци
Workers AI отказва bge-m3 заявка над 60 000 токена, а фиксиран брой чънкове не може да спази таван в токени — зависи от дължината им. Батчът вече се мери в знаци (EMBED_CHAR_BUDGET 55 000); прекалено голям товар се отхвърля при валидация, преди да е записано нещо.
✓
Позиционна проверка при отказ
Съдържателен отговор, който завършва с уговорка, губеше цитатите си. Маркерът се брои само ако започва в първите 200 знака — отказът води с фразата, уговорката я влачи накрая.
✓
Чат widget-ът свързан с бекенда (Фаза 4)
Custom HTML блокът вика реалния POST /chat вместо демо отговорите. Потвърдено от страната на API-то: заявки от втори посетител в usage_log, CORS работи за реалния произход и отказва останалите.
✓
Стойността на отговора във widget-а
Интерфейсът показва колко струва всеки отговор в долари, от cost_usd в тялото на отговора — същото число, което влиза в usage_log и което сумира дневният прекъсвач.
✓
Попъп с инструкции за чатбота
WPCode shortcode chk_chat_intro (PHP nowdoc): обхват на бота, поведение при липса на информация, лимити, предупреждение за лични данни. Показва се веднъж на сесия (sessionStorage), затваря се с бутон, Esc или клик извън прозореца.
✓
Целият архив в индекса
Шест източника, 1901 чънка, 3 083 871 знака — двата тома на „Чокманово“, „Родопските комити разказват“ и три статии. D1 и Vectorize се сверяват след всяко качване. Отговорите обхващат няколко книги наведнъж и цитират всяка поотделно.
✓
Разширяване до съседните откъси
Извличането по избор добавя откъса преди и след всяко съвпадение (neighbor_expansion_enabled, прозорец neighbor_window), за да не се къса разказ по средата на страница.
✓
Ограничение за разнообразие на резултатите
Таван на броя откъси от една и съща (застъпваща се) страница (diversity_limit_per_location), за да не измести една силно съвпадаща страница останалите относими източници.
✓
Речник на вариантите на имена
src/name-variants.json + флаг name_variants_enabled — съпоставя диалектни/исторически изписвания при търсене: редуване о/ъ, поколенчески наставки.
✓
Query expansion
Едно допълнително евтино повикване към Claude Haiku на въпрос, генериращо 3–5 алтернативни заявки за търсене (query_expansion_enabled).
✓
Multi-query сливане с RRF
Резултатите от оригиналната заявка и от вариантите се сливат с Reciprocal Rank Fusion.
✓
Резервирани места вместо тегла
От 8-те места 6 са запазени за оригиналната заявка и 2 за вариантите. Тегловното сливане позволяваше на вариантите да изместят точните съвпадения; резервираните места го изключват.
✓
Оценъчна среда за извличането (eval harness)
Отделен Cloudflare Worker (eval/eval-worker.ts) за сравняване на комбинации от флагове върху фиксиран набор контролни въпроси, преди да се включат в продукция. Съседните откъси и разнообразието са включени по подразбиране от 2026-09-03.
✓
Проверени еталони за оценка
Очакваното място на отговора в текста се потвърждава с SQL заявка срещу D1, преди контролният въпрос да влезе в набора.
✓
Флагване на грешен/непълен отговор
Бутон в widget-а под всеки отговор от бекенда; отваря поле за незадължителна бележка и праща доклад към нов POST /chat/flag. Не вика Claude и не пипа разходния прекъсвач.
✓
Нова таблица за докладвани отговори
answer_flags (миграция 0008) пази точния текст на флагнатия отговор — usage_log никога не пази текста на отговора, само въпроса, затова се записва само при флаг.
✓
Лимит срещу злоупотреба на flag endpoint-а
Отделна таблица flag_rate_limits със същия атомарен INSERT … ON CONFLICT … RETURNING модел като лимита на /chat, 10 доклада на IP на ден — публичен endpoint без автентикация.
✓
Преглед на флаговете в админ панела
GET/PATCH /admin/api/flags, филтри Всички/Нерешени/Решени и маркиране като „разгледан“ — списък за подобрения, изграден от реални доклади, не от предположения.
ИИ редактор (статии)
✓
Конвенция за машинно четими метаданни
WPCode снипет chk-ai-meta регистрира post meta _chk_ai и _chk_sources в REST — собствените статии стават индексируеми и от писателя, и от чатбота. Всяка нова колекция се закача за същата конвенция, не за нов механизъм.
✓
Webhook към Worker-а за статии
chk-writer-webhook вика отделния Cloudflare Worker chokmanovo-writer — държан настрана от чатбот Worker-а, за да не делят конфигурация, лимити и разходен прекъсвач.
✓
Вътрешна страница „ИИ редактор“
Снипет chk-ai-editor — работно място в админ частта за подготовка на статия по идея, без терминал и без достъп до Cloudflare.
Външни източници (Europeana)
✓
Проучване през Search и Record API
„Смолян“ е надеждната заявка: 171 записа, 170 с отворен лиценз. „Родопи“/„Rhodope“ е замърсена — най-големият доставчик е Музикална библиотека на Гърция (1248 записа за гръцка Родопа), плюс хербарни образци и римска епиграфика.
✓
Инвентар на 42-та източника от РБ Смолян
Всички TEXT, всички Public Domain Mark, част от „Дигитална колекция Паметта на Родопа планина“ — каноничните родопски източници 1884–1942, вкл. 7 вестника. Описанията са ръчно анотирани от библиотекарите със селата, които всеки източник покрива — затова заявката „Чокманово“ дава съвпадения по метаданни, не по OCR.
✓
Проверка за наличен пълен текст (IIIF)
Манифестите обявяват /search услуга, която връща 404 за всяка форма на параметрите; истинският показател е броят canvas-и — 2 значат корица и мостра, не дигитализирана книга. Search API търси само метаданни; пълният текст е зад отделните IIIF Fulltext и Newspapers API.
✓
Корекция на филтъра за лицензи
reusability=open покрива само PD, CC0, CC BY и CC BY-SA — CC BY-ND минава за „Permitted“. Постоянно включеният филтър скриваше половината от съществуващото за селото: от 8-те записа за вариантите на името 4 са иконите от „Св. пророк Илия“ (ИИИ при БАН през AMS Historica Болоня). Филтърът вече е параметър на заявката, не константа.
✓
Правна позиция
Метаданните на Europeana са CC0 — съхраними, индексируеми и публикуеми. Изображенията се хотлинкват към източника, никога не се копират в Media Library; задължително атрибуиране на институция, лиценз и линк към оригинала.
✓
Решение: една колекция, индексира се българският текст
Отхвърлени са масовият импорт на сурови записи, отделен Worker с адаптери по източник и IIIF deep-zoom (няма inode запас). Индексира се само българското описание, написано за всеки обект — каталожните стубове от 10–20 думи биха печелили съвпадения срещу 963 000-знаковия OCR корпус и биха давали отговори без цитат на том и страница.
✓
Колекция „Външни източници“
ID 6467, slug external-sources; core Title 6469, core Description 6471. Core полетата не могат да са multiple: yes — платформено ограничение на Tainacan.
✓
Схема с 14 собствени полета
orig_title, orig_desc, creator, year_text, external_id, source_url, thumb_url, media_url, rights, scope, source_type, institution, related_place (→ 4180) и related_photo (→ 961); всички са multiple: yes. Местата са релация към „Местности“, не втора паралелна таксономия.
✓
Две таксономии
source_type (ID 6475) със седем термина — изображение, книга, документ, периодика, звукозапис, видео, друго; source_institution (ID 6477) започва празна с allow_insert, термините се създават от харвестъра.
✓
Харвестър за Europeana
chk-scripts/europeana-harvest.php, пускан ръчно по SSH: –scope=village|smolyan|kaza [–apply] [–limit=N]. Сух пробег по подразбиране, реален запис само с –apply.
✓
Три обхвата и техният ред
Само вариантите на името на селото дават 8 записа, затова обхватът е разделен на три: village, smolyan (по dataProvider „Public Library – Smolyan“) и kaza (Ахъчелеби, Пашмакли, Устово, Райково, Смилян) — заедно 50–80 записа. Редът е smolyan → village → kaza: записите се застъпват и първият обхват ги заявява; обратният ред етикетира грешно.
✓
Само чернови, без път към публикуване
Харвестърът създава елементите единствено като DRAFT — в кода няма клон, който публикува. Празните чернови не се индексират и не се виждат на сайта; това е предпазителят срещу автоматично влизане на чужд каталожен текст в архива.
✓
Дедупликация през wp_option
Видените Europeana ID-та се пишат в chk_eu_seen постъпково и независимо от изхода, така че отхвърлен запис не се появява пак. Вариантите на имената и dataProvider низовете живеят в chk_eu_scopes, не в кода.
✓
Първите 30 записа от Europeana — прегледани и публикувани
И трите обхвата: village 8/8, smolyan 13/13, kaza 9 (след trash на 11 нерелевантни). Видими на сайта с оригиналното заглавие и описание от източника; собствен текст на български в Core Description предстои за всеки — виж „Предстои“.
✓
Изображения без нито един файл на диска
WPCode snippet chk-external-thumbs + споделена библиотека chk-scripts/lib/external-thumbs.php: по 2 WP attachment поста на запис, изцяло без реален файл — само мета сочещо към Europeana thumbnail proxy-то (фиксиран размер, ~400×604px). Филтри върху image_downsize/wp_get_attachment_url връщат външния URL при поискване. Изображенията вече се виждат навсякъде — grid на колекцията, item страницата, REST API — без да се пипат ограничените инодове на хостинга. Харвестърът вече ги създава автоматично за всеки нов запис, без нужда от отделен backfill.
✓
Атрибуция и защита от изрязване
Автор, институция, права и линк към оригинала под всяко изображение на item страницата. object-fit/background-size: contain навсякъде в колекцията — иконите са CC BY-ND, изрязване е дериват. Покрива и трети, отделен плъгин (Tainacan Extra View Modes), чийто изглед „Books“ рендира миниатюрата като background-image, не <img>.
✓
Открит мъртъв линк за пълноразмерните икони от BYZART
cmc.byzart.eu е мигрирал изцяло към нова платформа (Angular SPA, не сервира директно изображение) — оригиналният линк за 4-те икони вече не работи. Europeana thumbnail proxy-то остава единственото използваемо изображение за тях, 400×604 фиксиран таван.
Работен процес / инфраструктура
✓
Пряк достъп до продукцията
SSH достъп (порт 1022) + sshfs mount на хостинга, Claude Code за директна работа по файловете на живия сайт.
✓
Стабилна работна среда за Claude Code
Стартира се от локалната ~/dev/chokmanovo с –add-dir ~/sites/chokmanovo — стартът в sshfs папката води до Bun ENOENT грешки. CLAUDE.md живее локално.
✓
Скриптови промени без WP-CLI
WP-CLI липсва на хостинга, затова програмните промени в Tainacan минават през PHP CLI скрипт (8.4.25) с require wp-load.php, който вика директно \Tainacan\Repositories\… и \Tainacan\Entities\….
✓
Отделна Python среда на сървъра
Системният интерпретатор е Python 3.6.8 (EL7); проектните скриптове вървят във python3 -m venv, а не върху него.
✓
Планиране на фонови задачи без cron
На акаунта няма реален cron, а WP-Cron се задейства от посещения и е ненадежден при слаб трафик. Затова периодичните задачи (вкл. харвестъра) се пускат ръчно по SSH, вместо да се разчита на график, който не се изпълнява.
✓
Ограничение по брой файлове (inodes)
Няма запас за големи количества дребни файлове — това изключва IIIF плочки и всяко друго решение, което взривява броя файлове. Отчита се при избора на всяка нова функционалност.
✓
Правилно кодиране на URL параметри
add_query_arg() НЕ кодира аргументите, които добавя (build_query() вика _http_build_query с $urlencode = false). Кирилица, кавички и интервали влизат сурови, а nextCursor на Europeana със + или = чупи страницирането след първата страница — ползва се http_build_query() с конкатенация.
✓
Правилно броене на елементи в Tainacan
Няма общ тип tainacan-item — всяка колекция има свой tnc_col_{ID}_item. Броене по postmeta.collection_id без филтър по реалния post type връща силно завишени числа.
✓
Установени сирашки записи от изтрита колекция
20 публикувани записа с тип tnc_col_62_item при трайно изтрита колекция 62 — ранни тестови елементи от 22.07.2026, невидими в админа, безвредни (post ID-та не се преизползват).
✓
Валидацията не важи за чернови
Item::validate() се налага само за publish/future/private/pending — Repository::insert() я прескача за draft, затова черновата може да се вмъкне преди метаданните си. Item_Metadata_Entity::validate() е отделна и продължава да важи за всяко поле.
✓
Изтриване на полуготов елемент при провалена валидация
При грешка в задължително поле елементът се трие (wp_delete_post($id, true)), а не се оставя като чернова без това поле — ID-то вече е в списъка с видени и записът би се загубил безшумно завинаги.
✓
Snapshot guard преди и след скриптова промяна
Пазачът държи под наблюдение и собствените обекти, вместо да ги освобождава: освобождаването на колекцията и таксономията, в които се пише, означава непредвидена промяна там да остане незабелязана. Освобождава се само таксономията, за която се очаква да расте.
✓
Git за чатбот проекта
Версионна история на Cloudflare Workers кода.
✓
Дисциплина при промени
Резервно копие с UpdraftPlus преди всяка структурна промяна, LiteSpeed Cache purge след промяна, тестове на малки партиди преди bulk операции.

Предстои

40 задачи
Следващи
•
Поправка на числовите грешки в OCR текста
Ботът стъпва строго върху разпознатия текст, затова сгрешено число става уверено изречен грешен факт: 1485 вместо 1885 в ктиторски надпис, ХХ в. вместо ХIХ в. за икони от 1830 г. Числата и римските цифри са там, където OCR е най-слаб и окото на коректора се плъзга.
приоритет
•
Почистване на думи със смесена азбука
Tesseract понякога минава на латиница по средата на думата (OSMANСКО, grootелият). Откриване в целия корпус по [A-Za-z] + [А-Яа-я] в една дума — тези думи развалят ембедингите, не само четимостта.
приоритет
•
Обработка на последната по-голяма книга
Тодор Хвойнев: „Моите спомени“ — 118 стр.
приоритет
•
Дигитализация на още исторически източници
Цел: минимум 10 допълнителни източника, за да има чатботът достатъчно материал.
приоритет
•
Български описания за публикуваните записи от Europeana
4 от 30-те публикувани записа вече имат собствен текст на български в Core Description — четирите икони от църквата „Св. пророк Илия“ (Димитриос Астериадис, 1892 г.; Спирос Михаил, 1836 г.), заснети от Георги Линков през 2005 г. за проекта BYZART. Остават 26 записа само с оригиналния текст от източника — той е и текстът, който ще влезе в индекса на чатбота, не каталожните метаданни.
приоритет
•
Индексиране на публикуваните външни източници
Чънковете трябва да носят произход, за да може ботът да различава проверено местно знание от външен материал. Индексира се само българският текст, не каталожните метаданни.
приоритет
•
Довършване на ИИ редактора
Генериране на форматирана статия за категория История, стъпила върху книгите и архива, с изображения и галерии; опростен режим „Новини“, в който екипът пише няколко изречения. Индексират се всички Tainacan колекции и собствените статии, за да не се появяват дублирани или противоречиви твърдения.
приоритет
•
Писмо до РБ „Николай Вранчев“ — Смолян
Библиотеката държи пълните сканове на всичките 42 източника, докато в Europeana има само корици. Три искания: достъп до сканите със споменавания на Чокманово, взаимно линкване между двата архива и съвместна кандидатура по програма.
приоритет
•
Карта на местностите
Кеширан GeoJSON endpoint /?chk_toponyms=1 (WPCode) + Leaflet страница с подложки OpenTopoMap и Esri World Imagery.
приоритет
•
Геолокация на местностите
Координати за 6 опорни точки (петте махали + Св. Илия), останалите — по посока и разстояние от тях. Преглед на 62-те реда „спомената в текста“.
приоритет
•
Повторен OCR на речника в том 2
В речниковата глава липсват страници — раздел А прескача от КАЛАМУНСКО на СИСТЕКОВО (Л, М, Н, О, П, Р). Нов OCR на тези страници с по-висока резолюция.
приоритет
•
Front-end за „Дари спомен“
Публична страница за подаване + reCAPTCHA (самата колекция и workaround-ите вече са готови).
приоритет
•
Дарителски канали
Акаунти в Patreon и Buy Me a Coffee.
приоритет
•
Преглед на всички текстове на читалището
Редакция и актуализация на съществуващото съдържание в сайта.
приоритет
Праг по релевантност при извличането
Извличането взима най-близките top_k съседа без праг по score, така че последните може да са хлабаво свързани. Праг би стеснил цитатите до наистина относимите източници.
по-нататък
Правило за съмнителни данни в промпта
Правило 4 лови информация, която липсва; правило 7 бута модела да обяснява връзките между фактите. Заедно значат, че повреден източник не създава видима дупка, а добре написано погрешно твърдение. Ново правило би искало от модела да отбележи очевидно противоречив факт, вместо да го помирява.
по-нататък
Отрязани отговори при широки въпроси
С шест източника и top_k 8 широк въпрос може да опре в тавана от max_tokens 2048 и да се среже по средата на изречението. Досега 1 от 27 отговора при средно 562 токена. Вариантите са по-висок таван (решение за пари), по-меко правило 7 или по-нисък top_k.
по-нататък
Разграничаване на съименници в чатбота
Двама различни Райчо Чакорови (дядо и внук) засега не могат да се разграничат в отговорите.
по-нататък
Cloudflare Turnstile за чатбота
Проверка за реален потребител при отваряне на чата; сесиен токен (HMAC, 60 мин.), проверяван в Worker-а преди всяко извикване на Claude API.
по-нататък
Живи камери в селото
1–2 уеб камери, стрийм в YouTube, вградени на сайта.
по-нататък
Аудио прикачени файлове в История
Възможност постовете да включват звукозапис.
по-нататък
Допълнително втвърдяване на входа за родословието
По-нататъшно подобряване на текущата система с 6-цифрен код + HMAC бисквитка.
по-нататък
Обединяване на родовете в един граф
164-те отделни дървета от износа — в единен граф.
по-нататък
Речник на имената за OCR корекция
Лексикон от имената в GEDCOM корпуса за автоматична корекция на разпознатите имена.
по-нататък
Демографски портрет
Обобщена статистика върху родословната база — първа публична функционалност от нея.
по-нататък
Калкулатор на родство
Определяне на връзката между две лица с българската роднинска терминология.
по-нататък
Карта на миграциите
Върху Leaflet слоя на картата на местностите.
по-нататък
Печатен семеен лист
PDF лист за лице/род.
по-нататък
Колекция „Личности“ в Tainacan
Native колекция, захранваща визуализацията през REST API; кандидат-материал са портретите с имена и години (1882–1930) от Фондация NALIS в Europeana, всички с Public Domain Mark. Дотогава релационното поле „Свързана личност“ в „Външни източници“ сочи само към колекция 961.
по-нататък
Микросайт „За читалището“
Вертикална странична навигация (Gutenberg Columns 30/70, Synced Pattern) със секции: Библиотека, Кафе-читалня, Дистанционна работа, Събития, Кино салон, Екип, Уъркшопи — списъкът със секции предстои да се потвърди.
по-нататък
Правни страници
Политика за поверителност, политика за бисквитки, условия за ползване, лиценз CC BY-NC-SA за съдържанието.
по-нататък
GDPR съответствие
План за анонимизация на живи лица в родословните данни.
по-нататък
Мониторинг и тест за възстановяване
Cloudflare Web Analytics, UptimeRobot, тримесечен тест за възстановяване на UpdraftPlus копие в локален Docker.
по-нататък
OAI-PMH износ на собствените колекции
Обратната посока към Europeana — колекциите на читалището да бъдат обхождани от агрегатори. Националният агрегатор за България е библиотеката във Варна; директното кандидатстване не е пътят.
по-нататък
Страница с отворени данни
Документирани Tainacan REST endpoints + износ в CSV/JSON.
по-нататък
Миграция на нов домейн
Преместване на целия сайт от текущия адрес.
по-нататък
NER извличане на имена
Автоматично извличане на имена/дати от OCR текста за захранване на родословието.
по-нататък
Решение за сирашките записи от колекция 62
20 записа tnc_col_62_item от 22.07.2026 — почистване или оставяне; отделно решение, не част от функционална работа.
по-нататък
Git репозиторий за целия сайт + локална Docker среда
Git вече покрива RAG чатбот проекта (Cloudflare Workers); липсва версионна история и тестова среда за самия WordPress сайт.
по-нататък
Автоматизирани тестове (Playwright)
Проверка на критичните функции при всяка промяна.
по-нататък

Технически стек

CMSWordPress + Blocksy
АрхивTainacan + REST API
ФормуляриWPForms Lite
Код/snippetsWPCode
КешLiteSpeed Cache
Резервни копияUpdraftPlus
OCRPyMuPDF + Tesseract (bg)
RAG износragexport.py
РодословиеGEDCOM парсър (PHP)
Родословие — вход6-цифрен код + HMAC бисквитка
Родословие — дървованилен JS + SVG
МестностиTainacan GeoCoordinate
Карти (план)Leaflet + OpenTopoMap / Esri
Чатбот — инфраструктураCloudflare Workers + D1 + Vectorize
LLM (генерация)Claude Haiku 4.5
EmbeddingsWorkers AI (bge-m3)
Извличанеmulti-query + RRF
Чатбот — индекс6 източника · 1901 чънка
Чатбот — админ гейтCloudflare Zero Trust Access
Чатбот — лимити$2/ден · 20 заявки/IP
СтатииWorker chokmanovo-writer
Външни източнициEuropeana Search / Record API
ХарвестърPHP CLI 8.4.25 (ръчно по SSH)
ХостингSuperHosting
ВремеOpen-Meteo API