Initial commit

This commit is contained in:
tkachenko committed 2026-10-09 01:45:17 +00:00
commit 41d230d524
163 files changed
+11500

No files matched your search

+251
View File
@@ -0,0 +1,251 @@
# Архитектура шаблона документов Scientia
## Системный контекст
```text
┌────────────────────┐ редактирует ┌──────────────────────────────────┐
│ Автор документа │ ────────────▶ │ main.typ + chapters/ + assets/ │
└─────────┬──────────┘ └────────────────┬─────────────────┘
│ читает │ один import фасада
▼ ▼
┌────────────────────┐ ┌──────────────────────────────────┐
│ docs/ │ │ .template/lib/ │
│ инструкции и │ │ domain → application → │
│ примеры │ │ infrastructure → presentation │
└────────────────────┘ └────────────────┬─────────────────┘
│
┌────────────────────┐ условный import │ PDF
│ .private/ │ ────────────▶ main.typ ─────────┤
│ PNG + settings.typ │ один boolean ▼
┌────────────────────┐
│ document.pdf │
└────────────────────┘
┌────────────────────┐ сопровождает ┌─────────────────────────────────┐
│ Разработчик │ ─────────────▶ │ .template/development/ │
│ шаблона │ │ docs + modules + tests + tools │
└────────────────────┘ └─────────────────────────────────┘
```
Публичная и developer-документация физически разделены. Автору не требуется открывать `.template/`, а разработчик не использует `docs/` как описание внутренних контрактов.
## Архитектурные принципы
1. **Один файл ежедневной настройки**. Компания, режим, metadata и `#include` находятся в `main.typ`.
2. **Режим — значение, а не entrypoint**. `final`, `draft` и `clean-copy` являются вариантами одной переменной.
3. **Публичное видно**. Инструкции и примеры находятся в `docs/`, который VS Code не скрывает.
4. **Разработка скрыта**. Библиотека, tests, ADR и tools находятся в `.template/`.
5. **Пример является исполняемой документацией**. Один исходник одновременно обучает, компилируется в CI и устанавливается как тип документа.
6. **Важные параметры явные**. Даже отключённые значения показаны как `none`, `false` или `()`.
7. **Приватное не отслеживается**. Вся папка `.private/`, включая настройки offsets, исключена из Git; tracked placeholders никогда не заменяются реальными файлами.
8. **Один переключатель**. При `false` условный import не читает `.private`; при `true` обычный preview и обычная build task используют её настройки.
9. **Фасад скрывает реализацию**. Пользователь импортирует только `/.template/lib/index.typ`.
10. **Domain не зависит от layout**. Presentation использует domain contracts, но обратной зависимости нет.
## Целевая структура
```text
README.md # короткий маршрут автора
main.typ # единственная точка входа и настройки
chapters/ # пользовательский текст
assets/ # изображения, данные и bibliography
docs/ # публичная документация
├── README.md
├── documents.md
├── formatting.md
├── vscode.md
├── git.md
├── private-assets.md
├── writing-style.md
├── troubleshooting.md
└── examples/
├── README.md
├── private/
│ └── settings.typ
├── documents/
│ ├── report/
│ ├── letter/
│ ├── commercial-offer/
│ └── contract/
└── formatting/
.vscode/ # tracked workspace configuration
├── extensions.json
├── settings.json
└── tasks.json
.private/ # ignored settings, private media и backups
├── settings.typ
├── executors/
├── scientia/
├── technology/
└── too/
.template/ # скрытая реализация
├── lib/
├── companies/
└── development/
├── docs/
├── modules/
├── tests/
├── tools/
└── vscode/
```
В чистом fork каталог `.private/` не обязателен. Обычная компиляция использует placeholders из `.template/lib/assets/placeholders/`.
## Компоненты
| Компонент | Ответственность | Публичный интерфейс |
|-----------|-----------------|---------------------|
| Рабочее пространство автора | Один входной файл, главы и ресурсы | `main.typ`, `chapters/`, `assets/` |
| Публичная документация | Обучение без знания реализации | `README.md`, `docs/*.md` |
| Публичные примеры | Исполняемые примеры и источники выбора типа | `docs/examples/**/main.typ` |
| VS Code workspace | Рекомендации, автосохранение и задачи | `.vscode/*.json` |
| Публичный фасад | Единственный пользовательский Typst import | `.template/lib/index.typ` |
| Document Domain | Профиль, context и render options | `document-profile()`, `render-options()` |
| Company Domain | Реквизиты и firm resources | `company-profile()` |
| Parties Domain | Адресаты, подписанты и стороны | `recipient()`, `signer()`, `party()` |
| Attachments Domain | Порядок и идентичность приложений | `attachment()`, `attachment-set()` |
| Render Application | Resolve, normalize, validate, render | `render-document()` |
| Company Adapter | JSON profiles и resource overrides | `load-company()` |
| Presentation | Foundation, components и четыре renderer | `profiles.report/letter/commercial_offer/contract` |
| Employee/Private Adapter | Справочник сотрудников, private media и offsets | `report-executor()`, `private-company-media()` |
| Test Harness | Static, compile, semantic и visual gates | `run-tests.py` |
## Data flow
### Обычная сборка
1. Автор выбирает `company-id` и `document-mode` в `main.typ`.
2. `main.typ` создаёт profile, company overrides, bibliography и attachments.
3. `#show: document.with(...)` передаёт последующие `#include` как тело документа.
4. Facade вызывает application use case.
5. Application разрешает компанию, нормализует и валидирует profile metadata.
6. Renderer применяет foundation, нумерацию, media policy и компонует страницы.
7. При `none` для подписи или печати final renderer использует круг или крест.
8. Typst создаёт `document.pdf`.
### Выбор типа документа
1. Задача VS Code получает `report`, `letter`, `commercial-offer` или `contract`.
2. Tool сохраняет текущие `main.typ` и `chapters/` в `.private/starter-backups/<timestamp>/`.
3. Tool копирует соответствующий публичный пример из `docs/examples/documents/`.
4. Автор проверяет явно перечисленные параметры нового `main.typ`.
### Приватная сборка
1. Пользователь копирует готовую папку `.private` с `settings.typ` и PNG.
2. В `main.typ` значение `use-private-assets` меняется с `false` на `true`.
3. Условный import загружает `.private/settings.typ`.
4. Adapter сопоставляет публичный идентификатор сотрудника с фиксированным именем PNG и применяет private offset.
5. Отсутствующая или отключённая запись возвращает `none`; строка подписи остаётся пустой.
6. Обычная build task и Tinymist preview используют один и тот же `main.typ`.
## Ключевые интерфейсы
```typst
// Единственный пользовательский вход.
#let company-id = "scientia" // scientia | technology | too
#let document-mode = "final" // final | draft | clean-copy
#let use-private-assets = false // true, если скопирована .private
```
```typst
// Порядок и состав глав видны внизу main.typ.
#include "chapters/00-introduction.typ"
#pagebreak()
#include "chapters/10-main.typ"
```
```typst
// Отсутствующая .private не читается при false.
#let private-settings = if use-private-assets {
import "/.private/settings.typ": settings
settings
} else {
empty-private-settings
}
```
```typst
#show: document.with(
company: company,
profile: profiles.report(..),
options: (
mode: document-mode,
watermark: if document-mode == "draft" { "ЧЕРНОВИК" } else { none },
media-policy: if document-mode == "final" { "placeholder" } else { "reserve-space" },
diagnostics: true,
),
)
```
## Публичная и developer-документация
| Слой | Расположение | Содержит | Не содержит |
|------|--------------|----------|-------------|
| Публичный | `README.md`, `docs/` | первый запуск, Git, типы, formatting, папку `.private`, troubleshooting | DDD, ADR, snapshots, migration internals |
| Developer | `.template/development/docs/`, `modules/` | архитектуру, решения, границы и тестирование | обязательный маршрут обычного автора |
Корневой README обязан ссылаться на каждую публичную тему. Developer README доступен одной отдельной ссылкой и не конкурирует с пользовательской навигацией.
## Публичные примеры
| Пример | Обязательное покрытие |
|--------|----------------------|
| Report | титул, stage/volume, executors, includes, рисунки, таблица, formula, references, bibliography, appendix |
| Letter | recipient, исходящий номер, основной текст, attachment list, signer |
| Commercial offer | recipient, subject, price, tax, сроки, payment, scope appendix |
| Contract | parties, representatives, sections, requisites, signing, appendix |
| Formatting | варианты изображений, grid, простые/сложные таблицы, CSV, формулы, labels, lists |
Публичный пример не должен ссылаться на скрытый developer asset. Допустим только импорт фасада `/.template/lib/index.typ`.
## Политика приватных ресурсов
| Ресурс | Git | Поведение |
|--------|-----|-----------|
| Публичные реквизиты и логотипы | tracked | Загружаются из `.template/companies/` |
| Векторные placeholders | tracked | Используются обычной сборкой по умолчанию |
| `docs/examples/private/settings.typ` | tracked | Полный безопасный пример с `enabled: false` |
| `.template/lib/infrastructure/employees.typ` | tracked | ФИО, обычные роли и фиксированные имена PNG |
| `.private/` | ignored | Настройки доступности, offsets, реальные изображения и backups |
Typst 0.15 не предоставляет проверки существования файла. Поэтому отсутствие подписи моделируется отсутствующей записью или `enabled: false`; только включённая запись создаёт private path.
## Технологические решения
| Решение | Выбор | Обоснование |
|---------|-------|-------------|
| Compiler | Typst 0.15.1+ | Проверенный baseline и path type |
| Архитектура | Модульный монолит | Один процесс сборки без лишней инфраструктуры |
| Root entry | Один `main.typ` | Минимум выбора и все параметры в одном месте |
| Режимы | Переменная `document-mode` | Варианты видны комментариями, нет дублирования файлов |
| Public docs | Видимый `docs/` | Автор находит примеры в Explorer |
| Examples | Executable documentation | Код и объяснение не расходятся |
| Private activation | Literal `use-private-assets` + conditional import | Один понятный параметр, clean fork не читает отсутствующий каталог |
| Internal boundary | `.template/` | Реализация и developer docs не мешают автору |
| Testing | Python + Typst + Poppler | Контракты, PDF semantic и визуальный layout |
## Режимы отказа
| Сбой | Влияние | Митигация |
|------|---------|-----------|
| Автор меняет режим не в `main.typ` | Ожидаемый вариант PDF не получается | README и comments показывают единственную переменную |
| Пример использует скрытый developer asset | После очистки development сборка падает | Static path audit и compile всех public examples |
| Выбор типа уничтожает текст | Потеря работы | Timestamp backup до удаления `chapters/` |
| Подпись ещё не получена | Private compile падает при прямом path | Запись отсутствует или `enabled: false`, resolver возвращает `none` |
| Включённая запись не имеет PNG | Ошибка Typst с точным path | Включать запись только после копирования PNG; troubleshooting |
| Реальный файл попадает в Git | Утечка подписи | `.gitignore`, ignored `.private/`, no overwrite tracked placeholders |
| Сложная таблица переполняет страницу | Нарушение layout | Formatting example, stress fixture, repeated header tests |
| Публичная ссылка устарела | Автор теряет маршрут | Automated Markdown link audit |
| Изменение Typst меняет layout | Тихая регрессия | Version gate и snapshot review |
## Вне области видимости v1
- Поддержка старых root-файлов `document.typ`, `draft.typ` и `clean-copy.typ`.
- Автоматическое определение существования private files внутри Typst.
- Хранение реальных подписей и печатей в Git или Git LFS.
- Публикация внутренних VSIX в этом репозитории.
- Юридическая экспертиза договора.
- Научная верификация пользовательского содержания.
- Генерация DOCX.
- Публичная публикация в Typst Universe и open-source лицензирование.
+136
View File
@@ -0,0 +1,136 @@
# План редизайна пользовательской поверхности Scientia
## Обзор
| Фаза | Результат | Статус |
|------|-----------|--------|
| 1 | Зафиксирован baseline до редизайна | [x] |
| 2 | В корне оставлен один настраиваемый `main.typ` | [x] |
| 3 | Публичные инструкции и компилируемые примеры перенесены в `docs/` | [x] |
| 4 | Выбор типа выполняется задачей VS Code, private media — одним параметром | [x] |
| 5 | Полная регрессия и визуальная приёмка | [x] |
## План миграции
**Текущее до редизайна**: пользовательская справка находилась в скрытой `.template/help/`, конфигурация была разделена между `document.typ`, `main.typ`, `draft.typ` и `clean-copy.typ`, а учебные примеры лежали среди developer fixtures.
**Целевое состояние**: автор видит `main.typ`, `chapters/`, `assets/` и `docs/`. Режим задаётся одной переменной. Примеры четырёх типов документов и форматирования видимы, компилируемы и используются как заготовки. Приватная папка подключается одним параметром и содержит настройки индивидуальных подписей.
**Стратегия**: атомарное переключение пользовательского контракта без compatibility-файлов. Обратная совместимость не требуется.
| Фаза | Rollback |
|------|----------|
| 1 | Не требуется: только фиксация baseline |
| 2 | Восстановить предыдущие четыре корневых файла одним change set |
| 3 | Вернуть справку в `.template/help/`, не меняя библиотеку |
| 4 | Отключить задачи и использовать обычную сборку с placeholders |
| 5 | Откатить конкретную правку, повторить compile и visual suites |
---
## Фаза 1 — Зафиксировать baseline
**Цель**: доказать работоспособность библиотеки до изменения пользовательского контракта.
**Результат**: unit, negative, company matrix, semantic и visual suites проходят.
**Трудоёмкость**: S
**Статус**: [x] Готово
### Задачи
- [x] Сохранить compile и visual baseline (→ [Модуль: Тестирование](../modules/testing.md))
- [x] Зафиксировать Typst 0.15.1 (→ [ADR-0001](adr/0001-typst-015.md))
### Тесты
- [x] Unit: domain и numbering
- [x] Интеграционный: четыре профиля и компании
- [x] Визуальный: утверждённые snapshot pages
---
## Фаза 2 — Оставить один `main.typ`
**Цель**: сделать все ежедневные настройки и `#include` видимыми в одном файле.
**Результат**: `document.typ`, `draft.typ` и `clean-copy.typ` отсутствуют; режим выбирается в `main.typ`.
**Трудоёмкость**: M
**Статус**: [x] Готово
### Задачи
- [x] Перенести компанию, режим, metadata и порядок глав в `main.typ` (→ [Рабочее пространство автора](../modules/author-workspace.md))
- [x] Сохранить единый фасад импорта (→ [Публичный фасад](../modules/facade.md))
- [x] Проверить profile/options contract (→ [Документ](../modules/domain-document.md))
- [x] Сохранить границы организаций, сторон и приложений (→ [Организация](../modules/domain-company.md), [Стороны](../modules/domain-parties.md), [Приложения](../modules/domain-attachments.md))
- [x] Проверить application flow и загрузку компаний (→ [Сборка](../modules/application-render.md), [Ресурсы компаний](../modules/infrastructure-assets.md))
### Тесты
- [x] Static: единственный root entrypoint
- [x] Интеграционный: `final`, `draft`, `clean-copy` через profile fixtures
- [x] Ручной: порядок глав меняется только списком `#include`
---
## Фаза 3 — Открыть документацию и примеры
**Цель**: дать автору видимую справку и копируемые примеры без чтения реализации.
**Результат**: `docs/` содержит навигацию, четыре полных документа и каталог оформления.
**Трудоёмкость**: L
**Статус**: [x] Готово
### Задачи
- [x] Разделить публичную и developer-документацию (→ [Публичная документация](../modules/user-documentation.md))
- [x] Сделать примеры источником выбора типа документа (→ [Публичные примеры](../modules/starter-packs.md))
- [x] Показать таблицы, формулы, подписи и media blocks (→ [Компоненты](../modules/components.md), [Основа вёрстки](../modules/presentation-foundation.md))
- [x] Показать ссылки, bibliography и numbering (→ [Ссылки](../modules/references.md), [Нумерация](../modules/numbering.md))
- [x] Подготовить адекватные примеры профилей (→ [Отчёт](../modules/presentation-report.md), [Письмо](../modules/presentation-letter.md), [ТКП](../modules/presentation-commercial-offer.md), [Договор](../modules/presentation-contract.md))
### Тесты
- [x] Интеграционный: пять публичных примеров компилируются
- [x] Semantic: ожидаемые подписи, ссылки, приложения и реквизиты присутствуют
- [x] Ручной: весь `docs/` доступен из корневого README
---
## Фаза 4 — Упростить VS Code и приватные данные
**Цель**: оставить одну build task и безопасно подключать папку `.private` одним параметром.
**Результат**: выбор типа создаёт backup, обычная сборка работает с private media и без неё, отсутствующая подпись не ломает документ.
**Трудоёмкость**: M
**Статус**: [x] Готово
### Задачи
- [x] Оставить в Typewriter единственный `main.typ` и обновить задачи (→ [Рабочая область VS Code](../modules/vscode-workspace.md))
- [x] Добавить публичный справочник сотрудников, фиксированные PNG names и private offsets (→ [Приватные ресурсы](../modules/private-assets.md))
- [x] Использовать conditional import при явном `use-private-assets` (→ [ADR-0010](adr/0010-private-folder-and-employees.md))
### Тесты
- [x] Static: `.vscode/` синхронизируется, `docs/` не скрыт
- [x] Интеграционный: clean fork компилируется без `.private`
- [x] Интеграционный: private compile отображает реальные подписи и offsets
- [x] Интеграционный: `enabled: false` оставляет пустую строку без ошибки
---
## Фаза 5 — Hardening и выпуск
**Цель**: подтвердить отсутствие мёртвых путей, утечек и визуальных дефектов.
**Результат**: полный harness проходит, Markdown-ссылки валидны, приватные ресурсы игнорируются.
**Трудоёмкость**: M
**Статус**: [x] Готово
### Задачи
- [x] Обновить static workspace contract (→ [Тестирование](../modules/testing.md))
- [x] Удалить скрытые дубликаты примеров и notes (→ [Публичная документация](../modules/user-documentation.md))
- [x] Проверить визуально все страницы публичных примеров (→ [ADR-0006](adr/0006-visual-regression.md))
### Тесты
- [x] Полный automated harness
- [x] Аудит Markdown links и legacy paths
- [x] Визуальная проверка contact sheets и проблемных страниц
+63
View File
@@ -0,0 +1,63 @@
# Разработка шаблона Scientia
> Скрытая документация для сопровождающих Typst-библиотеки. Инструкции авторов находятся в публичном каталоге [`docs/`](../../../docs/README.md).
## Текущее устройство
Автор работает с единственным `main.typ`, каталогами `chapters/`, `assets/` и видимой документацией `docs/`. Внутренняя библиотека, профили организаций, тесты и архитектурные решения находятся в `.template/`.
Режим `final`, `draft` или `clean-copy` выбирается переменной внутри `main.typ`. Компилируемые публичные примеры четырёх видов документов и элементов оформления одновременно являются источниками для задачи выбора типа документа.
Обычная сборка использует векторные заглушки. Готовая папка `.private` подключается одним literal-переключателем в `main.typ`; отдельного ZIP installer и отдельной build task нет.
## Команды сопровождающего
```powershell
# Пользовательский документ
typst compile --root . main.typ document.pdf
# Полная регрессия
python .template/development/tests/run-tests.py
```
## Навигация
| Документ | Назначение |
|----------|------------|
| [PLAN.md](PLAN.md) | Завершённые фазы редизайна и проверки |
| [ARCHITECTURE.md](ARCHITECTURE.md) | Структура, компоненты, data flow и режимы отказа |
| [ADR-0001](adr/0001-typst-015.md) | Typst 0.15.1 как baseline |
| [ADR-0002](adr/0002-data-and-assets.md) | Границы данных и ресурсов |
| [ADR-0003](adr/0003-rendering-model.md) | Детерминированная модель выполнения |
| [ADR-0004](adr/0004-error-handling.md) | Ошибки и media fallback |
| [ADR-0005](adr/0005-ddd-boundaries.md) | Направление зависимостей |
| [ADR-0006](adr/0006-visual-regression.md) | Визуальная регрессия |
| [ADR-0007, устарел](adr/0007-author-workspace.md) | Историческое решение о трёх entrypoints |
| [ADR-0008](adr/0008-vscode-onboarding.md) | Версионируемая рабочая область VS Code |
| [ADR-0009](adr/0009-single-main-and-public-docs.md) | Один `main.typ`, публичные примеры и приватная сборка |
| [ADR-0010](adr/0010-private-folder-and-employees.md) | Папка `.private`, справочник сотрудников и частичные подписи |
| [Рабочее пространство автора](../modules/author-workspace.md) | Минимальный корень и один входной файл |
| [Публичные примеры](../modules/starter-packs.md) | Четыре вида документов и каталог оформления |
| [Публичная документация](../modules/user-documentation.md) | Видимый `docs/` для авторов |
| [Рабочая область VS Code](../modules/vscode-workspace.md) | Расширения, задачи и настройки |
| [Приватные ресурсы](../modules/private-assets.md) | Справочник сотрудников, `.private/settings.typ`, offsets и placeholders |
| [Публичный фасад](../modules/facade.md) | Стабильный Typst API |
| [Документ](../modules/domain-document.md) | Профили и режимы выпуска |
| [Организация](../modules/domain-company.md) | Реквизиты и ресурсы компании |
| [Стороны](../modules/domain-parties.md) | Адресаты, подписанты и стороны |
| [Приложения](../modules/domain-attachments.md) | Приложения разных видов документов |
| [Сборка](../modules/application-render.md) | Application orchestration |
| [Ресурсы компаний](../modules/infrastructure-assets.md) | Загрузка публичных профилей и overrides |
| [Основа вёрстки](../modules/presentation-foundation.md) | Tokens и media policy |
| [Отчёт](../modules/presentation-report.md) | Renderer отчёта |
| [Письмо](../modules/presentation-letter.md) | Renderer письма |
| [ТКП](../modules/presentation-commercial-offer.md) | Renderer предложения |
| [Договор](../modules/presentation-contract.md) | Renderer договора |
| [Компоненты](../modules/components.md) | Таблицы, формулы и подписи |
| [Ссылки](../modules/references.md) | Перекрёстные ссылки и библиография |
| [Нумерация](../modules/numbering.md) | Стратегии нумерации |
| [Тестирование](../modules/testing.md) | Static, compile, semantic и visual suites |
## Статус
Редизайн принят. Корневой и публичные примеры компилируются на Typst 0.15.1; полная тестовая матрица является release gate.
@@ -0,0 +1,30 @@
# ADR-0001: Typst 0.15.1 как минимальная платформа
**Дата**: 2026-08-26
**Статус**: Принято
## Контекст
Шаблон уже реализован на Typst и использует его counters, introspection, show rules и PDF-рендер. После обновления окружения доступен Typst 0.15.1, который добавляет несколько bibliographies, тип `path` для передачи project-relative ресурсов и более подробные diagnostics. Одновременно 0.15 меняет baseline некоторых layout-элементов, поэтому обновление должно сопровождаться визуальным аудитом.
Официальные основания: [changelog Typst 0.15.0](https://typst.app/docs/changelog/0.15.0/), [bibliography](https://typst.app/docs/reference/model/bibliography/), [path](https://typst.app/docs/reference/foundations/path/).
## Рассматриваемые варианты
1. **Остаться на Typst 0.14.2** — меньше миграционных рисков сейчас, но нет нативных нескольких bibliographies и нового `path`.
2. **Принять Typst 0.15.1+** — доступны нужные возможности, но требуется новый baseline и контроль будущих обновлений.
3. **Перейти на LaTeX или Word** — большая экосистема, но фактически требует переписать проверенную вёрстку и усложняет программируемые компоненты.
## Решение
Выбрали **Typst 0.15.1 как минимальную поддерживаемую версию**, потому что она уже установлена, поддерживает несколько bibliographies и даёт корректную модель передачи путей между пользовательским проектом и библиотекой.
Каждое обновление Typst выполняется отдельным изменением: сначала полная компиляционная и визуальная матрица, затем принятие новых snapshots.
## Последствия
**Становится проще**: тематические и поглавные библиографии, автономное подключение шаблона, диагностика layout convergence.
**Становится сложнее**: необходимо контролировать версию CLI и визуальные изменения baseline.
**Закрывает дверь на**: поддержку Typst 0.14 и более ранних версий без отдельной compatibility-ветки.
@@ -0,0 +1,37 @@
# ADR-0002: Публичные, пользовательские и приватные ресурсы
**Дата**: 2026-08-31
**Статус**: Принято
> Способ доставки через ZIP заменён папкой `.private` в [ADR-0010](0010-private-folder-and-employees.md). Граница публичных и приватных данных остаётся действующей.
## Контекст
Внутренний шаблон Scientia содержит фирменные реквизиты, логотипы, имена, подписи, печати и материалы конкретного документа. Логотипы, адреса, имена и реквизиты разрешено распространять внутри компании. Реальные подписи и печати нельзя хранить в Git вместе с шаблоном.
Typst не читает ZIP напрямую и не умеет проверить наличие изображения без попытки его загрузить. Поэтому приватный архив должен быть внешним каналом доставки, а отсутствие ресурса должно моделироваться значением `none`.
## Рассматриваемые варианты
1. **Оставить всё в одном tracked-каталоге** — максимально просто, но подписи и печати неизбежно распространяются с каждым форком.
2. **Хранить приватные изображения в Git LFS** — уменьшает основной репозиторий, но не устраняет доступ и историю распространения.
3. **Хранить подписи и печати в отдельном ZIP** — требует извлечения, зато отделяет приватный канал от шаблона.
4. **Не поддерживать реальные изображения вообще** — безопасно, но не покрывает подготовку финальных документов.
## Решение
Выбрали **три класса ресурсов**:
- публичные фирменные данные и логотипы хранятся в `.template/companies/`;
- материалы конкретного документа хранятся в `assets/`;
- реальные подписи и печати поставляются отдельным `private-assets.zip`; проверенная задача извлекает их в `.private/` и включает через `sys.inputs` только на время приватной сборки `main.typ`.
`.private/` и `private-assets*.zip` исключаются через `.gitignore`. В репозитории остаются только нейтральные placeholders: векторный круг для печати и крест для подписи. При значении ресурса `none` renderer использует placeholder; указанный путь обязан существовать.
## Последствия
**Становится проще**: безопасно форкать шаблон, централизованно обновлять публичные реквизиты и собирать документ без приватного архива.
**Становится сложнее**: для финального подписанного PDF нужно получить ZIP, извлечь его и явно указать пути.
**Закрывает дверь на**: хранение настоящих подписей и печатей в обычном Git, Git LFS или visual snapshots.
@@ -0,0 +1,28 @@
# ADR-0003: Синхронная детерминированная модель сборки
**Дата**: 2026-08-26
**Статус**: Принято
## Контекст
Сборка документа выполняется локальным Typst compiler: данные читаются из файлов проекта, затем происходит несколько внутренних итераций layout и создаётся PDF. Внешних сетевых сервисов, конкурентной записи или длительных независимых операций в v1 нет.
Добавление собственной async-модели не ускорит Typst-layout, но усложнит диагностику, воспроизводимость и тестирование.
## Рассматриваемые варианты
1. **Синхронная сборка одного документа** — простая, воспроизводимая и соответствует модели Typst.
2. **Параллельные renderer-профили внутри Typst** — не поддерживаются как управляемая модель и не дают изоляции layout-state.
3. **Внешний асинхронный build-сервис** — полезен для массовой генерации, но избыточен для локального шаблона.
## Решение
Выбрали **синхронную детерминированную сборку одного документа**. Параллельный запуск нескольких независимых fixtures допускается только во внешнем test harness, где каждый процесс получает собственный entry point и output.
## Последствия
**Становится проще**: воспроизводимость, порядок diagnostics, изоляция `state` и расследование visual regressions.
**Становится сложнее**: массовая генерация большого набора документов должна оркестрироваться внешним скриптом.
**Закрывает дверь на**: сетевые и фоновые операции непосредственно внутри шаблона v1.
@@ -0,0 +1,36 @@
# ADR-0004: Ранняя валидация и явные fallback-политики
**Дата**: 2026-08-26
**Статус**: Принято
## Контекст
Ошибки Typst часто проявляются во время layout далеко от места, где пользователь передал неверное значение. Для бизнес-документа особенно опасны тихие fallback: неверная компания, отсутствующая сторона, незаметно пропавшая подпись или citation, не попавшая в список источников.
При этом распространяемый шаблон должен компилироваться без реальных подписей и печатей. Они поставляются отдельным ZIP; PowerShell task проверяет и извлекает архив, после чего запускает `main.typ` с явным `sys.inputs`. Typst 0.15 не предоставляет проверки существования файла без попытки загрузки.
## Рассматриваемые варианты
1. **Полагаться только на diagnostics Typst** — мало кода, но сообщения не отражают доменный путь поля.
2. **Всегда аварийно завершаться при любом отсутствующем ресурсе** — строго, но шаблон нельзя удобно распространять без подписей.
3. **Валидировать domain до layout и явно моделировать необязательные ресурсы** — больше контрактов, зато ошибки предсказуемы.
## Решение
Выбрали **раннюю profile-specific валидацию**. Каждая ошибка называет профиль, путь поля, фактическое значение и ожидаемое ограничение.
Для подписи, печати и необязательных изображений поддерживаются политики:
- `hide` — не показывать ресурс и не резервировать место;
- `placeholder` — показать безопасную графическую заглушку: круг для печати или крест для подписи;
- `reserve-space` — оставить место для ручной подписи или печати.
Политика применяется только если поле равно `none`. Если поле содержит путь, но файл отсутствует, сборка завершается ошибкой.
## Последствия
**Становится проще**: распространение шаблона, поиск причины ошибки и тестирование негативных сценариев.
**Становится сложнее**: каждый профиль обязан определить required/optional поля и defaults.
**Закрывает дверь на**: молчаливое игнорирование неверно указанного пути к производственному ресурсу.
@@ -0,0 +1,37 @@
# ADR-0005: DDD-границы внутри модульного Typst-монолита
**Дата**: 2026-08-26
**Статус**: Принято
## Контекст
Шаблон должен расширяться новыми видами документов, но обычное использование должно оставаться простым. Один монолитный `show` с ветвлением по типу документа быстро свяжет корпоративные данные, file paths, domain-правила и пагинацию. Полноценные микросервисы или отдельные пакеты для каждого bounded context, напротив, избыточны для локальной Typst-библиотеки.
## Рассматриваемые варианты
1. **Одна функция с `if kind == ...`** — минимальный старт, но любое расширение меняет общее ядро и повышает риск регрессии.
2. **Модульный монолит с DDD-границами и profile contract** — изоляция без инфраструктурной сложности.
3. **Отдельный Typst package для каждого вида документа** — сильная физическая изоляция, но дублирование foundation и сложное совместное версионирование.
## Решение
Выбрали **модульный монолит** со слоями Domain → Application и адаптерами Infrastructure/Presentation. Domain не импортирует presentation или infrastructure. Новый вид документа добавляется новым `DocumentProfile`, а не новой веткой в `document()`.
Разрешённое направление зависимостей:
```text
Facade → Application → Domain
│ ▲
├→ Infrastructure
└→ Presentation → Shared Components
```
Presentation и Infrastructure могут создавать domain-значения или читать их, но не изменяют domain-инварианты.
## Последствия
**Становится проще**: добавление договора или другого профиля, независимые fixtures и локализация `show/state`.
**Становится сложнее**: необходимо поддерживать явные contracts и проверять import graph.
**Закрывает дверь на**: доступ domain-модулей к JSON, `image`, `page`, `context` и глобальным renderer-state.
@@ -0,0 +1,32 @@
# ADR-0006: Трёхуровневая стратегия регрессионного тестирования
**Дата**: 2026-08-26
**Статус**: Принято
## Контекст
Шаблон содержит хрупкую пагинацию, сложные таблицы, подписи, формулы, кириллическую нумерацию и show rules. Успешная компиляция не обнаруживает тихий перенос строки, наложение печати или изменение количества страниц. Чистый pixel-perfect diff, в свою очередь, слишком чувствителен к версии renderer и системным шрифтам.
## Рассматриваемые варианты
1. **Проверять только exit code компилятора** — быстро, но не защищает макет.
2. **Использовать только pixel-perfect snapshots** — ловит всё, но создаёт шум при допустимых изменениях окружения.
3. **Совместить unit, semantic и visual проверки** — больше инфраструктуры, зато дефекты классифицируются точнее.
## Решение
Выбрали **три уровня тестов**:
1. Domain unit tests через `typst eval` и `assert`.
2. Compile/semantic tests: exit code, diagnostics, A4, количество страниц, наличие обязательных текстовых маркеров и PDF metadata.
3. Visual regression: rasterize через Poppler, сравнивать контрольные области и полный perceptual diff с документированным порогом.
Snapshots создаются только из синтетического `test-company`; реальные подписи и печати не включаются в публичные тестовые изображения. Новая версия Typst всегда проверяется отдельным прогоном до обновления snapshots.
## Последствия
**Становится проще**: находить как логические, так и визуальные регрессии и безопасно менять отдельные profiles.
**Становится сложнее**: требуется Python/Poppler test runtime и процедура осознанного обновления эталонов.
**Закрывает дверь на**: автоматическое принятие новых snapshots при обычном тестовом запуске.
@@ -0,0 +1,39 @@
# ADR-0007: Минимальный корень и три режима одного документа
**Дата**: 2026-08-31
**Статус**: Устарело — заменено [ADR-0009](0009-single-main-and-public-docs.md)
## Контекст
> Этот ADR сохраняется как история промежуточного решения. Три entrypoint-файла были удалены после проверки на реальных отчётах: пользователю удобнее выбирать режим в одном `main.typ`.
В текущем корне служебные каталоги конкурируют с `chapters/` и `assets/`, а `main.typ` смешивает демонстрационные данные, настройку и сборку. Автору после форка нужен короткий маршрут без изучения DDD-слоёв, тестов и примеров.
Одновременно должны поддерживаться три выпуска одного содержания и четыре вида документов. Обратная совместимость со старыми путями не требуется.
## Рассматриваемые варианты
1. **Оставить служебные каталоги в корне** — удобно разработчику, но перегружает основной сценарий автора.
2. **Удалить тесты и документацию** — очищает корень, но делает шаблон хрупким и плохо сопровождаемым.
3. **Перенести внутреннее устройство в `.template/`** — сохраняет разработку и визуально отделяет её от пользовательских файлов.
4. **Создать отдельный entrypoint для каждого типа и режима** — явно, но приводит минимум к двенадцати корневым файлам и дублированию конфигурации.
## Решение
Выбрали **один скрытый каталог `.template/`**, общий `document.typ` и три корневые точки входа:
- `main.typ` передаёт `mode: "final"`;
- `draft.typ` передаёт `mode: "draft"`;
- `clean-copy.typ` передаёт `mode: "clean-copy"`.
Все entrypoints импортируют `render(mode:)` из `document.typ`. Report, letter, commercial-offer и contract starters реализуют одинаковый контракт, поэтому выбор типа документа не меняет entrypoints.
В starter явно записываются все семантически важные параметры, включая осознанные `none`, `false` и `()`. Низкоуровневые параметры layout остаются внутри библиотеки.
## Последствия
**Становится проще**: первый fork, переключение режима, выбор starter и обновление внутренней реализации.
**Становится сложнее**: `document.typ` является обязательным стабильным контрактом, а каждый starter должен проходить contract-tests.
**Закрывает дверь на**: compatibility-файлы в корне и отдельные копии полной конфигурации для каждого режима.
@@ -0,0 +1,35 @@
# ADR-0008: Версионируемая рабочая область VS Code и обучение автора
**Дата**: 2026-08-31
**Статус**: Принято
## Контекст
Основные пользователи шаблона пишут документы, но могут никогда не работать с кодом, Git и Typst. Устные инструкции и личные настройки редактора не воспроизводятся в новом форке. Одновременно каталог `.vscode/` не должен отвлекать автора от текста.
Часть расширений доступна в публичном Marketplace, а Typst Typewriter и Zotst распространяются внутри компании как локальные VSIX. Запись идентификатора расширения в рекомендациях VS Code не распространяет сам установочный пакет.
## Рассматриваемые варианты
1. **Не хранить настройки редактора** — корень формально проще, но каждый сотрудник вручную повторяет настройку и получает различное поведение.
2. **Настроить всё глобально на рабочих станциях** — удобно на одном компьютере, но не переносится вместе с форком и требует администрирования.
3. **Версионировать `.vscode/` и скрыть его в проводнике** — настройки синхронизируются через Git, оставаясь вне повседневной области автора.
4. **Положить локальные VSIX в шаблон** — обеспечивает автономную установку, но смешивает бинарные пакеты с исходниками и затрудняет централизованное обновление.
## Решение
Выбран вариант **версионировать `.vscode/`, но скрывать его из Explorer**:
- `extensions.json` содержит десять согласованных идентификаторов, включая два внутренних;
- `settings.json` включает автосохранение, языки проверки орфографии, TODO-маркеры и защитные настройки Git;
- `tasks.json` предоставляет обычную и приватную сборку, выбор одного из четырёх публичных примеров и компиляцию учебного каталога;
- внутренние VSIX хранятся в корпоративном хранилище, а README объясняет их установку;
- README и видимый `docs/` обучают Git в терминах истории документа, контрольных точек и параллельных версий.
## Последствия
**Становится проще**: первый запуск, одинаковая среда во всех форках, живой предпросмотр, проверка русского текста и совместная работа через Git.
**Становится сложнее**: изменения `.vscode/` требуют такого же review, как изменения шаблона; сопровождающий должен отдельно публиковать совместимые VSIX.
**Закрывает дверь на**: неявные обязательные глобальные настройки и распространение внутренних бинарных расширений внутри Git-шаблона.
@@ -0,0 +1,35 @@
# ADR-0009: Один main.typ, видимая документация и явная приватная сборка
**Дата**: 2026-08-31
**Статус**: Принято
> Часть решения об отдельной private build task заменена условным import из [ADR-0010](0010-private-folder-and-employees.md). Один `main.typ` и видимая публичная документация остаются действующими.
## Контекст
Проверка шаблона на реальных отчётах показала, что авторы ожидают менять компанию, стадию, режим и список глав в одном файле. Три корневых entrypoint-файла и скрытая пользовательская справка создавали лишний выбор. Одновременно Typst 0.15 не умеет безопасно проверять существование private path.
## Рассматриваемые варианты
1. **Сохранить `document.typ` и три entrypoints** — технически чисто, но пользователь должен понимать разделение ролей четырёх файлов.
2. **Один `main.typ` и tracked private placeholders с заменой** — просто, но реальная подпись становится изменением уже отслеживаемого файла.
3. **Один `main.typ`, public `docs/`, internal placeholders и private task** — минимальная поверхность без риска добавить реальный media в Git.
## Решение
Выбран третий вариант:
- `main.typ` содержит `company-id`, `document-mode`, metadata и `#include`;
- `docs/` видим и содержит executable examples;
- examples одновременно являются источниками задачи выбора типа;
- обычная сборка использует internal placeholders;
- задача приватной сборки проверяет `private-assets.zip`, копирует PNG в `.private/` и передаёт `--input private-assets=true`;
- прежний ADR-0007 считается устаревшим.
## Последствия
**Становится проще**: первый запуск, переключение режима, поиск примеров, выбор типа и выпуск с приватными изображениями.
**Становится сложнее**: main-файлы четырёх примеров частично повторяют setup-код; PowerShell tool становится security boundary для ZIP.
**Закрывает дверь на**: отдельные root entrypoints для режимов, скрытую public-документацию и замену tracked placeholders реальными файлами.
@@ -0,0 +1,26 @@
# ADR-0010: Папка `.private`, справочник сотрудников и частичные подписи
**Дата**: 2026-08-31
**Статус**: Принято
## Контекст
ZIP installer и отдельная build task скрывали второй переключатель private media от автора и не поддерживали реальные фамильные имена PNG. В проектах подписи поступают постепенно, а для каждого изображения уже подобрано индивидуальное вертикальное смещение. Чистый fork при этом обязан собираться без приватного каталога.
## Рассматриваемые варианты
1. **Оставить ZIP и `sys.inputs`** — безопасно для clean fork, но preview и обычная task не совпадают, а схема имён жёстко привязана к ролям.
2. **Автоматически сканировать `.private`** — желаемый UX, но Typst 0.15 не предоставляет file-exists и падает при попытке открыть отсутствующий PNG.
3. **Условный import и явная карта доступности** — один параметр в `main.typ`, отсутствие записи означает пустое место, offsets живут рядом с приватными PNG.
## Решение
Выбран вариант 3. Публичный tracked-справочник хранит идентификатор, ФИО, обычную должность и фиксированное имя PNG. Игнорируемый `.private/settings.typ` хранит доступность и offset. `main.typ` импортирует его только при literal `use-private-assets = true`. Все build/preview пути используют один entrypoint.
## Последствия
**Становится проще**: копировать `.private` целиком, менять роль сотрудника в одной строке, видеть реальные подписи в preview, работать при частично полученных PNG и публиковать clean fork с `false`.
**Становится сложнее**: при получении нового PNG нужно вручную включить запись; включённый, но отсутствующий файл по-прежнему вызывает точную ошибку Typst.
**Закрывает дверь на**: автоматическое определение файлов, role-based имена `responsible.png`, отдельную private build task и ZIP installer.