Merge branch 'main' of gitverse.ru:kpa39l/dedinit.ru
This commit is contained in:
@@ -14,6 +14,10 @@
|
|||||||
Никакой подробной документации и истории изменений в README не держим.
|
Никакой подробной документации и истории изменений в README не держим.
|
||||||
- `docs/` — вся подробная документация проекта:
|
- `docs/` — вся подробная документация проекта:
|
||||||
инструкции, справочники, дорожные карты, заметки об опыте настройки.
|
инструкции, справочники, дорожные карты, заметки об опыте настройки.
|
||||||
|
- **Файлы проекта (статус/требования/задачи/журнал) — тоже в `docs/`:**
|
||||||
|
`docs/STATUS.md` (текущее состояние), `docs/PRD.md` (требования),
|
||||||
|
`docs/TODO.md` (журнал задач), `docs/WALKTHROUGH.md` (капитанский журнал).
|
||||||
|
При закрытии сессии обновляются именно они.
|
||||||
- `AGENTS.md` — правила работы для агентов (этот файл).
|
- `AGENTS.md` — правила работы для агентов (этот файл).
|
||||||
- Подробная эксплуатационная документация НЕ должна лежать в корне репозитория.
|
- Подробная эксплуатационная документация НЕ должна лежать в корне репозитория.
|
||||||
Новые документы создавать только в `docs/`.
|
Новые документы создавать только в `docs/`.
|
||||||
@@ -65,9 +69,10 @@ make help # все команды
|
|||||||
|
|
||||||
## Процессы и планирование
|
## Процессы и планирование
|
||||||
|
|
||||||
- Планирование изменений — OpenSpec: `openspec/` (specs + changes).
|
- **ВСЕ задачи по изменению проекта выполняются через OpenSpec**:
|
||||||
Правки контента/кода — через openspec-скиллы из `.hermes/skills/`
|
`openspec/` (specs + changes). Порядок: propose → design → apply → archive.
|
||||||
(propose → design → apply → archive).
|
Новые изменения — только через openspec-скиллы из `.hermes/skills/`;
|
||||||
|
напрямую без OpenSpec ничего не менять (кроме `docs/`-правок и уборки).
|
||||||
- Проверочные действия перед этапами: `hugo version`, `make build`,
|
- Проверочные действия перед этапами: `hugo version`, `make build`,
|
||||||
контроль `hugo.toml`, `make deploy-dry-run`.
|
контроль `hugo.toml`, `make deploy-dry-run`.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 400" width="100%" height="auto">
|
||||||
|
<rect width="1200" height="400" fill="#0a0c10"/>
|
||||||
|
|
||||||
|
<!-- Hexagon pattern -->
|
||||||
|
<g stroke="#1a2332" stroke-width="1" fill="none" opacity="0.5">
|
||||||
|
<polygon points="100,100 130,80 160,100 160,140 130,160 100,140"/>
|
||||||
|
<polygon points="250,100 280,80 310,100 310,140 280,160 250,140"/>
|
||||||
|
<polygon points="400,100 430,80 460,100 460,140 430,160 400,140"/>
|
||||||
|
<polygon points="550,100 580,80 610,100 610,140 580,160 550,140"/>
|
||||||
|
<polygon points="700,100 730,80 760,100 760,140 730,160 700,140"/>
|
||||||
|
<polygon points="850,100 880,80 910,100 910,140 880,160 850,140"/>
|
||||||
|
<polygon points="1000,100 1030,80 1060,100 1060,140 1030,160 1000,140"/>
|
||||||
|
|
||||||
|
<polygon points="175,160 205,140 235,160 235,200 205,220 175,200"/>
|
||||||
|
<polygon points="325,160 355,140 385,160 385,200 355,220 325,200"/>
|
||||||
|
<polygon points="475,160 505,140 535,160 535,200 505,220 475,200"/>
|
||||||
|
<polygon points="625,160 655,140 685,160 685,200 655,220 625,200"/>
|
||||||
|
<polygon points="775,160 805,140 835,160 835,200 805,220 775,200"/>
|
||||||
|
<polygon points="925,160 955,140 985,160 985,200 955,220 925,200"/>
|
||||||
|
<polygon points="1075,160 1105,140 1135,160 1135,200 1105,220 1075,200"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Center text -->
|
||||||
|
<text x="600" y="175" font-family="monospace" font-size="36" fill="#58a6ff" text-anchor="middle" font-weight="bold">
|
||||||
|
GoToSocial и молча съеденные статусы
|
||||||
|
</text>
|
||||||
|
<text x="600" y="215" font-family="monospace" font-size="20" fill="#8b949e" text-anchor="middle">
|
||||||
|
ActivityPub · Relay · match_by_default · deny-by-default
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<!-- Decor line -->
|
||||||
|
<line x1="200" y1="250" x2="1000" y2="250" stroke="#30363d" stroke-width="1"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,164 @@
|
|||||||
|
---
|
||||||
|
date: '2026-09-13T03:00:00+03:00'
|
||||||
|
lastmod: '2026-09-13T03:00:00+03:00'
|
||||||
|
draft: false
|
||||||
|
title: 'GoToSocial молча съедал все статусы с релеев — и я нашёл почему'
|
||||||
|
slug: 'gotosocial-relay-match-by-default'
|
||||||
|
description: 'Подписка на релей выглядит рабочей: релей шлёт, нода отвечает 202 Accepted — а в базе пусто. Разбираюсь, почему: без галочки Match posts by default и пустых матчерах фильтр релеев GoToSocial по построению возвращает false, и статусы дропаются молча на уровне debug.'
|
||||||
|
|
||||||
|
categories:
|
||||||
|
- 'DevOps'
|
||||||
|
- 'Federation'
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- 'gotosocial'
|
||||||
|
- 'activitypub'
|
||||||
|
- 'fediverse'
|
||||||
|
- 'relay'
|
||||||
|
- 'mastodon'
|
||||||
|
- 'admin'
|
||||||
|
|
||||||
|
keywords:
|
||||||
|
- 'GoToSocial'
|
||||||
|
- 'ActivityPub'
|
||||||
|
- 'relay subscription'
|
||||||
|
- 'match by default'
|
||||||
|
- 'matchedByConnection'
|
||||||
|
- 'relay_subscriptions'
|
||||||
|
- '202 Accepted'
|
||||||
|
- 'dereference'
|
||||||
|
- 'dropping unpermitted'
|
||||||
|
- 'deny-by-default'
|
||||||
|
|
||||||
|
cover:
|
||||||
|
image: "hero.svg"
|
||||||
|
alt: "GoToSocial: релей молча съедает статусы — match_by_default"
|
||||||
|
---
|
||||||
|
|
||||||
|
# GoToSocial молча съедал все статусы с релеев — и я нашёл почему
|
||||||
|
|
||||||
|
**TL;DR:** Подписка на релей выглядит рабочей, релей шлёт, нода отвечает `202 Accepted` — а в базе пусто. Причина — **я не поставил галочку `Match posts by default`**, а матчеры оставил пустыми. В таком виде фильтр релеев по построению возвращает false для всего. Статусы дропаются молча, на уровне debug. Ошибка тихая: её заслоняют громкие ошибки dereference.
|
||||||
|
|
||||||
|
## Как это выглядит снаружи
|
||||||
|
|
||||||
|
Ты подписан на релей. Ты видишь:
|
||||||
|
|
||||||
|
- подписка активна, `approved = true`,
|
||||||
|
- релей исправно шлёт Announce,
|
||||||
|
- нода отвечает `202 Accepted`.
|
||||||
|
|
||||||
|
Всё зелёное. А лента месяцами состоит из пары доменов, на которые ты подписан напрямую. Релей как будто «работает вхолостую».
|
||||||
|
|
||||||
|
## ❌ Моя ошибка
|
||||||
|
|
||||||
|
Я настроил подписку так:
|
||||||
|
|
||||||
|
- ✅ разрешил **public**
|
||||||
|
- ✅ разрешил **unlisted**
|
||||||
|
- ✅ запретил **sensitive**
|
||||||
|
|
||||||
|
И решил, что этого достаточно. Логика была: «я разрешил то, что хочу, и запретил то, что не хочу — значит, всё остальное будет приходить само».
|
||||||
|
|
||||||
|
**Но это не так.** Разрешение public/unlisted и запрет sensitive — это **фильтры видимости**. Они говорят, *какие типы постов можно принимать*. Но они **не дают разрешения на приём вообще**. Разрешение даёт либо `match_by_default`, либо include-матчеры. У меня не было ни того, ни другого.
|
||||||
|
|
||||||
|
**Я не поставил галочку `Match posts by default`.** Без неё подписка работает в режиме deny-by-default: пропускает только то, что явно разрешено матчерами. Матчеров нет → не проходит **ничего**.
|
||||||
|
|
||||||
|
## Что происходит под капотом
|
||||||
|
|
||||||
|
1. Релей шлёт `POST /inbox` → нода отвечает `202 Accepted`. **Это подтверждение приёма HTTP-запроса, а не сохранения статуса.**
|
||||||
|
2. Нода скачивает оригинал по URI (dereference), тратит трафик и время.
|
||||||
|
3. Статус идёт в `relay.Filter.MatchedBySubscription`.
|
||||||
|
4. Нет совпадения → статус выбрасывается. В лог падает `dropping unpermitted status` — **warn/debug, не error**.
|
||||||
|
|
||||||
|
## Где прячется грабль
|
||||||
|
|
||||||
|
В таблице `relay_subscriptions` два ключевых поля: `flags` (битовая маска) и `matchers` (JSON-правила).
|
||||||
|
|
||||||
|
Флаги из `gtsmodel/relay.go`:
|
||||||
|
|
||||||
|
```
|
||||||
|
RelayFlagPublic = 2 (принимать публичные)
|
||||||
|
RelayFlagUnlisted = 4 (принимать скрытые)
|
||||||
|
RelayFlagMatchByDefault = 8 (принимать всё по умолчанию)
|
||||||
|
RelayFlagIgnoreSensitive = 16 (игнорировать чувствительное)
|
||||||
|
RelayFlagIgnoreMedia = 32 (игнорировать с медиа)
|
||||||
|
RelayFlagIgnoreReplies = 64 (игнорировать ответы)
|
||||||
|
```
|
||||||
|
|
||||||
|
У меня на всех трёх подписках стояло `flags = 22`. Раскладываем: `16 + 4 + 2` = `IgnoreSensitive + Unlisted + Public`. Выглядит осмысленно, правда? «Принимаем публичные и скрытые, игнорируем чувствительное».
|
||||||
|
|
||||||
|
Но бита `MatchByDefault` (8) там нет. И `matchers = NULL`.
|
||||||
|
|
||||||
|
## Почему без match_by_default дропается всё
|
||||||
|
|
||||||
|
Логика `matchedByConnection` в `internal/filter/relay/relay.go`:
|
||||||
|
|
||||||
|
1. Видимость: public → нужен флаг Public (есть), unlisted → нужен Unlisted (есть), остальное → false.
|
||||||
|
2. Чувствительное + `IgnoreSensitive` → false (это намеренно).
|
||||||
|
3. Медиа + `IgnoreMedia` → false (не стоит).
|
||||||
|
4. Ответ не себе + `IgnoreReplies` → false (не стоит).
|
||||||
|
5. Exclude-матчеры (чёрный список). Их нет → пропускаем.
|
||||||
|
6. **Если стоит `MatchByDefault` → true. У меня не стоит.**
|
||||||
|
7. Иначе ищем include-матчеры (белый список). Их нет вообще.
|
||||||
|
8. `return false`.
|
||||||
|
|
||||||
|
Вот оно. Пустая подписка без матчеров — это подписка, которая **не пропускает ничего**. Deny-by-default. Не «не знаю», а именно «не разрешаю».
|
||||||
|
|
||||||
|
## Что такое матчеры и зачем они нужны
|
||||||
|
|
||||||
|
Матчер — это ключевое слово, которое ищется в **содержимом поста и в его content warning**. Поиск регистронезависимый. Есть два режима совпадения: partial (по умолчанию, ловит часть слова) и whole word (только целое слово). Хэштеги матчатся через префикс `#`.
|
||||||
|
|
||||||
|
Матчеры бывают двух типов:
|
||||||
|
|
||||||
|
**Include-матчеры (белый список).** Работают, когда `match_by_default` выключен. Пост пройдёт только если совпал хотя бы с одним include-матчером. Нет include-матчеров и нет `match_by_default` — не пройдёт ничего. Именно это и случилось у меня.
|
||||||
|
|
||||||
|
**Exclude-матчеры (чёрный список).** Работают всегда, независимо от `match_by_default`. Если пост совпал с exclude-матчером — он дропается, даже если `match_by_default` включён.
|
||||||
|
|
||||||
|
Матчеры дают админу **хирургический контроль** вместо грубого «всё или ничего». Include — «хочу только посты про infosec и Linux». Exclude — «принимай всё, кроме спама и nsFW».
|
||||||
|
|
||||||
|
## Как диагностировать
|
||||||
|
|
||||||
|
SQL:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT relay_actor_uri, flags, matchers FROM relay_subscriptions;
|
||||||
|
-- flags без бита 8 и matchers = NULL → подписка не пропускает ничего
|
||||||
|
```
|
||||||
|
|
||||||
|
API:
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/v1/admin/relay_subscriptions
|
||||||
|
→ смотрим match_by_default
|
||||||
|
```
|
||||||
|
|
||||||
|
## ✅ Как чинить — и что надо было сделать сразу
|
||||||
|
|
||||||
|
Два корректных пути при добавлении релея:
|
||||||
|
|
||||||
|
**Путь 1: «Принимать всё, кроме явных запретов»** — поставить галочку `Match posts by default`. Тогда работают только exclude-матчеры и ignore-флаги. Всё, что не попало под запрет, — принимается.
|
||||||
|
|
||||||
|
**Путь 2: «Принимать только то, что я явно указал»** — не ставить `Match posts by default`, но создать include-матчеры. Например, `infosec`, `linux`, `#GoToSocial`.
|
||||||
|
|
||||||
|
Я выбрал ни то, ни другое. Надо было поставить галочку:
|
||||||
|
|
||||||
|
```
|
||||||
|
PUT /api/v1/admin/relay_subscriptions/{id}
|
||||||
|
{ "public": true, "unlisted": true, "match_by_default": true }
|
||||||
|
```
|
||||||
|
|
||||||
|
**После включения приток пошёл мгновенно:** 44 статуса за 30 минут, 12+ новых доменов (infosec.exchange, mastodon.world, burningboard.net, troet.cafe, norden.social, c.im, toot.wales, social.linux.pizza…). До этого лента месяцами содержала только пару доменов прямых подписок.
|
||||||
|
|
||||||
|
## Выводы
|
||||||
|
|
||||||
|
1. **Главное правило:** если хочешь «принимать всё, кроме запрещённого» — **обязательно ставь галочку `Match posts by default`**. Без неё релей будет слать, нода будет отвечать `202 Accepted`, а лента останется пустой.
|
||||||
|
2. Разрешить public/unlisted недостаточно — это лишь фильтры видимости, а не разрешение на приём. «Подписка есть» и «релей шлёт» ≠ «контент сохраняется».
|
||||||
|
3. Дизайн фильтра разумный — безопасный дефолт. Но для админа неочевидный: UI не кричит, что подписка без матчеров мёртвая.
|
||||||
|
4. Мониторь не только error, но и warn-строки `dropping unpermitted` / `not relayable`.
|
||||||
|
5. `202 Accepted` — это «запрос принят», а не «статус сохранён». Путать их — самый дешёвый способ незаметно потерять федерацию.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Теги:** #GoToSocial #ActivityPub #Fediverse #Relay #администрирование #грабли
|
||||||
|
|
||||||
|
Если у кого-то была та же тишина в ленте при живом релее — проверьте `match_by_default`. Возможно, вы тоже кормите чёрную дыру.
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
# dedinit.ru — PRD (требования к проекту)
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
Личный технический блог dedinit.ru на Hugo: быстрая публикация статей (MLOps/DevOps/Linux/CI-CD, личный опыт) без поломки сайта.
|
||||||
|
|
||||||
|
## Пользователи
|
||||||
|
- Автор (владелец) — публикует контент, настраивает тему.
|
||||||
|
- Читатели — русскоязычная аудитория (ru-ru), потребляют статьи.
|
||||||
|
|
||||||
|
## Функциональные требования
|
||||||
|
- Генерация статического сайта Hugo Extended, тема rDedInit (форк PaperMod).
|
||||||
|
- Статьи — bundle: `content/posts/<YYYYMMDD - slug>/index.md` + hero.svg (viewBox 1200x400, фон #0a0c10, заголовок #58a6ff).
|
||||||
|
- `date`/`lastmod` — в прошлом (иначе Hugo не публикует); `draft: false`.
|
||||||
|
- Подсветка блочного кода (цель проекта).
|
||||||
|
- RSS с полным текстом (site.Params.ShowFullTextinRSS, лимит 5 постов).
|
||||||
|
- Деплой — ТОЛЬКО `make deploy` (SFTP paramiko на kpa39l.myjino.ru:2222; аккаунт SFTP-only, rsync/ssh-exec невозможны).
|
||||||
|
- Русскоязычный UI (задача локализации i18n в процессе).
|
||||||
|
|
||||||
|
## Нефункциональные требования
|
||||||
|
- Git-история сохраняется (без потерь; git mv).
|
||||||
|
- Секреты/пароли в коммиты НЕ добавлять.
|
||||||
|
- Документация — на русском, в docs/; README — краткое введение.
|
||||||
|
- Все изменения проекта — через OpenSpec (propose → design → apply → archive).
|
||||||
|
- Не удалять/не перезаписывать данные пользователя без явного подтверждения.
|
||||||
|
|
||||||
|
## Границы (что НЕ делаем)
|
||||||
|
- Не внедряем мультиязычность en/ru (пока).
|
||||||
|
- Не переводим контент постов — только UI.
|
||||||
|
- Не меняем провайдер хостинга/деплоя без запроса.
|
||||||
|
|
||||||
|
## Критерии готовности
|
||||||
|
- Сайт собирается (`make build`) и деплоится (`make deploy`) без ошибок.
|
||||||
|
- UI на русском (по завершении локализации).
|
||||||
|
- Репозиторий: чистый git-статус, все коммиты в gitverse.
|
||||||
|
- Изменения проходят OpenSpec-цикл и архивируются.
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# dedinit.ru — Статус
|
||||||
|
|
||||||
|
Обновлено: 2026-09-13 (пост про GoToSocial/релеи)
|
||||||
|
|
||||||
|
## Текущее состояние
|
||||||
|
Сайт-блог dedinit.ru на Hugo Extended (тема rDedInit/форк PaperMod) в порядке: структура репозитория приведена к стандарту (README краткий + вся документация в docs/ + AGENTS.md), всё запушено в gitverse (main). Опубликован новый пост про GoToSocial и релеи. Следующая задача (локализация UI) оформлена через OpenSpec, реализация не начата.
|
||||||
|
|
||||||
|
## Сделано
|
||||||
|
- 2026-09-13: Опубликован пост «GoToSocial молча съедал все статусы с релеев — и я нашёл почему» (commit 0fcfeb3): hero.svg, index.md, `make build`, `make deploy` — прод отдаёт 200 (проверено).
|
||||||
|
- 2026-09-12: Уборка структуры репозитория — вся документация перенесена в docs/ (git mv, история сохранена), README.md переписан кратко, создан AGENTS.md с правилами проекта.
|
||||||
|
- 2026-09-12: Правило «ВСЕ изменения проекта — через OpenSpec» добавлено в AGENTS.md (commit 0f4d6da).
|
||||||
|
- 2026-09-12: Задача «Русская локализация i18n/ru.toml» оформлена через OpenSpec (propose) — change `localize-i18n-ru` валиден, запушен (f07bffe). Артефакты: proposal.md, specs/i18n/spec.md, design.md, tasks.md.
|
||||||
|
- 2026-09-12: В локальные openspec-скиллы проекта (.hermes/skills/) добавлена секция «Проверка (validate) — ПРАВИЛЬНЫЙ синтаксис» во все 6 SKILL.md (правильный вызов: `openspec validate <имя>`, НЕ `--change`).
|
||||||
|
|
||||||
|
## В работе / Следующие шаги
|
||||||
|
- [ ] **Реализация локализации i18n/ru.toml** (openspec change `localize-i18n-ru`, стадия apply): создать `i18n/ru.toml` с русскими переводами ключей темы, `make build`, проверить grep'ами в public/, закоммитить и запушить.
|
||||||
|
- [ ] Затем (из ROADMAP): цветовая схема под бренд dedinit(), русские переводы i18n, Telegram-посты (layout + карточки + shortcode), навигация, SEO и др.
|
||||||
|
|
||||||
|
## Как запустить / проверить
|
||||||
|
```bash
|
||||||
|
cd /opt/dedinit.ru
|
||||||
|
make build # сборка (hugo --minify)
|
||||||
|
make deploy-dry-run # симуляция деплоя
|
||||||
|
make deploy # ЕДИНСТВЕННЫЙ способ деплоя (SFTP, требует SSHPASS)
|
||||||
|
openspec validate localize-i18n-ru # валидация active-change
|
||||||
|
```
|
||||||
|
Источник истины — gitverse (origin main, SSH).
|
||||||
|
|
||||||
|
## Ключевые артефакты
|
||||||
|
- `/opt/dedinit.ru/AGENTS.md` — правила работы агентов (русский, docs/, make deploy, OpenSpec)
|
||||||
|
- `/opt/dedinit.ru/README.md` — краткое введение
|
||||||
|
- `/opt/dedinit.ru/docs/` — вся подробная документация (DEPLOY_INSTRUCTIONS, ROADMAP, PAPERMOD_PARAMS, QWEN, gitea-setup, ssh_setup, Telegram_frontend, local-aux-models, favicon-logo-setup, archive/)
|
||||||
|
- `/opt/dedinit.ru/openspec/changes/localize-i18n-ru/` — активный change (propose готов, apply впереди)
|
||||||
|
- `/opt/dedinit.ru/.hermes/skills/openspec-*/SKILL.md` — openspec-скиллы проекта (с секцией про validate)
|
||||||
|
|
||||||
|
## Открытые вопросы
|
||||||
|
- Продолжать ли локализацию (apply change localize-i18n-ru) в следующей сессии? (пользователь ещё не подтвердил apply)
|
||||||
|
- Перенести ли секцию про validate в глобальный скилл Hermes (переживёт перезапись локальных openspec-скиллов)?
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# TODO — dedinit.ru
|
||||||
|
|
||||||
|
Формат: | дата | задача | статус | закрыта в |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 2026-09-12 | Уборка структуры: вся документация в docs/, краткий README, AGENTS.md | ✅ закрыта | session уборки (коммит d426aa3) |
|
||||||
|
| 2026-09-12 | Правило «ВСЕ изменения через OpenSpec» в AGENTS.md | ✅ закрыта | commit 0f4d6da |
|
||||||
|
| 2026-09-12 | Оформить задачу локализации i18n/ru.toml через OpenSpec (propose) | ✅ закрыта | commit f07bffe (change localize-i18n-ru) |
|
||||||
|
| 2026-09-12 | Добавить в openspec-скиллы правильный синтаксис validate | ✅ закрыта | .hermes/skills/openspec-*/SKILL.md |
|
||||||
|
| 2026-09-12 | Файлы проекта (STATUS/PRD/TODO/WALKTHROUGH) — в docs/ | ✅ закрыта | docs/STATUS.md, docs/PRD.md, docs/TODO.md, docs/WALKTHROUGH.md |
|
||||||
|
| 2026-09-13 | Пост «GoToSocial молча съедал статусы с релеев — и я нашёл почему» | ✅ закрыта | commit 0fcfeb3, задеплоено, прод 200 |
|
||||||
|
| 2026-09-12 | Реализовать локализацию i18n/ru.toml (apply change localize-i18n-ru) | 🔵 открыта | |
|
||||||
|
| — | Настроить цветовую схему под бренд dedinit() | 🔵 открыта | (ROADMAP) |
|
||||||
|
| — | Русские переводы i18n (Read more→Читать далее и др.) | 🔵 открыта | (ROADMAP) |
|
||||||
|
| — | Telegram-посты: layout + карточки + shortcode | 🔵 открыта | (ROADMAP) |
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# WALKTHROUGH — dedinit.ru (капитанский журнал)
|
||||||
|
|
||||||
|
## 2026-09-13 — Пост «GoToSocial молча съедал статусы с релеев»
|
||||||
|
|
||||||
|
### Контекст
|
||||||
|
Опубликован пост о граблях с релеями GoToSocial: подписка выглядела рабочей (approved, 202 Accepted), а лента была пустой из-за отсутствия `match_by_default` и пустых матчеров (deny-by-default).
|
||||||
|
|
||||||
|
### Что сделано
|
||||||
|
1. Создан бандл `content/posts/20260913 - gotosocial relay match by default/`: index.md (frontmatter по конвенции, date 2026-09-13T03:00+03:00 в прошлом) + hero.svg (единый стиль).
|
||||||
|
2. `make build` — без ошибок; статья в `public/2026/gotosocial-relay-match-by-default/`, title и hero.svg на месте, RSS собрался (5 постов, новый первый).
|
||||||
|
3. Коммит 0fcfeb3 запушен в gitverse (истина).
|
||||||
|
4. Деплой `make deploy` (SSHPASS из docs/DEPLOY_INSTRUCTIONS.md) — прод отдаёт 200:
|
||||||
|
https://dedinit.ru/2026/gotosocial-relay-match-by-default/
|
||||||
|
5. docs/STATUS.md и docs/TODO.md обновлены (запись о публикации).
|
||||||
|
|
||||||
|
### Примечание
|
||||||
|
- Коммит/пуш правок docs/ в этой сессии блокировался песочницей (требует явного подтверждения) — изменения на диске, коммит по подтверждению пользователя.
|
||||||
|
|
||||||
|
## 2026-09-12 — Уборка структуры репозитория + OpenSpec
|
||||||
|
|
||||||
|
### Контекст
|
||||||
|
Репозиторий dedinit.ru (Hugo, тема rDedInit/PaperMod) имел документацию в корне; задача — привести к стандарту: README краткий, вся документация в docs/, AGENTS.md для агентов.
|
||||||
|
|
||||||
|
### Что сделано
|
||||||
|
1. **AGENTS.md создан** (корень) — правила: русский язык, структура (README краткий + docs/), стек (Hugo Extended, тема rDedInit, gitverse источник истины, деплой SFTP), статьи (bundle, date в прошлом, hero.svg), деплой только `make deploy`, git-правила, OpenSpec-процесс, проверки перед публикацией.
|
||||||
|
2. **Документация перенесена в docs/** (git mv): DEPLOY_INSTRUCTIONS.md, ROADMAP.md, PAPERMOD_PARAMS.md, QWEN.md, gitea-setup.md, ssh_setup_instructions.md, Telegram_frontend.md.
|
||||||
|
3. **Промежуточные файлы**: `.qwen/PROJECT_SUMMARY.md` → docs/archive/, `winscp_script.txt` → tools/.
|
||||||
|
4. **README.md переписан** кратко (55 строк): суть, быстрый старт, структура, указатели на docs/, конвенции. История убрана.
|
||||||
|
5. **Ссылки обновлены**: Makefile и tools/deploy_sftp.py → docs/DEPLOY_INSTRUCTIONS.md; docs/QWEN.md, docs/ROADMAP.md — пути.
|
||||||
|
6. **Коммиты/пуш**: d426aa3 (уборка), 0f4d6da (правило OpenSpec в AGENTS.md), f07bffe (openspec-propose локализации).
|
||||||
|
|
||||||
|
### OpenSpec — задача локализации
|
||||||
|
- Change: `openspec/changes/localize-i18n-ru/` (propose завершён, валиден).
|
||||||
|
- Артефакты: proposal.md (зачем), specs/i18n/spec.md (требования + сценарии), design.md (решения), tasks.md (5 задач).
|
||||||
|
- **Питфол синтаксиса**: `openspec validate --change <name>` — ОШИБКА (`unknown option '--change'`). Правильно: `openspec validate <name>`. Это зафиксировано во всех 6 локальных openspec-скиллах (.hermes/skills/openspec-*/SKILL.md).
|
||||||
|
|
||||||
|
### Файлы проекта
|
||||||
|
- Согласно правилу «вся документация в docs/» — файлы проекта лежат в docs/:
|
||||||
|
- `docs/STATUS.md` — текущее состояние, сделано, следующие шаги, как проверить.
|
||||||
|
- `docs/PRD.md` — требования, границы, критерии готовности.
|
||||||
|
- `docs/TODO.md` — журнал задач (история, статусы).
|
||||||
|
- `docs/WALKTHROUGH.md` — этот журнал.
|
||||||
|
|
||||||
|
### Как повторить / проверить
|
||||||
|
```bash
|
||||||
|
cd /opt/dedinit.ru
|
||||||
|
make build # сборка
|
||||||
|
openspec validate localize-i18n-ru # валидация active-change
|
||||||
|
git pull # обновление (origin = gitverse)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Известные подводные камни
|
||||||
|
- `.hermes/` игнорируется git (.gitignore) — openspec-скиллы не коммитятся.
|
||||||
|
- Секреты: winscp_script.txt и DEPLOY_INSTRUCTIONS.md содержали SFTP-пароль в истории (известно, ротация не проводилась). В новые файлы пароли НЕ записывать.
|
||||||
|
- Yandex.Disk НЕ должен синхронизировать .git (ломал репозиторий на Windows).
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-09-12
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
## Context
|
||||||
|
|
||||||
|
Проект dedinit.ru — Hugo Extended 0.165, тема rDedInit (форк PaperMod), язык ru-ru. Сейчас `i18n/` отсутствует, UI-строки темы отображаются английскими дефолтами PaperMod. См. proposal.md — Why.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:**
|
||||||
|
- Создать `i18n/ru.toml` с переводами всех ключей, используемых шаблонами rDedInit.
|
||||||
|
- Русский UI без изменения разметки и контента.
|
||||||
|
|
||||||
|
**Non-Goals:**
|
||||||
|
- Не переводим содержимое контента (посты).
|
||||||
|
- Не добавляем мультиязычность (en/ru переключатель) — вне рамок задачи.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
- **Формат TOML** (Hugo i18n) — стандарт Hugo, `ru.toml` рядом с другими локалями.
|
||||||
|
- **Источник ключей**: извлечены из `themes/rDedInit/layouts/` — `home`, `next_page`, `prev_page`, `read_time`, `words`, `toc`, `code_copy`, `code_copied`, `edit_post`, `translations`; дополнительно стандартные ключи PaperMod (`search`, `page_not_found`, `archives`, `tags`, `categories`, `posted_on`, `continue_reading`, `min_read`, `newer`, `older`, `page`, `of`, `read_more`, `translations` и др.) для полноты — отсутствующий ключ Hugo просто не находит и ничего не ломает (fallback).
|
||||||
|
- **Структура строки**: `[ключ]\nother = "перевод"` — Hugo i18n-формат (other — для языков без gender/plural).
|
||||||
|
- **Проверка**: `make build` + grep в public/ на русские строки («Читать далее», «Теги», «Содержание»).
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- [Перевод строки, которую тема не использует] → Не навредит: Hugo игнорирует неиспользуемые ключи; лишние ключи просто расширяют словарь (fallback при будущих фичах темы).
|
||||||
|
- [Ключ использован темой, но перевод терминологически неточен] → Быстрый фикс: правим одну строку в ru.toml; git-история позволит откатить.
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
## Why
|
||||||
|
|
||||||
|
Сайт dedinit.ru — русскоязычный блог (язык ru-ru), но в теме rDedInit (форк PaperMod) отсутствует файл `i18n/ru.toml`, из-за чего интерфейсные строки (кнопки, навигация, время чтения) показываются на английском по умолчанию. Это выглядит неаккуратно для русскоязычной аудитории.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- Создать `i18n/ru.toml` с русскими переводами всех UI-ключей темы rDedInit (PaperMod).
|
||||||
|
- Перевести строки: Home → «Главная», Read time → «Время чтения», Words → «слов», Table of Contents → «Содержание», Code copy → «Копировать код», Code copied → «Скопировано», Edit post → «Редактировать пост», Next → «Следующая», Prev → «Предыдущая», Translations → «Переводы», Search → «Поиск» (и др. стандартные ключи PaperMod).
|
||||||
|
- Поведение сайта, разметка, контент — не меняются; это чисто интернационализация UI.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
- `i18n`: русская локализация интерфейса темы (ключи-переводы UI). Новая способность проекта «русская локализация UI».
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
Отсутствуют — существующих spec в `openspec/specs/` не затронуто (site-rss не меняется).
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
- Затронутые файлы: `i18n/ru.toml` (новый).
|
||||||
|
- Не затрагивает: контент, шаблоны, деплой, RSS.
|
||||||
|
- Откат (rollback): удалить `i18n/ru.toml` (или `git revert`) — сайт вернётся к английским строкам по умолчанию, как сейчас.
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
## Purpose
|
||||||
|
|
||||||
|
Обеспечивает русскоязычный интерфейс сайта dedinit.ru: все UI-строки темы (навигация, время чтения, код, пагинация) отображаются на русском языке для аудитории ru-ru.
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Русские UI-строки в i18n/ru.toml
|
||||||
|
Система SHALL предоставлять файл `i18n/ru.toml`, содержащий русские переводы всех ключей интерфейса, используемых темой rDedInit (PaperMod): `home`, `next_page`, `prev_page`, `read_time`, `words`, `toc`, `code_copy`, `code_copied`, `edit_post`, `translations`, `search`, `page_not_found` и стандартные ключи PaperMod.
|
||||||
|
|
||||||
|
#### Scenario: Главная страница
|
||||||
|
- **WHEN** пользователь открывает главную страницу сайта
|
||||||
|
- **THEN** строка навигации «Home» отображается как «Главная»
|
||||||
|
|
||||||
|
#### Scenario: Время чтения статьи
|
||||||
|
- **WHEN** пользователь открывает статью с временем чтения
|
||||||
|
- **THEN** строка «X min read» отображается как «X мин чтения» (или эквивалент на русском)
|
||||||
|
|
||||||
|
#### Scenario: Оглавление
|
||||||
|
- **WHEN** статья содержит оглавление
|
||||||
|
- **THEN** заголовок оглавления отображается как «Содержание», а не «Table of Contents»
|
||||||
|
|
||||||
|
#### Scenario: Кнопки копирования кода
|
||||||
|
- **WHEN** пользователь наводит на блок кода
|
||||||
|
- **THEN** подсказка отображается как «Копировать код»; после копирования — «Скопировано»
|
||||||
|
|
||||||
|
#### Scenario: Пагинация
|
||||||
|
- **WHEN** пользователь видит пагинацию списка постов
|
||||||
|
- **THEN** ссылки отображаются как «Следующая» / «Предыдущая», а не «Next» / «Prev»
|
||||||
|
|
||||||
|
#### Scenario: Отсутствующий ключ
|
||||||
|
- **WHEN** в теме используется ключ, отсутствующий в `i18n/ru.toml`
|
||||||
|
- **THEN** сайт продолжает работать, отображая ключ по умолчанию из темы (Hugo fallback) — поведение не ломается
|
||||||
|
|
||||||
|
### Requirement: Фолбэк на английский при отсутствии перевода
|
||||||
|
Система SHALL сохранять работоспособность сайта при отсутствии какого-либо ключа: Hugo отображает строку по умолчанию темы (английскую), сайт не падает и не ломает разметку.
|
||||||
|
|
||||||
|
#### Scenario: Нет ключа в ru.toml
|
||||||
|
- **WHEN** в ru.toml отсутствует конкретный ключ
|
||||||
|
- **THEN** Hugo использует значение по умолчанию из темы (английское) и сборка проходит без ошибок
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
## 1. Локализация
|
||||||
|
|
||||||
|
- [ ] 1.1 Создать каталог `i18n/` и файл `i18n/ru.toml` с переводами всех ключей темы (home, next_page, prev_page, read_time, words, toc, code_copy, code_copied, edit_post, translations) и стандартных ключей PaperMod (search, page_not_found, archives, tags, categories, posted_on, continue_reading, min_read, newer, older, read_more и др.) — проверить `ls i18n/ru.toml`
|
||||||
|
- [ ] 1.2 Собрать сайт: `make build` — сборка проходит без ошибок (exit 0)
|
||||||
|
|
||||||
|
## 2. Проверка
|
||||||
|
|
||||||
|
- [ ] 2.1 Проверить в собранном public/ русские строки: `grep -r "Читать далее" public/ | head` и `grep -r "Содержание" public/ | head` — обе строки присутствуют в HTML
|
||||||
|
- [ ] 2.2 Проверить пагинацию: `grep -r "Следующая" public/ | head` и `grep -r "Предыдущая" public/ | head` — присутствуют
|
||||||
|
- [ ] 2.3 Закоммитить `i18n/ru.toml` и запушить в gitverse (origin main)
|
||||||
Reference in New Issue
Block a user