chancellery 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. chancellery-0.1.0/.claude/agents/architect.md +42 -0
  2. chancellery-0.1.0/.claude/settings.json +18 -0
  3. chancellery-0.1.0/.claude/statusline.sh +72 -0
  4. chancellery-0.1.0/.claude/team-roles.md +94 -0
  5. chancellery-0.1.0/.copier-answers.yml +10 -0
  6. chancellery-0.1.0/.github/workflows/ci.yml +34 -0
  7. chancellery-0.1.0/.gitignore +46 -0
  8. chancellery-0.1.0/BACKLOG.md +36 -0
  9. chancellery-0.1.0/BOARD.md +68 -0
  10. chancellery-0.1.0/CHANGELOG.md +112 -0
  11. chancellery-0.1.0/CLAUDE.md +394 -0
  12. chancellery-0.1.0/CONCEPT.md +112 -0
  13. chancellery-0.1.0/DECISIONS.md +204 -0
  14. chancellery-0.1.0/LICENSE +21 -0
  15. chancellery-0.1.0/PKG-INFO +214 -0
  16. chancellery-0.1.0/README.md +181 -0
  17. chancellery-0.1.0/hooks/pre-push +38 -0
  18. chancellery-0.1.0/pyproject.toml +140 -0
  19. chancellery-0.1.0/scripts/publish.sh +89 -0
  20. chancellery-0.1.0/scripts/pytest-guard.sh +71 -0
  21. chancellery-0.1.0/specs/T001-engine-extraction/spec.md +298 -0
  22. chancellery-0.1.0/specs/design-brief-template.md +73 -0
  23. chancellery-0.1.0/specs/spec-template.md +99 -0
  24. chancellery-0.1.0/src/chancellery/__init__.py +144 -0
  25. chancellery-0.1.0/src/chancellery/check.py +218 -0
  26. chancellery-0.1.0/src/chancellery/columns.py +372 -0
  27. chancellery-0.1.0/src/chancellery/config.py +74 -0
  28. chancellery-0.1.0/src/chancellery/domain.py +77 -0
  29. chancellery-0.1.0/src/chancellery/errors.py +27 -0
  30. chancellery-0.1.0/src/chancellery/inflection/__init__.py +43 -0
  31. chancellery-0.1.0/src/chancellery/inflection/fio.py +148 -0
  32. chancellery-0.1.0/src/chancellery/inflection/gender.py +132 -0
  33. chancellery-0.1.0/src/chancellery/inflection/morph.py +21 -0
  34. chancellery-0.1.0/src/chancellery/inflection/petrovich_fio.py +123 -0
  35. chancellery-0.1.0/src/chancellery/inflection/phrase.py +196 -0
  36. chancellery-0.1.0/src/chancellery/inflection/ports.py +23 -0
  37. chancellery-0.1.0/src/chancellery/inflection/rank.py +126 -0
  38. chancellery-0.1.0/src/chancellery/io/__init__.py +1 -0
  39. chancellery-0.1.0/src/chancellery/io/excel.py +190 -0
  40. chancellery-0.1.0/src/chancellery/io/naming.py +33 -0
  41. chancellery-0.1.0/src/chancellery/pipeline.py +239 -0
  42. chancellery-0.1.0/src/chancellery/py.typed +0 -0
  43. chancellery-0.1.0/src/chancellery/render/__init__.py +1 -0
  44. chancellery-0.1.0/src/chancellery/render/context.py +242 -0
  45. chancellery-0.1.0/src/chancellery/render/engine.py +249 -0
  46. chancellery-0.1.0/src/chancellery/render/filters.py +85 -0
  47. chancellery-0.1.0/src/chancellery/reverse/__init__.py +26 -0
  48. chancellery-0.1.0/src/chancellery/reverse/candidates.py +236 -0
  49. chancellery-0.1.0/src/chancellery/reverse/docx_rewrite.py +100 -0
  50. chancellery-0.1.0/src/chancellery/reverse/engine.py +313 -0
  51. chancellery-0.1.0/src/chancellery/reverse/matcher.py +92 -0
  52. chancellery-0.1.0/src/chancellery/reverse/report.py +97 -0
  53. chancellery-0.1.0/tests/conftest.py +14 -0
  54. chancellery-0.1.0/tests/fixtures/declension.csv +9 -0
  55. chancellery-0.1.0/tests/test_check.py +211 -0
  56. chancellery-0.1.0/tests/test_columns.py +257 -0
  57. chancellery-0.1.0/tests/test_config.py +60 -0
  58. chancellery-0.1.0/tests/test_declension_regression.py +103 -0
  59. chancellery-0.1.0/tests/test_excel.py +199 -0
  60. chancellery-0.1.0/tests/test_filters.py +216 -0
  61. chancellery-0.1.0/tests/test_gender.py +86 -0
  62. chancellery-0.1.0/tests/test_inflection.py +248 -0
  63. chancellery-0.1.0/tests/test_naming.py +37 -0
  64. chancellery-0.1.0/tests/test_phrase.py +117 -0
  65. chancellery-0.1.0/tests/test_pipeline.py +182 -0
  66. chancellery-0.1.0/tests/test_rank.py +77 -0
  67. chancellery-0.1.0/tests/test_render.py +337 -0
  68. chancellery-0.1.0/tests/test_reverse.py +665 -0
  69. chancellery-0.1.0/uv.lock +1204 -0
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: architect
3
+ description: "Архитектор-консультант проекта (read-only). Зови для разбора технических и архитектурных решений, выбора подхода, ревью архитектуры: даёт контекст → варианты с компромиссами → рекомендацию → развилку и предлагает ADR-Lite запись. Источник истины — файлы методики проекта; сам не коммитит."
4
+ tools: Read, Glob, Grep
5
+ model: inherit
6
+ ---
7
+ Ты — старший архитектор ПО в этом проекте. Работаешь с ведущей сессией
8
+ Claude Code и, если он подключён, с Дизайнером; финальные решения принимает
9
+ человек — обращайся к нему на «ты», как равный к равному. Ты не знаешь проект
10
+ заранее: всё проектное выясняешь из его файлов методики, а не додумываешь.
11
+
12
+ ## Как ты рассуждаешь (важнее любых фактов)
13
+ - Начинай с реальной проблемы; отделяй настоящие ограничения от придуманных.
14
+ - Называй компромиссы явно; не продавай один вариант — доводы «за» и «против»,
15
+ потом рекомендация.
16
+ - Красивую, но дефектную идею протыкай рано, честно и по-доброму — до того,
17
+ как её начнут строить.
18
+ - Отличай реальное от желаемого; не поддакивай приятным иллюзиям.
19
+ - Предпочитай скучные, собираемые решения; сложность ответа — под сложность
20
+ задачи.
21
+ - Уважай экспертизу собеседника: возражай с аргументами, не льсти, не
22
+ самоуничижайся.
23
+ - Где метафора или удобство спорят с доменной логикой — побеждает домен.
24
+ - Держи человека решающим; заканчивай открытой развилкой, если она есть.
25
+
26
+ ## Рабочий протокол
27
+ 1. Прежде чем отвечать — сориентируйся в проекте: прочитай CONCEPT.md,
28
+ DECISIONS.md, релевантные specs/, BOARD.md/BACKLOG.md. Эти файлы — источник
29
+ истины, приоритетнее твоих домыслов.
30
+ 2. Источник истины для тебя — файлы проекта, а не память. Если у тебя есть
31
+ доступ к какой-то внешней памяти — относись к ней как к подсказке-зеркалу,
32
+ но при конфликте верь файлам проекта и опирайся в выводах на них.
33
+ 3. Дай разбор: контекст → варианты с компромиссами → рекомендация → развилка.
34
+ 4. Отметь, какие файлы методики легли в основу ответа.
35
+ 5. Когда решение созрело — предложи готовую ADR-Lite запись для DECISIONS.md
36
+ в формате проекта. Сам не коммить: ты read-only, запись вносит лид/человек.
37
+
38
+ ## Границы
39
+ - Не пишешь прод-код (это лид) и не проектируешь визуал (это Дизайнер).
40
+ - Не выдумывай факты. Нет в файлах проекта — скажи прямо и предложи, что уточнить.
41
+ - Отвечай на языке методики проекта, сжато; длина ответа — под сложность.
42
+
@@ -0,0 +1,18 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "dt context --hook"
9
+ }
10
+ ]
11
+ }
12
+ ]
13
+ },
14
+ "statusLine": {
15
+ "type": "command",
16
+ "command": "t=$(git rev-parse --show-toplevel 2>/dev/null) && sh \"$t/.claude/statusline.sh\" \"$t\" || true"
17
+ }
18
+ }
@@ -0,0 +1,72 @@
1
+ #!/bin/sh
2
+ # Claude Code statusLine reader for dreamteam's operational state layer.
3
+ #
4
+ # Prints "<workdir> · <task status line>" for the task bound to the current
5
+ # git worktree, read verbatim from
6
+ # $DT_HOME/store/by-worktree/<slug>/context.line
7
+ # The line is written elsewhere (SessionStart hook, `dt task start`,
8
+ # `dt context`, `dt task move`); this script only *reads* it.
9
+ #
10
+ # Contract & guarantees:
11
+ # * No Python interpreter is launched (statusLine runs on every message
12
+ # update — a Python start-up would blow the budget). Only git + a sha1
13
+ # utility are used; the whole pass stays well under 50 ms.
14
+ # * The script never fails the status line: any glitch (not a git repo, no
15
+ # store, no binding, empty file, no sha1 tool) yields *empty stdout* and
16
+ # *exit 0*. A non-zero exit or empty output blanks the status line, which
17
+ # is exactly the desired "no bound task → nothing shown" behaviour.
18
+ # * stdin (the statusLine JSON payload) is ignored: Claude Code runs the
19
+ # command with the working directory set to the session cwd, so the
20
+ # worktree is found with `git rev-parse` from ".".
21
+ #
22
+ # `<slug>` and `$DT_HOME` are computed bit-for-bit like
23
+ # dreamteam.dt.paths.worktree_slug / dt_home — otherwise the wrong file (or no
24
+ # file) is read. See specs/T054-statusline/spec.md.
25
+
26
+ # The main worktree top-level. Passed as $1 by the settings.json bootstrap
27
+ # (which already ran `git rev-parse` to locate this script); falls back to a
28
+ # lookup so the script also works when run directly.
29
+ top=${1:-$(git rev-parse --show-toplevel 2>/dev/null)}
30
+ [ -n "$top" ] || exit 0
31
+
32
+ # The shared common dir keys $DT_HOME (same root from every linked worktree).
33
+ common=$(git rev-parse --path-format=absolute --git-common-dir 2>/dev/null) || exit 0
34
+
35
+ # Resolve $DT_HOME: an explicit override wins; otherwise the sibling
36
+ # "<main-worktree>.dt". A common dir named ".git" means its parent is the main
37
+ # worktree; anything else is a bare repo taken as the root directly.
38
+ if [ -n "${DT_HOME:-}" ]; then
39
+ home=$DT_HOME
40
+ else
41
+ case $common in
42
+ */.git) main=${common%/.git} ;;
43
+ *) main=$common ;;
44
+ esac
45
+ home="${main}.dt"
46
+ fi
47
+
48
+ # Resolve symlinks so the hashed path matches Python's Path(...).resolve().
49
+ resolved=$(cd "$top" 2>/dev/null && pwd -P) || exit 0
50
+
51
+ # sha1 over the path *bytes* (no trailing newline, mirroring str.encode()),
52
+ # first eight hex chars. Prefer coreutils sha1sum; fall back to shasum (macOS).
53
+ if command -v sha1sum >/dev/null 2>&1; then
54
+ slug=$(printf '%s' "$resolved" | sha1sum | cut -c1-8)
55
+ elif command -v shasum >/dev/null 2>&1; then
56
+ slug=$(printf '%s' "$resolved" | shasum | cut -c1-8)
57
+ else
58
+ exit 0
59
+ fi
60
+ [ -n "$slug" ] || exit 0
61
+
62
+ line_file="${home}/store/by-worktree/${slug}/context.line"
63
+ [ -r "$line_file" ] || exit 0
64
+ # Read only the FIRST line: the status line must be a single row, but the file
65
+ # is derived from an unsanitised task title that could carry an embedded
66
+ # newline. This also drops a `cat` subprocess. `read` returns non-zero on a
67
+ # final line without a trailing newline, yet still fills $line — so gate on the
68
+ # content, not on read's exit status.
69
+ IFS= read -r line <"$line_file"
70
+ [ -n "$line" ] || exit 0
71
+
72
+ printf '%s · %s\n' "$(basename "$resolved")" "$line"
@@ -0,0 +1,94 @@
1
+ # Роли команды: Архитектор и Дизайнер
2
+
3
+ Проект укомплектован переиспользуемым контуром сотрудничества поверх
4
+ методики. Оркеструет **лид** (эта сессия Claude Code), финальные
5
+ решения принимает **человек** (Разработчик). Две роли доступны по
6
+ умолчанию — пользоваться ими в конкретной задаче или нет, решает лид
7
+ по ситуации, а не галочка при создании проекта.
8
+
9
+ Источник истины и «память» ролей — штатные файлы методики проекта
10
+ (`CONCEPT.md`, `DECISIONS.md`, `specs/`, `BOARD.md`/`BACKLOG.md`), а не
11
+ внешнее хранилище. Внешняя память лиду не запрещена, но это лишь
12
+ зеркало канона в файлах: сбросилась — ничего не потеряно, всё
13
+ восстановимо из проекта.
14
+
15
+ > **Подхват после `dreamteam update`.** Роли (субагент-Архитектор и
16
+ > импорт этой методики) подхватываются при **старте сессии** Claude
17
+ > Code, а не в момент апдейта. Если `update` запущен из активной
18
+ > сессии — перезапусти её, чтобы лид увидел новую роль.
19
+
20
+ ## Лид
21
+
22
+ Основная сессия Claude Code в проекте (эта). Не отдельный артефакт, а
23
+ поведение по `CLAUDE.md`. Пишет прод-код, ведёт задачи, вызывает
24
+ Архитектора и Дизайнера и сводит их результаты. Живого трёхстороннего
25
+ чата между ролями нет: лид обращается к каждой роли отдельно и сам
26
+ объединяет ответы.
27
+
28
+ ## Архитектор (read-only субагент)
29
+
30
+ Консультант по логике и архитектурным решениям. Живёт как субагент
31
+ `.claude/agents/architect.md`; Claude Code обнаруживает его
32
+ автоматически — отдельный импорт для него не нужен. Read-only
33
+ (Read/Glob/Grep): читает файлы методики, рассуждает, предлагает — но
34
+ сам не коммитит и не пишет код.
35
+
36
+ **Когда звать:** выбор подхода, разбор технического решения, ревью
37
+ архитектуры, сомнение «не выйдет ли боком». Не для написания кода (это
38
+ лид) и не для визуала (это Дизайнер).
39
+
40
+ **Как звать:** делегируй субагенту `architect` вопрос вместе с
41
+ контекстом — он сам дочитает нужные файлы методики. Например: «Спроси
42
+ Архитектора, стоит ли выносить X в отдельный модуль: дай контекст,
43
+ варианты с компромиссами и рекомендацию».
44
+
45
+ **Что возвращает:** разбор в форме контекст → варианты с компромиссами
46
+ → рекомендация → открытая развилка, с пометкой, какие файлы методики
47
+ легли в основу. Когда решение созревает — предлагает готовую ADR-Lite
48
+ запись для `DECISIONS.md` в формате проекта.
49
+
50
+ **Петля «предложил → человек решил → ADR»:**
51
+
52
+ 1. Архитектор предлагает решение и черновик ADR-записи.
53
+ 2. Человек решает — Архитектор read-only и за него не решает.
54
+ 3. Лид/человек вносит финальную запись в `DECISIONS.md`.
55
+
56
+ Так значимое решение оседает в файлах проекта, а не в летучей памяти
57
+ агента.
58
+
59
+ ## Дизайнер (Claude Design через MCP)
60
+
61
+ Внешний агент Claude Design для визуальной работы: интерфейсы,
62
+ прототипы, визуальные спецификации. Лид вызывает его напрямую как MCP,
63
+ в субагент не оборачивает.
64
+
65
+ **Prerequisite — разовая настройка на уровне аккаунта:**
66
+
67
+ 1. `claude mcp add --scope user --transport http claude-design https://api.anthropic.com/v1/design/mcp`
68
+ 2. `/design-login` — OAuth-аутентификация (именно этот шаг подключает,
69
+ не `add`).
70
+ 3. (опц.) `claude mcp list` — проверить, что сервер зарегистрирован.
71
+
72
+ Доступ к Claude Design — на планах Pro / Max / Team / Enterprise
73
+ (beta). Если MCP не подключён или недоступен — это **не ошибка, а
74
+ развилка**: лид либо подключает Дизайнера, либо работает без него.
75
+ Дизайны используют design-system аккаунта (бренд-цвета, типографику),
76
+ если она настроена; для свежего личного проекта — дефолты Claude Design.
77
+
78
+ **Когда звать:** нужен визуальный дизайн или прототип интерфейса. Лид
79
+ передаёт Дизайнеру бриф из `specs/design-brief-template.md`, итерирует
80
+ и тянет результат в репозиторий как прототип.
81
+
82
+ **Важно:** Дизайнер выдаёт **веб** (HTML/CSS/JS), а не целевой стек
83
+ проекта. Его артефакт — визуальная спецификация; перевод в целевой
84
+ UI-стек всегда отдельный шаг лида.
85
+
86
+ ## Честные ограничения
87
+
88
+ - Нет живого трёхстороннего диалога: Архитектор и Дизайнер —
89
+ вызываемые роли, а не равные собеседники в общем чате.
90
+ - Субагент — консультация, а не стрим: виден результат, не
91
+ промежуточные реплики.
92
+ - Архитектор реконструирует роль по промпту и файлам проекта; качество
93
+ ответа = качество промпта + полнота файлов методики.
94
+ - Дизайнер мыслит вебом; перевод в целевой стек — шаг лида.
@@ -0,0 +1,10 @@
1
+ _commit: 1.7.0
2
+ _src_path: /home/vlakir/programming/dreamteam/src/dreamteam/template/.bundle
3
+ language: ru
4
+ package_manager: uv
5
+ project_name: chancellery
6
+ project_description: 'Библиотека русского склонения и генерации документов по шаблонам:
7
+ ФИО, должности, звания по падежам + рендер docx'
8
+ author_name: Vladimir Kirievskiy
9
+ author_email: vlakir73@yandex.ru
10
+ architect_model: inherit
@@ -0,0 +1,34 @@
1
+ # Четыре гейта проекта на каждый push и pull request.
2
+ # Прогон изолированный, поэтому мьютекс-обёртка не нужна: делить нечего.
3
+ name: Гейты
4
+
5
+ on:
6
+ push:
7
+ branches: ['**']
8
+ pull_request:
9
+
10
+ jobs:
11
+ gates:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Поставить uv
17
+ uses: astral-sh/setup-uv@v5
18
+ with:
19
+ enable-cache: true
20
+
21
+ - name: Поставить зависимости
22
+ run: uv sync --all-extras --dev
23
+
24
+ - name: Линтер
25
+ run: uv run ruff check .
26
+
27
+ - name: Формат
28
+ run: uv run ruff format --check .
29
+
30
+ - name: Типы
31
+ run: uv run mypy src
32
+
33
+ - name: Тесты и покрытие
34
+ run: uv run pytest --cov=src --cov-report=term-missing --cov-fail-under=80
@@ -0,0 +1,46 @@
1
+ # IDE / Editors
2
+ .idea/
3
+ .zencoder/
4
+ ~*
5
+ ~*.*
6
+ .~*
7
+ *.kate-swp
8
+
9
+ # Claude Code — служебные runtime-файлы (не для шаринга)
10
+ .claude/*.lock
11
+ .claude/settings.local.json
12
+
13
+ # Python
14
+ venv/
15
+ env/
16
+ __pycache__/
17
+ *.pyc
18
+ *.pyo
19
+ dist/
20
+ build/
21
+ *.egg-info/
22
+ .coverage
23
+
24
+ # Logs
25
+ *.log
26
+ *.log.*
27
+ log/
28
+
29
+ # Secrets / config
30
+ .env
31
+ .secrets*
32
+ secrets.env
33
+
34
+ # Local utilities
35
+ copy_to_remote.sh
36
+ vanilla_docker.sh
37
+
38
+ # Caches
39
+ .cache/
40
+
41
+ # Local SQLite
42
+ *.sqlite
43
+
44
+ # Temp / docs builds
45
+ temp/
46
+ doc/
@@ -0,0 +1,36 @@
1
+ # Backlog
2
+
3
+ Парковка идей, побочных находок и «надо бы потом починить».
4
+
5
+ **Правило:** если в процессе работы над текущей задачей Claude или
6
+ Разработчик замечают что-то постороннее — оно идёт сюда, а не в текущий
7
+ коммит. Это защищает от расползания scope.
8
+
9
+ Это **не формальный таск-трекер** со сроками и метриками — это парковка
10
+ идей. Но **порядок имеет значение**: сверху — то, что планируется
11
+ ближайшим, ниже — менее срочное (FIFO по умолчанию, можно поднимать
12
+ приоритетное наверх). Когда из бэклога что-то берётся в работу — оно
13
+ вырастает в задачу или спеку (`specs/T<NNN>-…`) и удаляется отсюда.
14
+
15
+ ## Формат
16
+
17
+ `- **T<NNN>** — [<дата находки>] <короткое описание> — <опционально: контекст / откуда всплыло>`
18
+
19
+ ID присваивается при создании; новый = `max(существующих T-ID в
20
+ BACKLOG.md, BOARD.md и CHANGELOG.md) + 1`. ID не переиспользуется
21
+ и сохраняется при перетекании задачи между BACKLOG и BOARD; после
22
+ релиза задача переходит в `CHANGELOG.md` (с тем же T-ID), что
23
+ гарантирует уникальность между релизами.
24
+
25
+ ## Items
26
+
27
+ - **T002** — [2026-09-05] `overrides.fio` объявлен в схеме конфигурации и
28
+ рекламируется в scaffold-конфиге «Дьяка», но к сборке контекста не
29
+ подключён: заданные вручную падежные формы ФИО молча игнорируются
30
+ (`overrides.position` и `overrides.rank` работают). Перевезён сломанным
31
+ сознательно — решение Разработчика при clarify T001: чинить в переезде
32
+ нельзя, иначе критерий «поведение не изменилось» перестаёт что-либо
33
+ доказывать. Пришёл из бэклога «Дьяка» (там T028).
34
+ Acceptance: заданная в `overrides.fio` форма попадает в документ так же,
35
+ как это уже работает для должности и звания; тест на каждый падеж;
36
+ поведение при отсутствии секции не меняется.
@@ -0,0 +1,68 @@
1
+ # Board
2
+
3
+ Лёгкая Kanban-альтернатива на одном markdown-файле: три колонки
4
+ (To Do / Doing / Done) под git, без внешних сервисов и инструментов.
5
+
6
+ ## Соотношение с другими файлами
7
+
8
+ - `BACKLOG.md` — длинная очередь идей и побочных находок. Сюда падает
9
+ «потом подумаем», «не сейчас». Парковка scope.
10
+ - `BOARD.md` (этот файл) — активный рабочий поток. Задачи, которые мы
11
+ уже взяли или собираемся брать в ближайшее время.
12
+ - `specs/T<NNN>-*/spec.md` — куда вырастает крупная задача из BOARD, если
13
+ она оказывается фичей >1 дня работы.
14
+
15
+ Жизненный цикл задачи: идея в `BACKLOG.md` → созрела → переезжает в
16
+ `To Do` здесь → берётся в работу (`Doing`) → закрывается (`Done`) →
17
+ после релиза переходит в `CHANGELOG.md` (запись обязательно содержит
18
+ T-ID), отсюда удаляется. **`CHANGELOG.md` — единственное persistent-
19
+ хранилище T-ID завершённых задач**, без него правило «ID не
20
+ переиспользуется» сломается.
21
+
22
+ ## Формат задачи
23
+
24
+ Каждая задача — `- **T<NNN>** — <короткое описание>`. ID присваивается
25
+ при создании: новый = `max(существующих T-ID в BOARD.md, BACKLOG.md и
26
+ CHANGELOG.md) + 1`. ID никогда не переиспользуется. ID общий для
27
+ `BOARD.md` и `BACKLOG.md` — при перетекании задачи между ними
28
+ сохраняется; после релиза задача попадает в `CHANGELOG.md` с тем же
29
+ T-ID, что гарантирует уникальность номеров между релизами.
30
+
31
+ Имя ветки: `T<NNN>-<slug>` (без namespace типа `fixes/` / `feature/` —
32
+ ID уже даёт идентификацию). Имя PR: `T<NNN>: <title>`. Спецификация
33
+ крупной фичи: `specs/T<NNN>-<slug>/spec.md`.
34
+
35
+ По вкусу можно добавлять:
36
+
37
+ - метку даты взятия,
38
+ - ссылку на спеку,
39
+ - имя ветки.
40
+
41
+ Пример:
42
+
43
+ ```
44
+ - **T<NNN>** — Превью постов в Telegram
45
+ (`specs/T<NNN>-telegram-preview/`, ветка `T<NNN>-telegram-preview`).
46
+ ```
47
+
48
+ ---
49
+
50
+ ## To Do
51
+
52
+ <!-- Готово к взятию. Очередь FIFO по умолчанию, можно поднимать
53
+ приоритетное наверх. -->
54
+
55
+ <!-- Записи задач в формате `- **T<NNN>** — описание`. См. раздел
56
+ «Формат задачи» выше. -->
57
+
58
+
59
+ ## Doing
60
+
61
+ <!-- В работе прямо сейчас. Держим короткой: максимум 1-2 задачи на
62
+ разработчика, иначе теряется фокус (классическое WIP-limit
63
+ правило из Kanban). -->
64
+
65
+ ## Done
66
+
67
+ <!-- Закрытые задачи, ждущие переноса в CHANGELOG.md при следующем
68
+ релизе или значимой точке. После переноса — очищаем. -->
@@ -0,0 +1,112 @@
1
+ # Changelog
2
+
3
+ История заметных изменений. Формат — упрощённый
4
+ [Keep a Changelog](https://keepachangelog.com/).
5
+
6
+ Записи группируются по версиям или датам релизов. Для проектов без
7
+ формального версионирования допустимо использовать дату как заголовок.
8
+
9
+ Категории:
10
+ - **Added** — новая функциональность.
11
+ - **Changed** — изменения в существующей функциональности.
12
+ - **Fixed** — исправления багов.
13
+ - **Removed** — удалённая функциональность.
14
+ - **Deprecated** — то, что помечено к удалению, но пока работает.
15
+ - **Security** — изменения, важные с точки зрения безопасности.
16
+
17
+ Если изменение связано с задачей из `BOARD.md` / `BACKLOG.md`,
18
+ запись **обязательно** содержит T-ID в скобках, например:
19
+ `Added: Превью постов в Telegram (T<NNN>).` Это сохраняет уникальность
20
+ T-ID между релизами — `CHANGELOG.md` единственное persistent-
21
+ хранилище номеров завершённых задач (см. правило нумерации
22
+ в `README.md`).
23
+
24
+ ---
25
+
26
+ ## [0.1.0] — 2026-09-05
27
+
28
+ ### Added
29
+ - Движок русского склонения, перенесённый из «Дьяка» 0.3.3 (T001):
30
+ ФИО и его части с автоопределением рода, инициалы в шести формах,
31
+ универсальный фраз-движок для должностей и подразделений, воинские и
32
+ служебные звания.
33
+ - Рендер `docx` по шаблону с русской разметкой (T001): падежные фильтры
34
+ `ип`/`рд`/`дт`/`вн`/`тв`/`пр`, согласование по полу фильтром `согл`,
35
+ строгий режим неизвестных переменных, чистка пустых значений вместе с
36
+ осиротевшей пунктуацией.
37
+ - Распознавание колонок по заголовку и содержимому, чтение таблицы
38
+ `xlsx` с учётом пользовательского фильтра и скрытых строк,
39
+ уникальные имена выходных файлов, `pydantic`-конфигурация ручных
40
+ переопределений форм (T001).
41
+ - Обратная сборка размеченного шаблона из готового документа и строки
42
+ данных (T001).
43
+ - Сухой прогон `check_table`: проверка склонения, пола и шаблона без
44
+ записи файлов (T001).
45
+ - Высокоуровневый сценарий `generate_documents` / `reverse_template` —
46
+ таблица + шаблон + конфиг → документы одним вызовом (T001).
47
+ - Порт прогресса `ProgressSink`: ход работы отдаётся потребителю через
48
+ фабрику от числа строк, поэтому обвязка терминала в зависимости
49
+ библиотеки не попадает (T001).
50
+ - Прогон четырёх гейтов в CI на каждый push и pull request (T001).
51
+ - Выкладка на публичный PyPI скриптом `scripts/publish.sh`: четыре
52
+ гейта, сборка, проверка артефактов `twine`, загрузка (T003).
53
+ - Лицензия MIT, метаданные пакета (classifiers, ссылки, ключевые слова)
54
+ и маркер `py.typed` — без него mypy у потребителей не видел бы типы
55
+ библиотеки (T003).
56
+
57
+ ### Changed
58
+ - Корень иерархии ошибок переименован `DyakError` → `ChancelleryError`
59
+ (T001).
60
+
61
+ ### Removed
62
+ - `PdfExportError` в библиотеку не переехал: экспорт в PDF — вызов
63
+ внешнего `soffice` и остаётся заботой приложений (T001).
64
+
65
+ ### Retrospective
66
+
67
+ - **Что зашло.** Приёмка по эталону донора: сессия «Дьяка» сняла снимок
68
+ поведения 0.3.3 и отдала скрипт сверки, а библиотека прогнала его у
69
+ себя через тонкую обвязку — ещё до того, как «Дьяк» начал переезжать.
70
+ Расхождение искалось бы на своей стороне, а не в чужом репозитории;
71
+ на такой приёмке перенос перестаёт быть верой в диффы. Второе, что
72
+ сработало, — переписка двух сессий по фактуре: колбэк-прогресс
73
+ выглядел разумно и молча сломал бы чужое окно, а поймано это было
74
+ до единой строки кода.
75
+ - **Что не зашло.** Обещание публикации в спеке было дано без проверки
76
+ предусловий: Trusted Publishing требует ручного шага владельца
77
+ учётной записи, и обнаружилось это на последнем шаге, когда всё уже
78
+ было написано. Дважды по ходу приходилось чинить контракт задним
79
+ числом (`read_table` и нормализаторы конфига не попали в `__all__`) —
80
+ список публичных имён набирался по памяти вместо вычитки.
81
+ - **Правки методики.** Проектный `CLAUDE.md` пополнился правилом про
82
+ русский язык комментариев и докстрингов. Стоит завести привычку: если
83
+ acceptance спеки упирается во внешний сервис, проверять предусловия
84
+ на этапе analyze, а не при выпуске.
85
+
86
+ <!-- При закрытии этой версии (переход к новой `## [N.M.0]`)
87
+ добавляется секция:
88
+
89
+ ### Retrospective
90
+
91
+ - **Что зашло:** ...
92
+ - **Что не зашло:** ...
93
+ - **Правки методики:** ...
94
+
95
+ Это короткий разбор результата milestone. Не обязательная длинная
96
+ форма — несколько строк по делу. -->
97
+
98
+
99
+ ---
100
+
101
+ <!-- Пример (удалить при заполнении шаблона):
102
+
103
+ ## [0.1.0] — 2026-05-13
104
+
105
+ ### Added
106
+ - Базовая структура проекта по шаблону dreamteam.
107
+ - Веб-форма публикации с превью.
108
+
109
+ ### Fixed
110
+ - Падение при пустом теле поста.
111
+
112
+ -->