Compare commits

...
3 Commits
Author SHA1 Message Date
ddadmin 772a6c48c7 chore(scratch): handoff по настройке трекера на Gitea
- Зачем:
  - продолжение работы в новой сессии: контракт трекера переписан,
    граф блокировок карты #10 восстановлен, часть веток не влита.
- Что:
  - добавлен .scratch/handoffs/20260729-2125-gitea-tracker-tea-setup.md
    с состоянием карты, списком хвостов и подсказками по скиллам.
- Проверка:
  - текст читается, ссылки на docs/ и на трекер ведут в существующие места.
2026-07-29 21:32:05 +03:00
ddadmin 6c9d986114 docs(agents): контракт трекера переведён на Gitea и CLI tea
- Зачем:
  - GitHub-аккаунт заблокирован, работа идёт в Gitea на git.dementev.space,
    а инструкции для агентов всё ещё описывали GitHub Issues и gh.
- Что:
  - docs/agents/issue-tracker.md переписан под Gitea и tea 0.15.0: установка,
    вход, скоупы токена, обход прокси, команды для issues/комментариев/меток;
  - блокировки переведены на нативные зависимости Gitea через tea api,
    текстовые строки Blocked by из тел тикетов убраны;
  - GitHub ушёл одной строкой в раздел «Архив»;
  - docs/agents/triage-labels.md и блок «Agent skills» в AGENTS.md
    переобвязаны на Gitea.
- Проверка:
  - tea issues list, tea labels list — читают трекер;
  - граф блокировок карты #10 собран заново и прочитан обратно через
    tea api repos/{owner}/{repo}/issues/<n>/dependencies.
2026-07-29 21:32:05 +03:00
ddadminandClaude Fable 5 2e42cf63ff chore(scratch): сохранены слепок трекера и handoff миграции на Gitea
- Зачем:
  - GitHub-аккаунт заблокирован 2026-07-26; трекер карты «боевой
    реализм» перенесён на git.dementev.space, контекст нужен новой
    сессии независимо от исхода апелляции.
- Что:
  - .scratch/backup/ — слепок всех 27 issues и резолюция тикета #15
    на момент блокировки;
  - .scratch/handoffs/ — handoff с состоянием карты, нюансами доступа
    к Gitea и списком хвостов (апелляция, выбор основного трекера).
- Проверка:
  - тексты читаются; ссылки на Gitea-трекер открываются
    (git.dementev.space/ddmitry/clickstream-ch-kafka-superset-demo).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 22:03:00 +03:00
7 changed files with 579 additions and 36 deletions
+99
View File
@@ -0,0 +1,99 @@
## Ответ
Сумма заказа живёт **на обеих сторонах**, и стороны законно расходятся:
клиентское событие несёт объявленную сумму (то, что сайт продиктовал трекеру
через dataLayer — до пересчёта на бэкенде), заказ бэкенда — итоговую правду
со статусами. Правило стенда: **поведение и атрибуцию считаем по трекеру,
деньги — по бэкенду**. Правило выучивается на конфликте: суммы у менти
не сойдутся, и он сам раскопает почему.
### Клиентская сторона (широкое событие, рамка #18)
- Таксономия торговых событий: `pageview` + `add_to_cart` + `purchase`.
Полный словарь Метрики (`detail`, `remove`, `impressions`) не берём:
механика та же, объём массивов большой, новых идей нет — остаётся теорией
в документе о реализме.
- `purchase` несёт `purchaseID/Revenue/Currency/Coupon` + массивы `product*`
состава заказа + сырую строку `ecommerce` (по образцу выгрузки Метрики,
фактура — #27).
- `add_to_cart` — массивы `product*` с одним товаром. Оживают мёртвые
счётчики `purchases`/`add_to_carts` в `v_utm_effectiveness` без правки витрины.
### Сторона бэкенда (второй источник, рамка #18)
- **Формат выгрузки: полный ежедневный слепок заказов в пределах окна
изменяемости K дней.** Запись = снапшот заказа: `order_id`, `user_id`,
времена, `status` (`created`/`paid`/`cancelled`), `updated_at`, итоговые
суммы (товары / скидка / доставка / итого) + **позиции вложенным JSON**
(`items: [{sku, qty, price}]`) — «родной» экспорт бэкенда, а не диалект
Метрики.
- Обоснование слепка (а не инкремента): очень частый боевой формат;
идемпотентный приём и самовосстановление (пропущенный день ничего не
ломает); дедуп до последней версии становится обязательным с первого дня.
Окно K делает объём защитимым (за окном заказ неизменяем — возить незачем)
и даёт границу пересчёта: выручка дня D «дышит» K дней, потом замерзает.
- Приём: дедуп через `ReplacingMergeTree`/`argMax`; разбор JSON-позиций —
**один раз** в трансформации ODS → DDS, дальше витрины работают с плоскими
массивами. Это единственный носитель навыка «вложенный JSON в ClickHouse»
в стенде.
- В `docs/generator-realism.md` — честный абзац trade-off «инкремент экономнее,
слепок надёжнее» и сноска про compacted topic как родной Kafka-паттерн
для состояния сущности.
### Сверка
- **Ключ: клиентский `purchaseID` = `order_id` бэкенда** (магазин знает номер
заказа на `/confirmation` — как `actionField.id` у Метрики).
- Конструируемые расхождения — перечислимый список из четырёх причин,
детерминированных от seed:
- **A. Отмена** — `purchase` есть, заказ дошёл до `status='cancelled'`
(бесплатно из статусов);
- **B. Потерянное событие** — заказ есть, `purchase` не доехал
(вероятность в генераторе);
- **C. Дельта суммы** — систематическая, по построению: клиент объявляет
сумму товаров до промокода и доставки, бэкенд — итог; менти может вывести
формулу связи;
- **D. Дубль события** — повторный `purchase` от обновления
`/confirmation` (вероятность).
- Пятый эффект бесплатно даёт формат доставки: **опоздание** (заказ впервые
появляется в слепке D+1/D+2) — «вчера не сходилось, сегодня сошлось».
- Не берём: сироту-фрод (дублирует B механически) и расхождение часов
клиент/сервер — это жители тумана «Грязь в данных», придут своим тикетом.
### Слои и витрины
- DDS: одна новая сущность `dds.order` (последняя версия заказа, позиции
разобраны в массивы). `purchase` отдельной сущности не получает — это
строка широкого события.
- DM, три роли: **выручка** (`v_revenue_daily`, по категориям через `dictGet`
каталога) — строится только от заказов; **сверка**
(`v_purchase_vs_orders`) — FULL OUTER JOIN по ключу с классификатором
расхождения; **атрибуция** (`v_utm_effectiveness`) — остаётся клиентской,
плюс объявленная выручка по UTM. Точный состав колонок — в спеку (#17).
### Эталонный мир
Пересборка артефакта неизбежна и уже оплачена решением #18; этот тикет
нагружает её смыслом: манифест расширяется контрольными числами заказной
стороны — заказы и выручка по дням, и ровно N потерянных / M дублей /
K отмен для самопроверки лабы сверки. Политика версионирования артефакта
здесь не решается (пункт тумана карты).
### Ограничения исполнения и страховочные срезы
Аудитория — джун после базовой программы: расхождения — перечислимый список,
не хаос. Если при сборке спеки (#17) суммарный объём испугает, резать в
порядке: (1) статусы сузить до `created`/`cancelled` — урок дедупа держится
на самом слепке; (2) расхождения вводить поэтапно — сначала A+C, потом B+D;
(3) окно K сделать константой мира, а не параметром.
### Отклонено по дороге
Событийный лог заказов (сборка автомата — дальше от типовой работы DE),
CDC-формат (имитация Debezium без Debezium), шапка+строки (воскрешает склейку
по ключам), отдельный поток возвратов, полный словарь торговых событий
Метрики, механика Sign/CollapsingMergeTree (остаётся кандидатом на потом,
её дом — клиентская сторона, поток визитов Метрики Про, не заказы).
Фактура: `docs/research/2026-07-26-yandex-clickstream-format.md` (#27),
резолюция «Модель данных» (#18).
@@ -0,0 +1,213 @@
# Слепок трекера на 2026-07-26 (аккаунт GitHub заблокирован)
Страховочная копия wayfinder-карты и ключевых резолюций из GitHub Issues.
Снята из контекста сессии в момент блокировки аккаунта `dementev-dev`
(сразу после закрытия #15). Если аккаунт восстановят — файл можно удалить;
если нет — это источник для восстановления трекера на новом месте.
**Хвост, не доехавший до GitHub:** строка про #15 в Decisions so far карты #10
(текст — в разделе «Карта», помечен как НЕ ОПУБЛИКОВАНО).
## Состояние issues (все, на момент блокировки)
| # | Состояние | Метки | Название |
|---|---|---|---|
| 27 | CLOSED | wayfinder:research | Что отдаёт Яндекс как кликстрим: форма события и выгрузка |
| 24 | CLOSED | needs-triage | Вычистить прозаические тесты из test_world_dags_contract.py |
| 23 | CLOSED | ready-for-agent | Сверка цифр курса на живом стенде и финальная проверка |
| 22 | CLOSED | ready-for-agent | Лабы 07 (next-day) и 08 (continue) + метадокументы курса |
| 21 | CLOSED | ready-for-agent | Каркас курса и переобвязка уроков 0–6 под путь import |
| 20 | OPEN | wayfinder:task | Обновить мажорную версию airflow до версии 3 |
| 18 | CLOSED | wayfinder:grilling | Модель данных: широкое событие и второй источник |
| 17 | OPEN | wayfinder:task | Собрать спеку боевого реализма |
| 16 | OPEN | wayfinder:grilling | Анонимы и identity stitching: нужно ли и сколько |
| 15 | CLOSED (2026-07-26) | wayfinder:grilling | Purchase с выручкой: форма события и место в стенде |
| 14 | OPEN | wayfinder:grilling | Кластер: где живёт опыт менти и какая топология |
| 13 | CLOSED | wayfinder:research | Цена кластера для пайплайна |
| 12 | CLOSED | wayfinder:research | Ресурсный бюджет стенда на 16 ГБ |
| 11 | CLOSED | wayfinder:task | Реализм генератора: границы применимости стенда |
| 10 | OPEN | wayfinder:map | Карта: боевой реализм стенда |
| 9 | CLOSED | — | Редизайн пути менти: мир из артефакта и две ветки роста |
| 8 | CLOSED | wontfix | Техдолг: инкрементальный ETL вместо full_refresh на каждый день |
| 7 | CLOSED | ready-for-agent | Редизайн лаб курса под три режима менти |
| 6 | CLOSED | ready-for-agent | Один учебный профиль: daily-wave — учебный, ci — служебный |
| 5 | CLOSED | ready-for-agent | Инкрементальные счётчики manifest: next-day без перечитки всей Kafka |
| 4 | CLOSED | ready-for-agent | Поверхность DAG'ов: generator_control → world_init, беспараметрный world_next_day |
| 3 | CLOSED | ready-for-agent | Эталонный мир: 3-дневный артефакт в git и import по умолчанию |
| 2 | CLOSED | wontfix | Airflow Grid: всплывающая JS-ошибка при авто-обновлении |
| 1 | CLOSED | — | Быстрый разлогин в Airflow и Superset |
Блокировки #15 (нативные dependencies): blocked_by #11, #18 — обе закрыты.
## Карта #10 «Карта: боевой реализм стенда» (тело)
### Destination
Принятая спека в `docs/specs/` «Боевой реализм стенда»: где менти получает
кластерный опыт ClickHouse и в какой топологии; какие доработки реализма
данных генератора делаем; явные границы. Спека готова к разбиению через
`/to-tickets`.
### Notes
- Расчёт на ноутбук менти 16 ГБ RAM (у кого 8 ГБ — VDS за счёт менти).
- Исполнение доработок — после фичи «Редизайн пути менти» (#9); карта
решений может идти параллельно с ней.
- Изменения генератора тянут пересборку эталонного мира
(`data/startup_history/reference-world.json.xz`) и «поплывшие» числа
в лабах — учитывать в каждом решении.
- Скиллы: `/grilling` и `/domain-modeling` для тикетов-решений,
`/research` для тикетов-исследований.
- Рабочая гипотеза владельца: кластер здесь, облегчённо (2 шарда без
реплик), цель — Distributed и ON CLUSTER на живом потоке; «голый»
clickhouse-learning-cluster этого не даёт.
- Порядок: решение по модели данных предшествует кластерному — иначе
межшардовые джойны четырёх топиков придётся оплатить дважды.
### Decisions so far
- [Реализм генератора: границы применимости стенда](#11) — документ
`docs/generator-realism.md` (коммит 068d96f): честно как в бою —
схема/воронка/сессии/волна/обвязка; упрощено — четыре топика, только
pageview, клоны пользователей, нет «грязи», масштаб.
- [Ресурсный бюджет стенда на 16 ГБ](#12) — полный стенд в покое ≈3,4 ГБ;
2×1 добавляет ≈0,6–0,8 ГБ (влезает свободно), 2×2 — ≈1,7–1,9 ГБ (влезает,
но впритык к дефолтному бюджету WSL2 ~8 ГБ); координатором брать
clickhouse-keeper, не ZooKeeper.
- [Цена кластера для пайплайна](#13) — объём средне-крупный (~15–20 файлов,
тяжёлое — SQL); главная боль — JOIN поверх Distributed и TRUNCATE в
трансформациях (риск для контрольных сумм), приём из Kafka требует одного
консьюмера + Distributed-цели; рекомендация исследования — опциональный
`make up-cluster`, не дефолт.
- [Что отдаёт Яндекс как кликстрим](#27) — плоское широкое ядро (~140
колонок) плюс параллельные массивы для многозначного, вложенного JSON
нет (Яндекс ближе к Snowplow, чем к Segment); доставка батчем (Logs API,
TSV, лог доформировывается ~3 дня), поток только в «Метрике Про» через
Data Transfer с задержкой до 15 минут — Kafka у Яндекса нет, наша Kafka
учебная замена; детали в `docs/research/2026-07-26-yandex-clickstream-format.md`
(ветка `research/yandex-clickstream-format`, коммит `e6e34f2`).
- [Модель данных: широкое событие и второй источник](#18) — переходим на
одно широкое событие по образцу Яндекс Метрики (плоское ядро +
параллельные массивы + сырое поле `ecommerce`, таксономия event_type);
интеграционная ценность — заказы бэкенда той же Kafka, но пачками с
опозданиями и отменами (одна труба, два режима), каталог товаров —
словарь ClickHouse из файла; прямое чтение прод-Postgres и файловые
источники отклонены (файлы — зона Lakehouse-стенда); Kafka — учебная
замена батчевого Logs API, фиксируем в docs/generator-realism.md.
- **[НЕ ОПУБЛИКОВАНО — добавить при восстановлении доступа]**
[Purchase с выручкой: форма события и место в стенде](#15) — деньги на
обеих сторонах (клиент объявляет через dataLayer, бэкенд — итоговая
правда; атрибуция по трекеру, деньги по бэкенду); клиент:
`pageview`+`add_to_cart`+`purchase`; заказы — ежедневный полный слепок
окна изменяемости K со статусами и JSON-позициями, приём через
ReplacingMergeTree; сверка по `purchaseID`=`order_id` с четырьмя
конструируемыми расхождениями (отмена, потеря, дельта суммы, дубль);
выручка в DM — только от заказов.
### Not yet specified
- «Грязь» в данных: боты, дубли событий, опоздавшие мобильные батчи,
расхождение часов клиент/коллектор — вернуться после решений по
purchase и identity.
- Политика версионирования эталонного артефакта при изменениях
генератора (когда пересобирать, как жить лабам со сменой чисел).
- Как новые возможности лягут в лабы курса (после редизайна лаб, #7).
### Out of scope
- ~~Формат доставки событий: закрыть теорией~~ — решение отменено
2026-07-22: исследование цены кластера показало, что четыре топика
несовместимы с шардированием без GLOBAL JOIN; вопрос вернулся в рамку
тикетом «Модель данных: широкое событие и второй источник».
- Редизайн лаб курса — отдельный issue #7.
- Инкрементальный ETL — отдельный issue #8; багфиксы — #1, #2.
### Комментарии карты
1. Решение 2026-07-23: дальнейшая работа карты пойдёт в новом
v2-репозитории. v1 замораживается как стабильный стенд для менти
(добить путь менти, баги #1/#2, лекции). v2 стартует пустым
репозиторием с осознанным первым коммитом (переносим только нужное;
генератор переписывается, переиспользуются идеи). Имя нового
репозитория выберем из решения о нише; карта и открытые тикеты
переедут туда после создания.
2. Рабочий кандидат имени v2-репозитория: **clickstream-data-platform**
(согласован 2026-07-23). Мотив: стенд перерастает классическое DWH —
потоковый приём, оркестрация, кластер, витрины; «data platform»
описывает целое. Финальное закрепление — при решении тикета о нише.
## Тикет #15 «Purchase с выручкой: форма события и место в стенде»
### Тело
Part of #10
**Question:** Решить форму события покупки с суммой заказа: схема и носитель
(клиентское событие в широкой модели, источник заказов бэкенда или обе
стороны со сверкой — зависит от решения «Модель данных» #18), как ложится
в DDL/DM и дашборд, что делает с эталонным миром (пересборка артефакта
и чисел лаб).
Комментарий владельца: решение #18 добавило вторую сущность — заказ бэкенда
(Kafka, пачками, с опозданиями и отменами). При решении формы purchase решить
и форму события заказа, и сюжет сверки «клиентский purchase против
бэкенд-заказа». Фактура по ecommerce Яндекса — в
docs/research/2026-07-26-yandex-clickstream-format.md.
### Резолюция (опубликована 2026-07-26, тикет закрыт)
Опубликованный текст: комментарий
`issues/15#issuecomment-5084445090`. Полная копия — в
`.scratch/backup/20260726-resolution-15.md` (соседний файл).
## Тикет #18 «Модель данных: широкое событие и второй источник» (резолюция)
**Да, переходим на широкое событие.** Четыре топика-осколка уходят; модель
одна, целевая (двух моделей «для дефолта и для кластера» не держим). Мотив —
реализм: менти должен узнавать в стенде тот кликстрим, с которым столкнётся
на работе. Кластер — побочный довод, не причина.
**Форма события — по образцу Яндекс Метрики** (фактура — #27,
`docs/research/2026-07-26-yandex-clickstream-format.md`): плоское широкое
ядро плюс параллельные массивы для многозначного (товары, цели, свои
параметры) плюс одно сырое поле-строка `ecommerce`. Вложенных объектов в
стиле Segment/Amplitude не делаем. Таксономия `event_type` вместо «только
pageview». Точный состав полей — в спеку (#17), опора — таблица 53 полей
из исследования.
**Интеграционная учебная ценность** (взамен склейки осколков):
- **Второй источник — заказы бэкенда.** Вторая версия правды о покупке;
учебный сюжет — сверка клиентского purchase против заказа, расхождения,
отмены. Форма события заказа и сверка — тикет #15.
- **Транспорт заказов — та же Kafka, но пачками**: бэкенд выгружает заказы
раз в модельный день, с опозданиями и отменами. Одна труба, два режима —
как в бою, где батчи льют в брокер из удобства. Открывает темы, которых
у менти нет после курсовой airflow-greenplum: согласование потока и
батча, поздние данные, кросс-источниковые проверки, сенсоры/Datasets
(DAG слоя DDS ждёт дневной батч).
- **Каталог товаров — словарь ClickHouse из файла** (CSV в репозитории;
тот же файл использует генератор — расхождений нет по построению).
Даёт `dictGet` и политику обновления словаря.
**Ландшафт итогом:** Kafka — единственная труба (кликстрим потоком, заказы
пачками); Postgres остаётся только служебной базой Airflow; смешанность
ландшафта выражена режимами и частотами, а не второй трубой.
**Отклонено по дороге:** прямое чтение прод-базы магазина из ClickHouse
(анти-приём: нагрузка на прод и связность; в документе о реализме
зафиксировать как явный учебный пункт «в бою — реплика или выгрузка»);
файловые дропы как источник (зона Lakehouse-стенда; остаются теорией —
«настоящий Logs API — это скачанный TSV»); HTTP-сервис заказов и CDC
(Debezium) — цена выше учебной отдачи; каталог отдельным топиком Kafka —
выдумка, в бою так не делают.
**Честность к Яндексу:** у Метрики кликстрим — батч (Logs API), потока в
общем доступе нет; наша Kafka — учебная замена, так и называем в
`docs/generator-realism.md`.
**Что это открывает дальше:** #15 (purchase и форма заказа) и #14 (кластер:
с широким событием склейка осколков исчезает, ключ шардирования решается
там) разблокированы; новые приёмы стенда — ARRAY JOIN, словари,
сенсоры/Datasets, кросс-источниковый DQ; кандидат — версии записи через
Sign (механика CollapsingMergeTree из потока Метрики Про).
@@ -0,0 +1,92 @@
# Handoff: трекер переехал на Gitea, карта живёт, GitHub в блоке
Дата: 2026-07-26. Пишу по итогам сессии, в которой закрыли тикет #15
карты «Боевой реализм стенда» и экстренно переносили репозиторий с
трекером на собственный Gitea после блокировки GitHub-аккаунта.
## Что случилось и что сделано
1. **Тикет #15 «Purchase с выручкой» решён и закрыт** (шесть решений,
полная резолюция — в тикете). Гист — в Decisions so far карты #10.
2. **GitHub-аккаунт dementev-dev заблокирован** (ToS violation, причина
не названа) — сразу после закрытия #15. Апелляция готовится, см.
«Хвосты» ниже.
3. **Репозиторий и весь трекер перенесены на Gitea:**
`https://git.dementev.space/ddmitry/clickstream-ch-kafka-superset-demo`
(remote `gitea`, ветка `main` запушена). Все 27 номеров issues
воссозданы 1:1 (на местах PR #19/#25/#26 — закрытые заглушки, чтобы
ссылки `#NN` в текстах не разъехались).
4. **Из транскриптов прошлых сессий субагентами восстановлены дословно:**
тела #18, #20, #27; резолюции #12, #13, #15, #18, #27; граф блокировок.
Тела #14, #16, #17 — реконструкции по памяти (помечены в самих тикетах).
Резолюция #11 отдельным текстом не нашлась — её содержание есть в
`docs/generator-realism.md`.
## Состояние карты (#10 на Gitea)
- Закрыты: #11, #12, #13, #15, #18, #27 — гисты в Decisions so far.
- **Фронтир:** #14 «Кластер: где живёт опыт менти и какая топология»
(все блокеры закрыты; для него готова фактура — резолюции #12 и #13)
и #16 «Анонимы и identity stitching» (разблокирован закрытием #15).
- #17 «Собрать спеку боевого реализма» ждёт #14 и #16.
- Блокировки записаны строками `Blocked by:` в телах тикетов — нативные
dependencies в этой инсталляции Gitea выключены (API отдаёт 404).
## Как работать с Gitea (нюансы, стоившие времени)
- Хост домашний — **в обход прокси**: `curl --noproxy '*'`,
`no_proxy=git.dementev.space git push gitea ...`.
- Токен — в `~/.git-credentials` (строка ddmitry). Скоупы:
`write:repository`, `write:issue`, `write:package`. Создание репо через
API недоступно (нужен `write:user`) — создавать в UI.
- Токен не подставлять в командную строку (блокируется классификатором) —
читать из файла в python/через конфиг curl.
- `docs/agents/issue-tracker.md` всё ещё описывает GitHub/gh — пока
GitHub в блоке, рабочий трекер де-факто Gitea (операции — через
`curl`/python по API, образцы: `upload.py` и `apply_updates.py` в
scratchpad прошлой сессии; проще написать заново по образцу из этого
handoff).
## Хвосты (в порядке срочности)
1. **Апелляция в GitHub.** SMS на номера РФ не доходят (шлюз GitHub не
шлёт в РФ), попытки смены номера упёрлись в rate limit. План: спустя
~сутки — казахстанский номер (в выпадашке стран выбрать Kazakhstan);
параллельно тикет через «I can't sign in» на support.github.com
(без SMS, только почтовый код) с почты аккаунта. Черновик письма —
в прошлой сессии; суть: спросить причину, описать легитимное
использование (учебные репо, менти, Pages), упомянуть всплеск
API-активности через gh CLI как возможный триггер. Один тред, не
плодить дубли. Если ответят «multiple free accounts» — стандартный
выход: конвертировать менторскую учётку в организацию.
2. **При восстановлении GitHub:** донести в карту #10 строку про #15
(на Gitea она уже есть, на GitHub — нет), затем решить, какой трекер
основной, и синхронизировать/заморозить второй.
3. **Решение о доме трекера и v2.** Подозрение владельца: собственный
git-сервер для менторской работы — не такая плохая идея (независимость
от блокировок). Против: доступность для менти (публичные ссылки из
роадмапа, GitHub Pages де-факто витрина), привычность GitHub в резюме
менти. Это решение стоит принять осознанно — возможно, грилингом,
и оно связано с запланированным v2-репозиторием (комментарии к карте
#10: v2 стартует пустым, кандидат имени clickstream-data-platform).
4. **Резервные копии:** `.scratch/backup/20260726-*.md` — слепок трекера
и резолюция #15 на момент блокировки. После стабилизации (GitHub или
окончательный переезд) — можно удалить. Этот handoff и backup пока
не закоммичены.
## Suggested skills
- `/wayfinder #10 #14` или `/wayfinder #10 #16` — продолжать карту
(следующий тикет по выбору владельца; для #14 фактура уже собрана
в резолюциях #12/#13).
- `/grilling` + `/domain-modeling` — внутри тикетов-решений; владелец
просил на развилках сначала веер гипотез (дивергенцию), потом
конвергенцию с рекомендацией.
- `/conventional-commits` — при коммите backup/handoff.
## Ссылки
- Трекер: https://git.dementev.space/ddmitry/clickstream-ch-kafka-superset-demo/issues
- Карта: issue #10 там же; резолюция #15 — комментарий в issue #15.
- Исследование формата Яндекса: `docs/research/2026-07-26-yandex-clickstream-format.md`.
- Границы реализма генератора: `docs/generator-realism.md`.
@@ -0,0 +1,76 @@
# Handoff: трекер настроен на Gitea и tea, граф блокировок восстановлен
Дата: 2026-07-29. Сессия была узкой: перевести контракт работы с задачами с
GitHub на Gitea, поставить CLI `tea` и убедиться, что всё это живое.
## Что сделано
1. **`tea` 0.15.0 поставлен** в `~/.local/bin/tea` (бинарник с
`dl.gitea.com`, sha256 сверена). Логин `git.dementev.space` назначен
логином по умолчанию — `tea` работает из любого каталога.
2. **Контракт переписан** — коммит `bc62b94` в ветке
`chore/gitea-tracker-config` (`docs/agents/issue-tracker.md`,
`docs/agents/triage-labels.md`, блок «Agent skills» в `AGENTS.md`).
Ветка **не влита и не запушена**.
3. **Нативные зависимости Gitea оказались рабочими.** Прошлый вывод «API
отдаёт 404» был следствием нехватки прав у старого токена. Граф
блокировок карты #10 собран заново нативными связями, текстовые строки
`Blocked by:` из тел #14, #16, #17 убраны — источник истины теперь один.
Что построено: #17#14, #15, #16; #14#12, #13, #18; #16#15.
4. **Метка `wayfinder:prototype`** заведена (не хватало; остальные
`wayfinder:*` и все пять меток триажа уже были).
5. **Прокси.** `~/dotfiles` домен покрывал, расхождение было только в
`~/.t3/userdata/settings.json` — владелец поправил сам. Короткая рабочая
форма, если переменная не подхватилась: `NO_PROXY='*' tea ...`.
6. **Старый токен отозван** владельцем. Он оставался открытым текстом в
записях разрешений `.claude/settings.local.json` — файл стоит подчистить
при случае, хотя токен уже мёртвый.
Подробности по командам, скоупам и граблям — в самом
`docs/agents/issue-tracker.md`, здесь не дублирую.
## Состояние карты «Боевой реализм стенда» (#10)
Не менялось за эту сессию, только уточнилось представление блокировок.
- Закрыты: #11, #12, #13, #15, #18, #27.
- **Фронтир:** #14 «Кластер: где живёт опыт менти и какая топология» и
#16 «Анонимы и identity stitching» — оба открыты и разблокированы.
- #17 «Собрать спеку боевого реализма» ждёт #14 и #16.
- Вне карты: #20 «Обновить мажорную версию airflow до версии 3».
## Хвосты
1. **Ветка `chore/gitea-tracker-config`** — влить в `main` (PR в Gitea или
merge локально) и запушить.
2. **Ветка `chore/gitea-migration`** — тоже не влита. В ней слепок трекера
на момент блокировки GitHub и handoff предыдущей сессии
(`.scratch/backup/`, `.scratch/handoffs/20260726-2201-*`). Решить:
влить или удалить как отработавшую.
3. **Переписка с GitHub** идёт, затянулась. Владелец считает, что основную
работу в любом случае ведём в Gitea. Открытым остаётся вопрос, что делать
с GitHub-зеркалом, когда (и если) аккаунт вернут.
4. **Решение о доме трекера и v2-репозитории** — не принято. Против Gitea:
доступность публичных ссылок для менти, привычность GitHub в их резюме.
За: независимость от блокировок. Связано с планом v2 (кандидат имени
`clickstream-data-platform`, стартует пустым).
5. **`docs/adr/0001`** упоминает GitHub Issues как отклонённый вариант — это
ADR своего времени, трогать не надо, но при чтении может сбивать.
## Suggested skills
- `/wayfinder #10 #14` или `/wayfinder #10 #16` — продолжить карту; какой
из двух, выбирает владелец. Для #14 фактура уже собрана в резолюциях
#12 и #13.
- `/grilling` внутри тикетов-решений: владелец просил на развилках сначала
веер гипотез (дивергенцию), потом конвергенцию с рекомендацией.
- `/conventional-commits` — при любом коммите в этом репозитории.
- `/github` — если дойдёт до вливания веток через PR.
## Ссылки
- Трекер: https://git.dementev.space/ddmitry/clickstream-ch-kafka-superset-demo/issues
- Карта — issue #10 там же.
- Контракт трекера: `docs/agents/issue-tracker.md`.
- Исследование формата Яндекса: `docs/research/2026-07-26-yandex-clickstream-format.md`.
- Границы реализма генератора: `docs/generator-realism.md`.
+2 -2
View File
@@ -41,11 +41,11 @@
### Issue tracker
GitHub Issues (через CLI `gh`). Спека фичи — файлом в `docs/specs/` (источник истины), корневой issue — тонкий, со ссылкой на спеку и чек-листом дочерних issues. См. `docs/agents/issue-tracker.md`.
Gitea на `git.dementev.space` (через CLI `tea`). Спека фичи — файлом в `docs/specs/` (источник истины), корневой issue — тонкий, со ссылкой на спеку и чек-листом дочерних issues. См. `docs/agents/issue-tracker.md`.
### Triage labels
Пять канонических ролей как метки GitHub, имена совпадают (`needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, `wontfix`). См. `docs/agents/triage-labels.md`.
Пять канонических ролей как метки Gitea, имена совпадают (`needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, `wontfix`). См. `docs/agents/triage-labels.md`.
### Domain docs
+93 -31
View File
@@ -1,16 +1,52 @@
# Issue tracker: GitHub
# Issue tracker: Gitea
Задачи этого репозитория живут в GitHub Issues. Все операции — через CLI `gh`;
репозиторий `gh` определяет сам по `git remote`.
Задачи этого репозитория живут в Gitea на `git.dementev.space`
(`ddmitry/clickstream-ch-kafka-superset-demo`, это remote `origin`). Все
операции — через CLI [`tea`](https://gitea.com/gitea/tea), официальный клиент
Gitea; по устройству он близок к `gh` и `glab`. Логин и репозиторий `tea`
определяет сам по git remote в текущем каталоге.
- **Создать issue**: `gh issue create --title "..." --body "..."` (многострочное
тело — heredoc'ом).
- **Прочитать issue**: `gh issue view <номер> --comments`.
- **Список**: `gh issue list --state open --json number,title,labels` с нужными
фильтрами `--label` / `--state`.
- **Комментарий**: `gh issue comment <номер> --body "..."`.
- **Метки**: `gh issue edit <номер> --add-label "..."` / `--remove-label "..."`.
- **Закрыть**: `gh issue close <номер> --comment "..."`.
## Перед первым запуском
- **Бинарник.** Скачивается с `https://dl.gitea.com/tea/<версия>/` (файл
`tea-<версия>-linux-amd64` и `.sha256` рядом), кладётся в `~/.local/bin/tea`.
Проверка: `tea --version`.
- **Вход.** `tea logins add --name git.dementev.space --url
https://git.dementev.space`, токен передаётся переменной
`GITEA_SERVER_TOKEN` (не аргументом командной строки — он попадёт в историю
оболочки). Логин уже добавлен и назначен по умолчанию, так что `tea` работает
из любого каталога.
- **Скоупы токена:** `read:user` (без него `tea` откажется добавлять логин),
`write:issue`, `write:repository`. Токен выпускается в UI: Settings →
Applications. Нехватка скоупа выглядит не как «нет прав», а как невнятная
ошибка или пустой ответ — на этом уже один раз потеряли нативные блокировки
(решили, что их нет в установке).
- **Прокси.** Домен `dementev.space` должен быть в `NO_PROXY`, иначе запросы
уходят в прокси и виснут. В обычной оболочке это делает `proxy-client` из
`~/dotfiles`; для агента в t3 — блок `environment` в
`~/.t3/userdata/settings.json`. Если переменная не подхватилась, короткий
разовый префикс: `NO_PROXY='*' tea ...`.
## Команды
- **Создать issue:** `tea issues create --title "..." --description "..."`.
Многострочное тело удобнее собрать heredoc'ом в переменную и подставить
как `--description "$BODY"`.
- **Прочитать issue:** `tea issues <номер> --comments`.
- **Список:** `tea issues list --state open --output json --fields
index,title,labels,assignees`. Фильтры: `--labels`, `--assignee`,
`--keyword`.
- **Комментарий:** `tea comments add <номер> -d "..."`.
- **Метки:** `tea issues edit <номер> --add-labels "..."` / `--remove-labels
"..."`. Список меток репозитория — `tea labels list`, создать новую —
`tea labels create --name "..." --color "..."`.
- **Закрыть:** `tea issues close <номер>`. Комментария при закрытии команда не
принимает — сначала `tea comments add`, потом `close`.
- **Взять в работу:** `tea issues edit <номер> --add-assignees ddmitry`.
Сокращения вида `@me` в `tea` нет, имя пишется целиком.
- **Чего нет в CLI** — через `tea api <path>`: команда ходит в REST API Gitea
уже с сохранённым токеном, например
`tea api repos/ddmitry/clickstream-ch-kafka-superset-demo/issues/17`.
## Спека — источник истины
@@ -25,39 +61,65 @@
## Когда скилл говорит «опубликовать в issue tracker»
Создать GitHub issue.
Создать issue в Gitea: `tea issues create ...`.
## Когда скилл говорит «достать тикет»
`gh issue view <номер> --comments`.
`tea issues <номер> --comments`.
## PR как поверхность триажа
**Нет** — одиночный учебный репозиторий, внешних PR не ждём. (Если включить —
`/triage` начнёт гонять PR через те же метки и состояния командами `gh pr ...`.)
`/triage` начнёт гонять PR через те же метки и состояния командами
`tea pulls ...`.)
## Wayfinding-операции
Используются `/wayfinder`. Карта — один issue, тикеты — дочерние issues.
- **Карта**: issue с меткой `wayfinder:map` (Notes / Decisions-so-far / Fog в теле).
- **Дочерний тикет**: sub-issue карты (`gh api` на endpoint sub-issues); если
sub-issues недоступны — пункт task-list в теле карты + `Part of #<map>` в
начале тела тикета. Метки: `wayfinder:<type>` (`research`/`prototype`/
`grilling`/`task`).
- **Блокировки**: нативные issue dependencies —
`gh api --method POST repos/<owner>/<repo>/issues/<child>/dependencies/blocked_by -F issue_id=<db-id блокера>`
(`<db-id>` — числовой database id: `gh api repos/<owner>/<repo>/issues/<n> --jq .id`,
не `#номер`). Fallback — строка `Blocked by: #<n>` в начале тела. Тикет
разблокирован, когда все блокеры закрыты.
- **Карта**: issue с меткой `wayfinder:map` (Notes / Decisions-so-far / Fog
в теле).
- **Дочерний тикет**: вложенных issues в Gitea нет, поэтому связь держится
двумя ссылками — пункт списка `- [ ] #NN` в теле карты и строка
`Part of #<карта>` в начале тела тикета. Метки: `wayfinder:<тип>`
(`research` / `prototype` / `grilling` / `task`).
- **Блокировки**: нативные зависимости Gitea — единственный источник истины,
текстовых строк `Blocked by:` в телах тикетов больше нет. В CLI их команд
нет, работаем через `tea api` (`{owner}` и `{repo}` подставляются из текущего
репозитория):
- добавить блокер: `tea api repos/{owner}/{repo}/issues/<n>/dependencies
-F index=<блокер> -f owner=ddmitry -f repo=clickstream-ch-kafka-superset-demo`
— поля `owner` и `repo` обязательны, без них API отвечает
«repository does not exist»;
- кто блокирует тикет: `GET .../issues/<n>/dependencies`;
- кого блокирует тикет: `GET .../issues/<n>/blocks`;
- снять блокировку: тот же путь методом `DELETE` с тем же телом.
Тикет разблокирован, когда у всех блокеров `state == "closed"`.
- **Фронтир**: открытые дети карты минус заблокированные и назначенные; первый
в порядке карты.
- **Взять в работу**: `gh issue edit <n> --add-assignee @me`.
- **Закрыть**: комментарий с ответом, `gh issue close`, указатель на контекст —
в Decisions-so-far карты.
в порядке карты. Блокеры проверяются запросом `dependencies` по каждому
кандидату.
- **Взять в работу**: `tea issues edit <n> --add-assignees ddmitry` — первая
запись за сессию.
- **Закрыть**: `tea comments add <n> -d "<ответ>"`, затем `tea issues close
<n>`, затем указатель на контекст (суть + ссылка) в Decisions-so-far карты.
## Что проверено и когда
2026-07-29: Gitea 1.27.0, `tea` 0.15.0. Список команд и флагов снят с
`tea <команда> --help` установленного бинарника, а не из документации в вебе.
При обновлении `tea` стоит перечитать `--help`: набор флагов между версиями
менялся. Нативные зависимости и правка тел тикетов через `tea api` проверены
живыми запросами: граф блокировок карты #10 в тот день собран заново
(#17 ← #14, #15, #16; #14 ← #12, #13, #18; #16 ← #15).
## Архив
До 2026-07-19 задачи велись markdown-файлами в `.scratch/<feature>/issues/`
(фичи `data-generator` и `generator-model-time-startup-history`, задачи 0121).
Не мигрированы; доступны в истории git — срез `0e312b3`.
- До 2026-07-26 трекер жил в GitHub Issues (`dementev-dev/…`). Аккаунт
заблокирован, remote `github` заморожен; все 27 номеров issues воссозданы в
Gitea один в один. Слепок трекера на момент блокировки —
`.scratch/backup/20260726-tracker-snapshot.md` (ветка
`chore/gitea-migration`).
- До 2026-07-19 задачи велись markdown-файлами в `.scratch/<feature>/issues/`
(фичи `data-generator` и `generator-model-time-startup-history`, задачи
01–21). Не мигрированы; доступны в истории git — срез `0e312b3`.
+4 -3
View File
@@ -1,9 +1,9 @@
# Triage labels
Скиллы оперируют пятью каноническими ролями триажа. Здесь они сопоставлены с
метками GitHub Issues этого репозитория.
метками issues этого репозитория в Gitea.
| Роль в mattpocock/skills | Метка GitHub | Значение |
| Роль в mattpocock/skills | Метка Gitea | Значение |
| ------------------------ | ----------------- | ---------------------------------------------- |
| `needs-triage` | `needs-triage` | Мейнтейнеру нужно оценить задачу |
| `needs-info` | `needs-info` | Ждём от репортёра дополнительную информацию |
@@ -12,4 +12,5 @@
| `wontfix` | `wontfix` | Не будет сделано |
Правый столбец можно поменять под свою лексику. Сейчас — дефолт (метка = имя
роли); метки созданы в репозитории GitHub.
роли); все пять меток заведены в репозитории Gitea. Посмотреть текущий список —
`tea labels list`.