# Історія проєкту

Журнал того, **що і чому** змінювалося. Дати до 21 серпня відновлені з
документів і часу файлів — репозиторію тоді ще не було, git-історія
починається пізніше.

Супутні документи: поточний стан — `STATE.md`, відкладені питання —
`BACKLOG.md`, метод роботи — `.claude/agents/lesson-localizer.md`,
розгортання — `DEPLOY.md`.

---

## 14 вересня 2026 — три правки замовника і лічильник, що рахував від сторінки

**Що знайшлося до правок.** STATE казав, що серія A12 лежить у панелі як
`v30`/`v47`/`v03`/`v03`. На сервері ж 12 вересня Редактор застосувала
правки з панелі в рівнях 1–4, і Level1 став `v02`, Level2 — `v19`.
`apply_edits._next_stem` брав стем відкритої сторінки плюс один, а
Редактор натиснула на старій сторінці `v01`. `--archive` прибрав `v30` у
backup, нитки з моїми відповідями лишилися під `v30`, панель на `v02` їх
не бачила. Восьмий випадок того самого роду, і вказівкою цього разу була
адреса в браузері. Тепер номер — більший за всі відомі: тека інтерактиву,
`backup/`, покажчик сторінок.

**Правки.** «L 3 A12» на стор. 8 Level3 — блок копірайту просто не був
перекладений, перебрендування зрізало англійський рядок; додано, як в
інших рівнях. Сіра емблема — не сіра: у файлі лише білі пікселі, а сірість
давало зменшення тонких штрихів до 34 pt. Замість «замінити сірий на
білий» штрихи потовщено на 2 px у `brand.py`; 3 px уже злипали літери.
Руки Ангола на стор. 2 Level0: в оригіналі «з'єднай крапки» рук не має,
Редактор обвела їх областями, дитина не бачила їх до кліку. Крапки по
контуру тепер малює `artdots.py` в екранній збірці, читаючи набір областей
буклета, а не копію координат: якщо Редактор поправить руки, крапки підуть
за ними; якщо перейменує — збірка зупиниться і скаже.

**Оберт.** П'ять рівнів через `apply_edits.py` на маку, по 18–40 с кожен;
поля збіглися з попередньою збіркою до одиниці. Рівні 1–4 викладено в
панель без перезапуску контейнера: у ту саму годину Редактор працювала над
стор. 2 Level0, і її набір уже відрізнявся від мого. Рівень 0 і перезапуск
відкладено до її виходу — перше правило: злиття, а не заміна.

## 8 вересня 2026, вечір — Level1_A12 перекладено, детектор побачив рівень 1

**Переклад.** `tr_Level1_A12.py` — ті самі чотири історії, що в Level2_A12,
формулювання, які збігаються, взято звідти дослівно. Рівень 1 верстається
по рядку на блок, тож перенос — ручний, а праві межі задані явно: на стор. 1
і 3 історія стоїть у вузькій колонці поруч із малюнком, і типова межа
508 pt пустила б текст під нього. Мовозалежне: `John.` по крапках → `Іван.`;
вірш Луки 1:37 у два рядки по 40 pt (48 не вкладаються), суцільним —
«неможливої»; пропуски з першою літерою переставлено під українські
довжини (ІСУС, СИН БОЖИЙ, НАЗАРЕТ, ВІФЛЕЄМ, «Місця немає!»). Одна помилка
першої збірки: два рядки «Доповни напис…» ліг у ОДИН блок, а другий блок
лишився англійським — у шаблоні це два однорядкові блоки. `qa.py` навчено
приймати в `GROW_OK` і зростання вліво: напис, центрований у рамці, а не на
англійському якорі, — рішення, а не дефект.

**Детектор не бачив завдань рівня 1 взагалі.** Інвентаризація трималася
за маркер `Q:`, якого в рівнях 0–1 немає: Level1_A1 в інтерактиві так і
пішов — 81 віджет без жодного завдання, без чекбоксів «виконано», без
варіантів. Тепер голова завдання — рядок фірмового кольору цілком, від
лівого краю колонки або з дієсловом зі словника (§3.3, п. 12). Ще двоє
правил з того ж буклета: інструкція-підпис під віршем (п. 13) і кілька
рядів рамок з варіантами (п. 14). «Напиши ТАК або НІ» — `bool` зі
списком ТАК/НІ. Результат: 98 полів замість 44, усі під своїми
завданнями. Перевірено на Level2–4_A12 — ті самі поля, крім трьох рамок
ТАК/НІ в Level2_A12, які стали списками.

**9 вересня, ранок — два інструменти для Редактора.** На стор. 2 діти на
папері малюють у фігурах, а на екрані мають писати. Раніше таке місце
оголошував розробник (`EXTRA_FIELDS`); тепер у панелі є кнопка «Текстове
поле»: пунктирний прямокутник із вісьмома маркерами, який Редактор ставить,
пересуває й тягне сам. Поле лежить у тому самому наборі, що й області
(`fields`), і накладає його той самий `art_apply.py` — набір без областей,
лише з полями, теж годиться. Друге: інструкцію «Намалюй…» треба замінити на
«Запиши…» лише в інтерактиві. Доти позначити це Редактор не міг — правка
йшла і в друк. Тепер у режимі «Текст» є позначка «тільки для інтерактиву»:
правка лягає в екранний накладний файл, друк лишається, а після перезбірки
блок показується довгим штрихом, як блоки `SCREEN_TEXT`. Перевірено в
локальній панелі й через `art_apply` на Level1_A12: два поля, повторне
накладання не подвоює.

## 10 вересня 2026 — Level0_A12, останній буклет серії A12

Чотири сторінки, майже без тексту. Переклад дослівно узгоджено з рівнями
1–2 там, де речення збігаються; блоки «Adult Guidance» стали «Для
дорослого». Мовозалежне одне: «His name is / John.» на сувої → «Його ім'я /
Іван.» по крапках. Дві дрібниці збірки: інструкція «З'єднай крапки…» на
стор. 3 наїжджала на сувій — скорочено до «З'єднай крапки. Що написав
Захарій?»; таблиця балів у рівні 0 стоїть на 2.9 pt нижче, і «Додаткові
бали» малюються зі зсувом.

Детектор на рівні 0 дав дві хиби, обидві виправлено правилами: блок «Для
дорослого» (8 pt, фірмовим кольором) ставав завданням — тепер дрібний
кольоровий текст інструкцією не вважається; петля дороги на карті ставала
«рамкою відповіді» — у завданнях «проведи лінію / намалюй» без пропусків
рамки полів не дістають. На Level1–4_A12 поля не змінилися.

Заразом за день у панелі: «Текстове поле» і «тільки для інтерактиву»
для Редактора, автозбереження кожні 30 с, рожева/зелена «Зберегти»,
чернетка в браузері, ім'я набору тепер жорстко від буклета й сторінки
(полів «файл областей» і «завдання» більше немає), превʼю «показати
розмальовку» показує й залиті рамки. Чекбокс «виконано» прибрано назавжди;
«розфарбуй» з рамками дає заливку замість овалу. Стор. 5 Level1_A12 в
інтерактиві дістала гостей із малюнків програми (`cut_figures.py`).

---

**Правка «Обведи» → «Розфарбуй» зникла після «Застосувати правки».** Дві
причини, і обидві — вказівки. Перша: панель після застосування
перечитувала сторінки СТАРОГО стема, хоч перезбірка дає новий номер;
`api_apply` тепер віддає новий стем, і панель переходить на нього, як
після «Перезібрати PDF». Друга, гірша: у контейнері `apply_edits` клав
інтерактив у `data/lessons/uk/interactive` — шар контейнера, не том. PDF
зникав з наступною перезбіркою образу, а `art_apply` тієї теки й не
переглядав, тож «Перезібрати PDF» брало за базу стару версію з `forms/`
без правки. Тепер на сервері інтерактив і друк збираються в `forms/`
(том), теки береться з `lessons.py`, а номер версії наскрізний по всіх
теках. Побічно: у `forms/` Level3_A12 і Level4_A12 досі лежать v01 без
частини правок Редактора — їх треба перезібрати повним обертом.

**Чекбокса «виконано» більше немає.** Рішення замовника, узгоджене з
Редактором: кожне завдання матиме справжній інтерактив — області
розмальовки або текстове поле, які Редактор ставить сама. Детектор
заглушку не ставить, а завдання без полів називає в звіті як роботу для
Редактора. Скасовує рішення від 3 вересня. У наявних PDF квадратики
зникнуть при наступній перезбірці з панелі; на Level1_A12 полів стало 89
замість 98, на Level2–4 нічого не змінилося.

**Викладено в панель, і одразу знайшлася восьма помилка того ж роду.** На
стор. 1 Level1_A12 панель відкрила області Редактора з Level2_A12: набори
названі за сторінкою (`regions_p01_…`), а буклетів у панелі кілька. Гірше,
що «одна сторінка — один набір» відклало б чужий набір у `.old-` при
першому ж збереженні, а `regions_p03_q1_auto.json` збігся б у двох
буклетах. Тепер покажчик віддає буклет набору (поле `pdf` без версії),
панель показує лише свої, відкладання чіпає лише свої, а нові файли від
розпізнавання містять ім'я буклета. Фільтр довелося переносити з читання
покажчика на показ списку: покажчик читається раніше, ніж панель дізнається
стем сторінки. Перевірено в браузері з обох боків.

---

## 8 вересня 2026 — розмальовка ілюстрацій стала робочим інструментом

День почався з двох правок у буклеті, а закінчився тим, що Редактор
розмалювала п'ять сторінок сама, без розробника. Дорогою знайшлося вісім
помилок, і сім із них — одного роду.

**Level2_A12.** Чекбокс «виконано» знято із завдання, яке стало
розмальовкою, і не вручну, а правилом: завдання, для якого Редактор обвела
області, — виконуване. Детектор дізнається про це з наборів у панелі
(`_declared_art`), бо сам їх не бачить: області накладаються ПІСЛЯ нього.
Заразом контрольна перевірка §3.7.2 навчена не кричати «БЕЗ ПОЛІВ» на
завдання-розмальовку.

**Кнопка «Розпізнати області» дійшла до кінця.** Раніше вона робила PDF під
новим номером — і на цьому все: сторінки панелі, покажчик тексту й нитки
лишалися при старому імені, тож Редактор своєї роботи не бачила. Тринадцять
натискань поспіль не дійшли нікуди. Тепер сервер сам перемальовує сторінки,
переносить покажчик і нитки, а панель переходить на нову версію й переписує
адресу.

**Своя палітра замість системної.** `input type=color` закривається за
першим дотиком і одразу застосовує колір; Редактор просила водити по полю,
бачити обране й застосовувати кнопкою. Зробив вікно: поле-спектр (відтінок ×
насиченість), планка світлості, зразок у лівому нижньому куті, HEX,
«Скасувати» і «Застосувати». Поділ величин довелося переставити двічі:
спершу планка міняла насиченість — для світлих кольорів вона виглядала
порожньою; потім поле темніло разом із планкою — під пальцем пливла вся
палітра. Сталий набір із десяти кольорів прибрано: тепер кнопка вибору
перша, за нею «змійкою» кольори, які Редактор уже брала.

**Сім помилок одного роду: мовчазне використання застарілої вказівки.**

* відповіді Редактора в нитці сервер відкидав (запобіжник проти
  протилежної біди виявився надто грубим);
* набране в полі не рахувалося незбереженим, і ↻ стирав його — причому я
  ж сам радив натиснути ↻;
* `artseg` перезаписував файл областей цілком: розпізнала Єлисавету —
  Захарія не стало; розпізнала храм — не стало Єлисавети;
* `fields_build --archive` завжди давав `_v01`, бо архів щойно спорожнив
  теку;
* набір областей указував на конкретну версію PDF, і перезбірка мовчки
  брала стару базу;
* прибирання областей за точним префіксом не бачило порції, накладеної
  колись під іншим номером завдання: на стор. 1 назбиралося 42 області
  замість 22;
* два набори на одну сторінку (від перейменування завдання) — і при
  перезбірці безіменна заготовка затирала тринадцять названих і
  розфарбованих областей ангола. Цю втрату спричинила моя ж зміна,
  зроблена того самого дня.

Спільне правило записане в скіл: **там, де людина працює накопичувально,
запис має бути злиттям, а не заміною; а якщо в системі є вказівка на щось
версійоване — питай, хто її оновлює.**

**Восьма — не з цього роду, і найповчальніша.** Вікно кольору показувало
саму палітру без кнопок. Кеш ні до чого: у стилях панелі жило правило
`canvas{position:absolute;inset:0}` для полотна СТОРІНКИ, і воно зачепило
поле палітри — те вийшло з потоку й накрило собою кнопки. У розмітці все
було на місці, сервер віддавав правильний файл. Знайшлося за хвилину, щойно
я відкрив панель у браузері й подивився на обчислені стилі замість
вихідного коду. Двічі до того я шукав причину не там.

**Підсумок дня.** Редактор обробила п'ять сторінок: 22 + 26 + 9 + 13 + 17
областей, усі названі, кольори свої. `Level2_A12_UA_v35` у панелі.

---

## 7 вересня 2026, вечір — розмальовка: від одного кольору до інструмента

День почався з коректури Level2_A12, а закінчився новим шматком конвеєра.
Три зауваження Редактора закриті (порожній рядок між підказками й «Божий»,
«жодної» у вірші по крапках, «здивовані» замість «вражені»), але цікаве
почалося після них.

**«Розфарбуй» було не розмальовкою, а підсвіткою.** Механіка працювала —
кожна літера окреме поле, — але колір був один на завдання. Дитина тицяла
двадцять разів і бачила рівний жовтий прямокутник із літер. Тепер колір на
кожну літеру, і рахується він за золотим перетином: крок 0.618 кола ніколи
не повторюється (для вірша з 36 літер усі 36 кольорів різні) і розводить
сусідів якнайдалі. Послідовний обхід кола дав би градієнт, а не розмальовку.

**Редактор не бачила цих кольорів** — і справедливо про це сказала: панель
рендерить сторінку з вимкненими полями, інакше показувала б заповнений
буклет. Тепер поруч лежить друга картинка з увімкненими літерами, а в
навігації є кнопка «показати розмальовку».

**Далі виявилося, що малюнки не оброблені взагалі.** Механізм клікабельних
областей був готовий іще з першого буклета, але області там обводив
Редактор руками — 30–40 хвилин на малюнок. Порахував обсяг: **317 завдань
«розфарбуй малюнок»** у програмі, 237 із них на рівнях 0 і 1. Ручним
обведенням це 160–210 годин.

**Замовник запропонував інструмент: людина позначає місце, агент обробляє.**
Ідея слушна, і до неї додалася друга — рахувати, скільки пікселів бракує до
замикання контуру. Інстинкт вірний, реалізація інша: контурів як шляхів у
цій графіці немає (кожна лінія — тонкий залитий багатокутник), тож розриви
замикаються морфологічно — лінії розширюємо, ділимо площину, повертаємо
мітки назад.

Проба на чотирьох ілюстраціях: **99–100 % покриття**, 9–26 областей,
менше секунди на малюнок. Ручне обведення першого буклета дало 97 % при 35
областях. Ланцюг перевірено наскрізь: рамка → 17 областей → 324 віджети →
дитина тицяє, малюнок фарбується, штрихи цілі, швів немає.

**Чотири помилки за день, і всі одного роду — «написано одне, працює інше».**

* сервер панелі мовчки викидав відповіді Редактора з нитки. Запобіжник
  ставили проти протилежної біди («не затирати відповіді розробника») і
  зробили надто грубим;
* набране в полі не рахувалося незбереженим, тож ↻ стирав його без
  попередження — і я ж сам радив натиснути ↻. Редактор двічі втратила
  написане;
* дві функції `gotoPage`, друга перекривала першу, і області з однієї
  сторінки малювалися на всіх. Коментар при другій обіцяв рівно те, чого
  вона не робила;
* `fields_build --archive` завжди давав `_v01`, бо архів щойно спорожнив
  теку. Збігалося з дійсністю випадково, доки стем не став `_v02`.

Спільне в них одне: правило жило в голові або в коментарі, а не в коді.
Три з чотирьох знайшла Редактор, а не перевірки.

---

## 7 вересня 2026, друга половина дня — Level2_A12: «ясла» не влізли в шість запитань

Третій буклет зимової розсилки, перший на рівні 2. Той самий матеріал, що й
у Level3_A12, але для молодших: більший кегль, менше тексту, більше малюнків.
Головоломок чотири, і жодна не пережила перекладу.

**Сітка-акростих не складається, і винна довжина українського слова.**
На стор. 8 шість запитань перетинає надруковане `MANGER` — по літері на
відповідь. Українською ясла — це `ЯСЛА`, чотири літери на шість запитань.
Різати запитання не можна: з ними поїдуть бали `/12`, а бали — облік
учителя. Тож змінили не запитання, а **ключове слово**: `РІЗДВО`. Кожна
літера є у своїй відповіді (`МаРія`, `галІлея`, `наЗарет`, `заїзД`,
`Віфлеєм`, `йОсип`), а для грудневої розсилки слово доречніше за «ясла».

**Кросворду довелося відпустити напрямки.** В оригіналі два слова вниз, два
впоперек. Але в `ГАВРИЇЛ` немає ні `І` (там `Ї`), ні `С` — тобто `ІСУС` не
чіпляється до жодного горизонтального слова, і зв'язної сітки при
закріплених напрямках не існує взагалі. Підказки в макеті йшли суцільним
списком без заголовків, тож перестановка нічого не зламала; заголовки
«По вертикалі / По горизонталі» додано за правилом від 3 вересня.

**Акростих `ІВАН` — уперше всі малюнки з перевірним джерелом.** `JOHN` — це
jug, owl, hat, nail; українською жодне не годиться. Взяли індика, відро,
ананас і ножиці, усі з Openclipart під CC0. Попередні три малюнки
(ілюмінатор, екскаватор, умивальник) прийшли знімками екрана, і їхня
ліцензія досі відкрите питання до друку — тут його вже немає.

Перший індик був контурний, з розпущеним хвостом, і на 42 pt читався як
голуб або павич. Замінено на кольорового з борідкою: правило «як назве цей
предмет дитина, не знаючи відповіді» коштувало вже трьох малюнків, і
дешевше перевірити його очима, ніж вислухати від Редактора.

**Три знахідки в конвеєрі, всі — «код бачив лише один із двох випадків».**

* `framed_options()` шукала варіанти тільки в РЯД. Три однакові рамки одна
  під одною («розфарбуй правильне закінчення речення») падали в чекбокс
  «виконано». Тепер групування йде і за колонкою;
* рамка всередині сувою ставала текстовим полем — порожній жовтий
  прямокутник посеред малюнка. Правило: якщо завдання вже має пропуски, а
  рамка жодного з них не містить, це малюнок, а не місце відповіді;
* `quotes_check.py` порівнював регістр. Напис «"Почута молитва твоя."»
  відкриває речення з великої літери, а в Огієнка воно стоїть посеред
  вірша («...бо почута молитва твоя») — перевірка сварилася на верстку.

Ані Level3_A12, ані Level4_A12 від цих змін не зрушили: 233 і 186 полів,
як були.

**Пунктирний вірш на екрані став заливкою літер.** «З'єднай крапки» тапом
не зробиш, зате обведення (`render_mode = 1`) детектор бачить як контурні
літери й дає завдання `paint`: дитина проводить пальцем по кожній літері.
Суть та сама, тож інструкцію на екрані переписано через `SCREEN_TEXT`.

**Що лишилося неоднозначним.** Три квадратики «постав галочку або хрестик»
на стор. 2 в інтерактиві стали трьома незалежними чекбоксами — так велить
правило замовника про окремі квадрати. Але хрестика чекбокс не дає:
позначене = правильне, порожнє = неправильне або не зроблене. Розрізнити їх
на екрані нічим. Символи `✓` і `✗` є лише в Arial Unicode, а шрифт полів
форми — Helvetica, тож списком вибору цього теж не обійти. Питання до
Редактора.

Друк `Level2_A12_UA_v04.pdf` (`qa.py` — самі `GROW`, усі оголошені),
інтерактив `Level2_A12_UA_v02.pdf` — 231 поле, круговий тест 222/222,
чекбокс «виконано» один (намалюй і розфарбуй).

---

## 3 вересня 2026 — Level3_A12: буклет, зроблений із головоломок

Панель уранці перебудовано на плаваючі вікна, далі — другий буклет зимової
розсилки. Він виявився втричі важчим за Level4: **шість мовозалежних
головоломок замість двох**.

**П'ять перебудовано, і жодну не можна було просто перекласти.** Кросворд
(сім слів), філворд 9×8, анаграма «Господь із тобою», переставляння слів
Луки 2: 14 і драбинка «Анна». Скрізь причина та сама: довжини слів інші,
тож перетини не сходяться — стару сітку закриваємо й складаємо нову.

**Драбинка змусила написати окремий укладач.** Загальний відмовлявся її
класти, і слушно: він не ставить два горизонтальні слова на сусідні рядки,
бо в кросворді це дало б паразитні вертикальні пари. Але драбинка саме на
цьому й побудована — чотири слова одне під одним, прошиті вертикальним.
У Level3_A1 це не спливло випадково: там вертикальне слово мало десять
літер, і горизонтальні розійшлися по рядках самі.

Третє слово драбинки довелося переклюсувати. Англійське `thanked`
українською «славила», а в ньому немає «Н», якою чіпляється «Анна».
Питання взяте з того самого вірша 38, але про «визволення».

**Огієнко перейменував двох.** `Захарія` в нього `Захарій`, `ангел` —
`Ангол`, а Симеон із Луки 2 — **`Семен`** (вірш 25: «йому ймення Семен»).
Останнє замовник підтвердив окремо: дитина має знайти в тексті те саме
слово, яке шукає. Це ж слово задає й акростих на стор. 7.

**Тиха втрата, яку впіймало порівняння з оригіналом.** Варіанти вибору
(«царем / фарисеєм / рибалкою / священиком») були розставлені координатами
— і чотири завдання впали в чекбокс «виконано». Детектор ріже такий рядок
або по окремих спанах, або по прогалинах у кілька пробілів; координатна
розстановка зливає все в один суцільний спан. Виявилося це не з вигляду
сторінки — вона бездоганна, — а з того, що **той самий детектор на
англійському оригіналі дає там нормальні поля**. Тобто втрату вніс саме
переклад. Правило: коли завдання падає в «виконано», спершу прогнати
детектор по оригіналу — це відрізняє ваду конвеєра від вади перекладу.

Після виправлення чекбоксів «виконано» лишилося п'ять замість дев'яти, і
сторінка 1 працює краще за оригінал, де це завдання теж провалювалося.

Друк `_v10` — `qa.py` 0 зауважень, обидві цитати збігаються з Огієнком.
Інтерактив — 232 поля, круговий тест 218/218.

**Відкрито: акростихи з малюнків.** Замовник вирішив не міняти механіку, а
підібрати нові зображення — рішення діє й на Level2_A12. Виявилося, що
дешевше, ніж здавалося: оригінал сам повторює малюнок (змія двічі), тож
повтори дозволені. «ІСУС» — індик, сонце, удав (наявна змія), сонце;
«СЕМЕН» — сова, ескімо, місяць, ескімо, ножиці (перші чотири вже є в PDF).
Нових зображень треба три. В обох випадках клітинок на одну більше, ніж
літер, — зайву доведеться закрити.

## 3 вересня 2026 — день, коли Редактор став співавтором конвеєра

Level3_A12 пройдено наскрізь, але головне за день — не буклет. **Дев'ять із
зауважень Редактора виявилися не помилками перекладу, а прогалинами
конвеєра**, і кожне з них полагоджено правилом, а не латкою в уроці.

**Панель уранці перебудовано на плаваючі вікна.** Одна висока колонка не
лізла в планшет: список областей, поля коректури й довідка билися за ту саму
висоту. Тепер це чотири вікна за зразком палітр Photoshop; кожне тягнеться
за планку й згортається в неї ж. Панель масштабу прибрано — за півтора
місяця нею не скористалися жодного разу.

**Level3_A12 виявився втричі важчим за Level4:** шість мовозалежних
головоломок замість двох. Кросворд, філворд 9×8, анаграма, переставляння
слів, драбинка «Анна» — усе перебудовано під українські довжини. Драбинці
знадобився власний укладач: загальний не ставить горизонтальні слова на
сусідні рядки, бо в кросворді це дало б паразитні вертикальні пари, а
драбинка саме на цьому й побудована.

**Огієнко перейменував двох:** `Захарія` → `Захарій`, `ангел` → `Ангол`, а
Симеон із Луки 2 — **`Семен`**. Останнє замовник підтвердив окремо: дитина
має знайти в тексті те саме слово, яке шукає.

### Що зламалося тихо — і чому це головне

Сім дефектів за день **не бачила жодна перевірка**, їх знайшов Редактор
очима. Це й є висновок дня: `qa.py` стереже геометрію друкованої сторінки й
сліпий там, де вона бездоганна.

* **Напис «ЗОЛОТИЙ ВІРШ:» виїжджав за бокс** на порожній папір. Накладання
  ні на що немає, права межа далеко — усі перевірки мовчали. Додано клас
  `BOX`: блок, який сидів у намальованій рамці, має там і лишитися. Новий
  клас одразу знайшов той самий дефект у Level2_A1, учетверо гірший.
* **Звірка цитат читала файл перекладу, а не надруковане.** Правки Редактора
  лягають поверх — і саме через них цитата може змінитися. Тобто перевірка
  мовчала рівно в тому випадку, заради якого існує. Спливло, коли Редактор
  виправив «жадної» на «жодної» в Луки 1:37 (це слово Огієнка) і правку
  прийняли без звуку.
* **Панель стирала нитки відповідей.** Вона зберігає документ цілком —
  рішення від 1 вересня, ухвалене до появи ниток. Редактор відкривав
  сторінку до моєї відповіді, зберігав після — і відповідь зникала мовчки.
  Так загубилися дві.
* **Правки на блоки з лівим якорем не застосовувалися.** У панелі текст
  новий, на сторінці старий. Причина: `x` третім елементом сегмента означає
  «координату пораховано з ширини тексту», і такі рядки `edits.py` навмисно
  не чіпає. Але тут це був звичайний лівий якір — перенесено в опцію рядка.
* **Фантомні поля з нізвідки.** Стару сітку кросворда закривають дві білі
  смуги (суцільною одна бути не може — заважає персонаж), і шов між ними
  пройшов рівно по ряду клітинок. Жодна не лежала цілком в одній смузі,
  хоча разом вони закривали її повністю. Перевірка z-order шукала входження
  в ОДИН прямокутник — тепер рахує покриття сукупністю.
* **Біла смуга зрізала низ літер** рядка над нею. Виправляється підйомом
  блока, а не звуженням смуги.
* **Невидимі криві фігури** всередині рамки змусили детектор вважати її
  ілюстрацією — і порожня таблиця на три рядки лишилася без полів.

### Чекбокс «виконано»: з дев'яти до нуля

Уранці буклет мав дев'ять завдань, які не стали полями. Редактор двічі
спитав, що це за квадратик, і запропонував прибрати його зовсім. Ми
обговорили й лишили — це єдиний слід таких завдань в інтерактиві, дитина
має чим відзвітувати, — але тепер він **пояснює себе підказкою**.

А потім з'ясувалося, що прибирати його треба інакше: **вісім із дев'яти
завдань виявилися робочими** після виправлень у детекторі. Однослівні мітки
(«Кесар □»), варіанти в хмаринках замість рамок, нумерація стовпчиком
замість ряду, і нарешті «проведи лінію» — його замінили списками вибору.
Наприкінці дня чекбоксів **жодного**, і в скілі записано межу: чекбокс — це
остання лінія, а не відмовка; більше двох у буклеті означає, що щось не
розпізналося.

### Дві версії тексту — і чим за них платимо

«Проведи лінію» на екрані звучить безглуздо. Спершу переписали інструкцію
один раз для обох носіїв, свідомо відмовившись від розділення: панель
показує інтерактив, тож Редактор вичитував би екранну версію, а друкована
розходилася б тихо.

Редактор погодився платити цю ціну за умови, що бачитиме друковану версію —
і запропонував недрукований символ у PDF. Зробили **не в PDF, а в панелі**:
символ у файлі покладався б на те, що друкарня його не надрукує, а це ризик,
який нікуди не подінеться. Тепер урок оголошує `SCREEN_TEXT`, з нього
збирається окремий PDF, а панель показує обидва тексти й фарбує такий блок
довгим штрихом. Обидва **редаговані**, і кожен має свій накладний файл:
правка екранного тексту, потрапивши в друкований файл, мовчки замінила б
те, що йде в друкарню.

### Малюнки

Акростихи з малюнків не переносяться: літери задані назвами предметів.
Замовник вирішив міняти зображення, а не механіку. Виявилося дешевше, ніж
здавалося — оригінал сам повторює малюнок, тож із наявних у PDF склалося
майже все.

Урок, вартий запам'ятовування: **назва предмета має бути єдиною очевидною**.
«Ескімо» провалилося — і Редактор, і замовник назвали його «морозивом», тобто
«М», і акростих не складався. Перевірка проста: спитати себе, як назве цей
предмет дитина, не знаючи відповіді. Той самий ризик лишається в
«ілюмінатора»: хтось скаже «вікно».

Друк `Level3_A12_UA_v37.pdf` — `qa.py` 0 зауважень, цитати збігаються.
Інтерактив — 247 полів, круговий тест 234/234, чекбоксів «виконано» жодного.

## 2 вересня 2026 — коректура стала самостійною

День пішов не на буклети, а на те, щоб **правка Редактора доїжджала до PDF
без розробника**. До ранку 1 вересня її треба було переносити руками в файл
перекладу; на вечір 2 вересня Редактор натискає кнопку в панелі й за
11 секунд бачить виправлену сторінку.

**Крок 1 — коректура переїхала в саму панель.** Спершу вона була окремою
сторінкою, і це виявилося хибним: Редактор працює на планшеті стилусом, а
перехід на інший документ губить масштаб, положення аркуша й контекст
завдання, до якого текст належить. Тепер це другий режим тієї самої панелі —
перемикач **Області / Текст**. Текст не розпізнається з картинки: покажчик
збирається з `layout.json`, `template.json` і файлу перекладу, тож
координати точні до пункта, а текст — саме той, що надрукований. У поле
правки **підставляється сам текст блока**: виділяти й копіювати стилусом
незручно, і за це заплатили б на кожній правці.

**Крок 2 — правка накладається як дані.** `edits.py` підставляє новий текст
у наявну специфікацію, лишаючи стилі, координати й розбивку на сегменти.
Модель у цьому обороті не бере участі взагалі. Береться лише те, де
підстановка однозначна; решта лишається розробникові **з написаною
причиною** — і ця межа проведена не з обережності: рядок, центрований
вручну, має координату, пораховану по ширині старого тексту, тож тиха
підстановка зсунула б напис, а геометричний контроль такого зсуву не ловить.

**Найдорожча помилка дня — правки були тимчасовими.** Оберт накладав їх на
одну збірку й очищав файл зауважень. Тобто будь-яка наступна перезбірка —
з будь-якої причини — **мовчки відкочувала все, що Редактор виправив**: у
тому конкретному PDF правка була, у джерелі її не було ніколи. За день так
загубилося п'ять правок; відновилися вони лише тому, що перенесені
зауваження складалися в `editor/applied/`. Тепер застосоване лягає в
постійний `editor/edits_<буклет>.json` і накладається при **кожній**
збірці — файл перекладу лишається тим, що написав розробник, а виправлене
Редактором лежить окремо, видно в git і переглядається.

Латка з першого разу не спрацювала, і знайшов це Редактор: постійний файл
поповнювався **після** збірки, а покажчик тексту читав його **до** — панель
показувала старий текст поруч зі свіжою картинкою. Порядок виправлено,
додано відкат: якщо якийсь крок оберту впав, постійний файл повертається до
попереднього стану, інакше невдала правка осіла б у ньому назавжди.

**Зауваження стало розмовою.** Доти воно було однобічним: Редактор писав, а
відповідь ішла в обхід — через замовника; три питання за один день коштували
трьох обертів через посередника. Тепер це нитка (`ua_build/reply.py`), і
відповідь лягає поруч із зауваженням. Заразом розділено два поля: **правка
тексту** йде в урок, **зауваження** — ніколи. Доти поле було одне, і
зауваження, написане в нього, для однорядкового блока проходило як звичайна
правка, тобто друкувалося в буклеті замість тексту. Геометричний контроль
такого не ловить: текст як текст.

**Огієнко замість Турконяка на весь проєкт** — рішення замовника після
першої ж правки Редактора. Разом із ним з'явилася машинна звірка:
`quotes_check.py` бере оголошені уроком цитати й порівнює з текстом
дослівно, прощаючи лапки, розриви рядків і тире, але не жодного слова. Це
те саме для тексту, чим `qa.py` є для геометрії — і саме воно дозволяє
пускати правку цитати в автоматичний оберт: виправити її Редактор може, але
тільки на правильну.

**Оцифровані джерела Огієнка брехали, і це доведено, а не відчуто.** Три
незалежні сайти дають у Матвія 1:21 «даси Йому **йменна** Ісус». Підрахунок
по всьому корпусу: «ймення» — 337 разів, «йменна» — двічі. Помилка
оцифрування зафіксована в таблиці `ERRATA` разом із доказом.

**Глосарій:** «Учитель» → «Вчитель», «ключовий вірш» → «золотий вірш»,
«Біблійне читання» → «біблійний текст», `journey` → «подорож», а не «дорога».
Останнє знайшов Редактор — і не просто вибрав краще слово, а **відновив
оригінал**: автор скрізь пише `journey`, переклад це стер до «дороги».
Пошук по таких замінах ведеться **за коренем, а не за фразою**: «ключовий
вірш» не знаходить «ключового вірша» і «Ключові вірші». Кожне місце
перевіряється окремо — «дорогу до дерева життя» в Буття 3:24 чіпати не можна.

**Сітку слів на стор. 2 перебудовано**: під Огієнком дві відповіді з семи
змінили довжину.

**Підсумок роботи Редактора за день:** чотири сторінки з восьми, сім правок
лягли автоматично, решту зауважень (перебудова сітки, вибір терміна)
переніс розробник. Співвідношення приблизно **дві третини автоматично** —
саме те, заради чого будувався оберт.

Проміжні файли: друк `Level4_A12_UA_v27.pdf`, інтерактив
`Level4_A12_UA_v01.pdf` (181 поле). Обидва контролі чисті.

## 1 вересня 2026 — Level4_A12 наскрізь

Перший буклет зимової розсилки пройдено від англійського оригіналу до
інтерактиву: друк `_UA_v10` (геометричний контроль чистий), інтерактив
`_UA_v01` — 163 поля, круговий тест 160/160, жодного чекбокса «виконано».

**Акростих уперше лишився акростихом.** Англійське TREASURES українською не
збирається: перші літери задані самими відповідями, і жодне осмислене слово
в них не лягає. Спрацював зворотний хід — **спершу слово, потім підказки під
нього**: `ПОДАРУНОК` теж із дев'яти літер (бали `/9` ті самі), теж по темі
(«Дари мудреців»), усі дев'ять відповідей із Матвія 2. Доти акростихи двічі
замінювали письмовим завданням.

Але головне за день — не буклет, а шість дірок у конвеєрі, які він показав.
Найдорожча: **`qa.py` весь час перевіряв не той документ** — шаблон, джерело
й зібраний PDF були вписані в нього константами від пілотного буклета
Level 2, тобто «обов'язковий геометричний контроль» на всій серії A1
порівнював чужий файл. Далі: логотип лягав на бокс балів там, де сторінка
починається завданням; табуляція друкувалася порожнім квадратиком; і п'ять
правил детектора, кожне з яких давало ту саму видиму поразку — завдання без
полів і чекбокс «виконано» замість них (хмаринка репліки як овальна рамка,
поріг ширини рамки, умовна шапка, «1. 2. 3.» як три поля).

Шосте — не правило, а розподіл відповідальності. Є місця, де дитина пише, а
геометрія про це не каже: напис на дороговказі. Автоматичне правило «велика
біла площина всередині малюнка» поставило б поле й на ясла, і на будинки.
Тому такі місця тепер оголошує сам урок — `EXTRA_FIELDS` у файлі перекладу.

Серія A1 від усіх змін не постраждала: ті самі 196 / 152 / 252 поля на
рівнях 2–4, на рівнях 0 і 1 — на одне більше (хмаринка, яка доти лишалася
без поля).

## 1 вересня 2026 — Скіл 1 на серії A12

Порядок робіт змінено замовником: **A12 → B1 → B2**, зимова розсилка
інструкторам по Україні. Серія A2 відкладена.

Розбір п'яти буклетів A12 показав, що структурно вони — ті самі A1. Натомість
знайшлися дві речі, які дали б **мовчазну** помилку, а не падіння:

1. **Фірмовий колір належить буклету, а не рівню.** Константи в `lessons.py`
   зняті з A1 і на рівнях 2–3 тримаються всюди, а на рівнях 0, 1 і в
   Level4_A2 колір гуляє. Детектор варіантів відповіді звіряє колір точною
   рівністю — розбіжність в один байт означає «варіантів немає». Тепер колір
   міряється по самому буклету, константа рівня лишилася запасом; той самий
   виміряний колір іде в логотип.
2. **`SassoonInfantRg`** — суцільний варіант дитячого шрифту, у A1 не
   траплявся. У Level1_A12 стоїть упереміш із пунктирним в одному рядку: одне
   слово вірша вже надруковане як опора.

Правило, яке з цього лишилося: перед новою серією звіряй **усі** шрифти й
кольори з конфігом. Незнайомий шрифт з'являється одним спаном на восьми
сторінках, а колір узагалі не видно оком.

Звіт — `STRUCTURE_A12.md`.

## 1 вересня 2026 — панель у мережі

**https://ipdf.bibletime.org.ua** — панель обведення областей працює на VPS,
сертифікат Let's Encrypt до 30 листопада 2026.

Розгортання пішло не за планом, і кожна хибна гіпотеза коштувала кроку:

1. **Панель не знаходила сторінку.** `regions.html` тримала жорстко вписане
   `DEFAULT_IMG` зі стемом `v07`; у контейнері сторінки рендеряться з `v06`.
   Тепер без `?png=` панель питає `/api/index` і бере те, що є насправді.
2. **Виправлення не доходило до браузера.** Том у `/app/editor` заморозив не
   лише дані, а й **код** панелі — `git pull && up --build` оновлював образ,
   а сторінка лишалася стара. Образ тепер несе еталонну копію в
   `/opt/editor-dist`, entrypoint кладе її поверх тому.
3. **Домен мовчав.** Порти 80/443 відкриті, `http://` дає 308 на `https://`,
   а TLS падає без сертифіката. Три версії поспіль виявилися хибними —
   порожній `PUBLIC_HOST`, потім DNS контейнера, — доки не з'ясувалося, що
   відповідав **чужий системний Caddy**, який уже тримав ці порти для інших
   сайтів. Той самий заголовок `Server:`, той самий редирект — ззовні
   відрізнити було нічим.

Підсумкова конфігурація виявилася простішою за задуману: контейнер слухає
лише `127.0.0.1:8765`, назовні його віддає системний Caddy хоста. Профіль
`public` із власним Caddy лишився в `docker-compose.yml` для порожнього VPS.

**Правило, яке з цього лишилося:** перед профілем `public` виконуй
`ss -tlnp | grep -E ':(80|443)\s'`.

## 21 серпня 2026 — репозиторій і контейнер

Проєкт покладено в приватний репозиторій
`kupolua/bibletime-interactive-pdf` — 70 файлів, 28 МБ із 228 МБ теки.
Виключено `.venv`, `verify/`, `render/`, десятки версій зібраних PDF;
включено `.specstory` (стенограми сесій) і англійські оригінали.

Зібрано Docker-образ і `docker-compose.yml`. Дорогою знайшлося три речі,
через які клон репозиторію не працював би:

* `verify/highlight_{test,grid,adaptive}.py` не потрапляли в репозиторій,
  хоча `art_apply.py` їх імпортує. Причина тонка: правило `verify/` виключає
  **сам каталог**, і git у нього більше не заходить — тож `!verify/…`
  всередині не діє. Виключати треба вміст: `verify/*`;
* `forms/Level2_UA_form_v06.pdf` — типове джерело `art_apply.py` — був
  відсутній;
* код звертається до шрифтів за macOS-шляхами. У контейнері за тими самими
  шляхами лежить **Liberation Sans** — метрично сумісний з Arial, перевірено
  вимірюванням: 151.00 pt на маку і 151.00 в контейнері. Винятки —
  Arial Black і SFNSRounded, відповідників немає, тож **фінальні PDF для
  друкарні збираються на маку**.

## 6 серпня 2026 — серія A1 на всіх п'яти рівнях

Головний етап. Конвеєр параметризовано під довільний урок (`lessons.py`), і
через нього пройдено п'ять буклетів A1 — від англійського оригіналу до
друку та інтерактиву.

| | Друк | Інтерактив |
|---|---|---|
| Рівень 0 | `_UA_v03` | 36 полів |
| Рівень 1 | `_UA_v07` | 81 поле |
| Рівень 2 | `_UA_v04` | 196 полів |
| Рівень 3 | `_UA_v08` | 152 поля |
| Рівень 4 | `_UA_v12` | 252 поля |

Кількість версій друку — це кількість ітерацій. Що ламалося: `Q:` виявилося
окремим рядком шаблону; вузький бокс джерела пускав абзаци під ілюстрації;
повернутий невидимий білий текст не можна стирати — разом із ним зникали
бокси балів; кросворд із фіксованою орієнтацією виявився математично
неможливим (4 збіги літер там, де потрібно 5). Усе це записано в
`lesson-localizer.md` §2.4a і §2.6.

Написано генератор кросвордів під українські слова (`puzzlekit.py`) з
рестартами й випадковим вибором серед рівноцінних розміщень — жадібний
алгоритм детерміновано програвав. Там же — крапковий текст для обведення.

**Перебрендування** на «Біблійну Годину» застосовано до всіх буклетів
(`rebrand.py`): напис у шапці, логотип, сайт, підвал. Перша версія знищила
обкладинку — спроба зсувати роздільник з'їдала підзаголовок; правило
звелося до «нічого не рухаємо, вписуємо напис у вільне місце».

**Інвентаризацію** всієї програми зроблено: 180 буклетів, 18 механік завдань
(`INVENTORY.md`). З'ясувалося, що 22 % завдань проблемні — 419 із них у
полях форми не відтворюються взагалі.

Панель відкрито в локальну мережу (`--lan`) з токеном і перевіркою `Host`.

## 5 серпня 2026 — розмальовування й панель областей

Закрито питання «галочок Acrobat» — дефекту не було. Три досліди показали,
що галочки дає **програмна зміна значення** поля: Acrobat на це
перегенеровує вигляд із `/MK/CA`, а типовий підпис чекбокса — символ «4»
ZapfDingbats. Справжній клік нічого не перегенеровує.

**Метод, якого бракувало:** стан кнопки не можна перевіряти зміною значення
з консолі — тільки справжнім кліком або `/AS`+`/V`, записаними у файл.

Розмальовування малюнків доведено до робочого стану: 35 областей, 97 %
площі, 252 віджети. Панель редагування областей стала повноцінною —
редагування контурів, збереження на диск, перезбірка PDF просто з неї.
Модальні вікна прибрано: вони блокували автоматизацію браузера.

## 3 серпня 2026 — початок

Перший інтерактивний PDF (`Level2_UA_form_v01`). Відпрацювання конвеєра
на позасерійному буклеті «У що вірять християни» — 24 сторінки, 821 віджет
у фінальній версії v09.

Вихідні матеріали від замовника: англійські оригінали (травень 2026) і
шаблони верстки InDesign.
