Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6.3 KiB
Спека: модуль tools/brain-config.mjs — чтение проектной настройки мозга
Дата: 2026-06-15 Статус: к исполнению (Фаза 1, Задачи 1–2)
Цель
Дать движку единый чистый модуль, который читает проектную настройку из файла
.claude/brain.local.md и отдаёт типизированные значения с безопасными
направлениями отказа. Захардкоженные «знания про конкретный проект», рассыпанные
по tools/*.mjs, должны получить один источник правды. Этот модуль — только
загрузчик и нормализатор настройки; он не меняет поведение существующих файлов
(они подключатся к нему отдельными задачами с дефолтами = текущие значения).
Контракт API
Модуль tools/brain-config.mjs (Node ESM, стиль каталога tools/) экспортирует
три чистые/полу-чистые функции:
parseBrainConfig(md) -> object— чистая. На вход markdown-строка (содержимое.claude/brain.local.md). Возвращает объект ключей из YAML-frontmatter. Наnull, пустую строку или текст без frontmatter — пустой объект{}.resolveConfig(raw) -> object— чистая. На вход результатparseBrainConfig. Накладывает дефолты и fail-safe направления (§D3). Возвращает полный объект настройки.loadConfig(root = '.', fsImpl = fs) -> object— I/O-обёртка. Читает${root}/.claude/brain.local.md; если файла нет — отдаёт дефолты (черезresolveConfig({})).fsImplинъектируется для тестов.
Ключи настройки: config_version, registry_path, state_dir, evidence_archive,
normative_files (список), project_url_whitelist (список), classifier_context,
economy_default, enabled_hook_groups (список).
Алгоритм парсинга frontmatter
Минимальный YAML-frontmatter без внешних зависимостей:
- Границы frontmatter — регэксп
^---\n([\s\S]*?)\n---(блок между первыми---). - Внутри блока построчно:
- строка вида
- item(элемент списка) добавляется к текущему ключу-списку; - строка вида
key: valueначинает новый ключ; пустоеvalueозначает начало списка (ключ инициализируется пустым массивом, последующие- itemв него падают); - непустое числовое
value(^\d+$) приводится к числу, иначе остаётся строкой; - после скалярного ключа «текущий ключ-список» сбрасывается.
- строка вида
- Строки, не подходящие ни под один шаблон, игнорируются.
Fail-safe направления (дефолты + отказ)
resolveConfig накладывает безопасные дефолты на отсутствующие ключи. Отсутствие
или невалидность ключа НЕ отключает молча безопасность.
| Ключ | Дефолт при отсутствии | Направление |
|---|---|---|
state_dir |
.claude/brain-state |
безопасный дефолт |
evidence_archive |
brain-state |
безопасный дефолт |
normative_files |
[] (легитимно пусто) |
явная пустота |
registry_path |
'' (зависимости off осознанно) |
явная пустота |
project_url_whitelist |
[] + флаг project_url_whitelist_failClosed: true |
fail-closed (внешнее закрыто) |
classifier_context |
строка с маркером generic |
безопасная деградация качества |
economy_default |
'100' |
безопасный дефолт |
project_url_whitelist_failClosed равен true, когда whitelist пуст или не массив
(потребитель обязан блокировать внешние адреса), и false, когда список непуст.
Крайние случаи
parseBrainConfig(null)иparseBrainConfig('')→{}.- Текст без frontmatter (
'тело без ---') →{}. loadConfigпри отсутствующем файле (читалка бросает) → дефолты, без падения.- Числовое значение (
config_version: 1) → число1, не строка. - Список из одного элемента → массив длины 1.
Конвенция заголовка модуля
Файл начинается с shebang #!/usr/bin/env node и JSDoc-блока, описывающего
назначение (единый источник проектной настройки мозга, чистый парсер +
resolveConfig с fail-safe дефолтами). Стиль — как у соседних чистых модулей
каталога tools/ (замороженные константы через Object.freeze, ESM-импорт fs).
Критерий приёмки
npx vitest run --config vitest.config.tools.mjs tools/brain-config.test.mjs— сначала RED (нет реализации), затем GREEN (все кейсы контракта и fail-safe).npx vitest run --config vitest.config.tools.mjs— полный сводtools/остаётся зелёным (модуль новый, существующее поведение не затронуто).
[
{"id":"tools-pure-module-style","kind":"EXTRACTED","ref":"tools/cost-pricing.mjs","anchor":"export const PRICING = Object.freeze("}
]