config: отсутствие файла и содержание сообщений валидатора

- R20: конфига нет — не стартуем; умолчания существуют для неполного файла,
  а не для отсутствующего
- R21: значение поля в сообщении валидатора печатается по тому же признаку
  секретности, что у R15 и R16, второй список не заводится
- в перечень проверок R18 добавлен формат идентификаторов сущностей
This commit is contained in:
av
2026-07-25 20:15:36 +03:00
parent 0fc1994db7
commit 9085601db2
+43 -4
View File
@@ -57,6 +57,28 @@
(тесты, второй инстанс): без неё их разводят переменной окружения — тем
самым каналом, который закрывает R1.
### R20. Отсутствие файла конфигурации — ошибка старта
**ДОЛЖЕН.** Если файла нет ни по пути из опции, ни по имени по умолчанию в
рабочей директории (R3), приложение не стартует: сообщение называет
искомый путь, код возврата ненулевой.
**Почему.** Конфиг — артефакт деплоя (R12), и его отсутствие означает, что
развёртывание не довело работу до конца, а не что приложение попросили
работать на умолчаниях. Умолчания (R7) существуют, чтобы работал
**неполный** файл, а не отсутствующий, — именно здесь читатель спотыкается
чаще всего.
Старт без файла ничего не спасает: у приложения с обязательными полями или
секретами всё равно упадёт валидация (R18), только вместо одного сообщения
«нет `config.toml`» получится каскад «поле пусто», за которым настоящая
причина — деплой не отрендерил файл — не видна.
Приложение, которое запускается вообще без конфигурации, этой конвенцией не
описывается: это отдельный случай и отдельная конвенция.
<!-- local:проверки -->
<!-- /local -->
### R4. В репозитории лежит образец, а не рабочий конфиг
**НЕ ДОЛЖЕН.** Реальный конфиг не коммитится; в репозитории — образец.
@@ -225,7 +247,7 @@
| R18.1 | обязательные поля заданы (непустота секретов — R15) | в ветке, которая это поле читает |
| R18.2 | пути существуют и доступны на запись/чтение по назначению | при первой записи, уже после отчёта об успешном старте |
| R18.3 | числовые диапазоны и единицы: доли, таймауты, счётчики попыток | искажённым поведением без единого сообщения об ошибке |
| R18.4 | строки, которые парсятся во что-то (длительности, зоны, URL), реально парсятся | при первом обращении к тому, что за ними стоит |
| R18.4 | строки, которые парсятся во что-то (длительности, зоны, URL, идентификаторы сущностей), реально парсятся | при первом обращении к тому, что за ними стоит |
| R18.5 | включённые секции консистентны: у включённой интеграции заданы все обязательные поля | при первом вызове интеграции, часто по расписанию |
**Почему.** Список минимальный и собран по одному признаку — правый
@@ -233,9 +255,6 @@
потеряна, и диагностируется как дефект приложения. Проверка на старте
сводит их все к одному моменту и одному сообщению.
<!-- local:проверки -->
<!-- /local -->
### R19. Проблемы конфига показываются разом
**ДОЛЖЕН.** Валидация собирает все найденные проблемы и выводит их одним
@@ -247,6 +266,26 @@
одного источника: разом они читаются как одна причина, по одной — как
череда несвязанных мелочей.
### R21. Значение поля в сообщении валидатора — по признаку секретности
**ДОЛЖЕН.** Состав сообщения определяется тем же признаком секретности
поля, которым уже пользуются R15 и R16:
| № | Поле | В сообщении |
|---|---|---|
| R21.1 | несекретное | имя поля, ожидание и полученное значение: «ожидалось 0–1, получено `1.5`» |
| R21.2 | секретное | имя поля и суть нарушения, без значения |
**Почему.** Сообщение без значения отправляет читателя в файл — сличать
глазами каждую строку списка R19; ошибки вида «секунды вместо миллисекунд»
или пробел в конце значения из такого сообщения не читаются вовсе. Значение
секретного поля при этом печатать некуда: вывод старта уходит в лог
супервизора и вывод CI, где круг доступа шире прав файла `0600`, — тот же
канал утечки, который закрывает R16. Отдельный список «что не печатать» не
заводится: признак один на R15, R16 и R21, а второй список разошёлся бы с
первым — и поле оказалось бы секретным для логов, но печатаемым
валидатором.
## Связано
- `arch/time.md` — формат времени; зона отображения — единственный