@deksden-com/dd-console 0.0.0-stage → 0.2.0

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.
@@ -0,0 +1,456 @@
1
+ # План реализации локальной веб-консоли dd-flow / dd-eval
2
+
3
+ Статус: базовая реализация первого read-only выпуска поставлена; этот документ
4
+ остаётся целевой спецификацией для незакрытых compatibility/performance пунктов.
5
+ Дата: 2026-09-13, актуализирован: 2026-09-13.
6
+ Основание: [исследование текущего кода](research.md).
7
+
8
+ ## 0. Зафиксированные решения
9
+
10
+ | Вопрос | Решение |
11
+ |---|---|
12
+ | Продукт | Отдельный локальный пакет и репозиторий `deksden-com/dd-console` |
13
+ | Первая ценность | Наблюдение и детализация реальных запусков dd-flow и dd-eval |
14
+ | Доступ | Один пользователь, текущая машина, только loopback |
15
+ | Выполнение | Раннеры остаются единственными владельцами процесса и состояния; консоль только читает |
16
+ | Обновление | Polling с общим серверным кэшем; SSE — после измеренного первого выпуска |
17
+ | UI | Отдельные URL для сущностей, breadcrumbs, Back/Forward, детали по запросу |
18
+ | Управление | Не входит в первый выпуск; появится отдельным этапом на существующих control contracts |
19
+ | Хранение консоли | `DD_CONSOLE_HOME`, по умолчанию `~/.dd-console`; только настройки sources, кэш и логи консоли |
20
+
21
+ Инструментарий зафиксирован в разделе 3. До подключения реальных данных остаётся проверить compatibility matrix и наличие публичной проекции текущего состояния Eval. Это обязательные результаты этапа 1, а не предположение о готовности существующих контрактов.
22
+
23
+ ## 1. Решение и границы первого выпуска
24
+
25
+ Локальный сервер с веб-интерфейсом, двумя разделами Flows/Evals и отдельными экранами детализации. Каждый экран доступен по прямому URL. Breadcrumbs, браузерные Back/Forward и обновление страницы сохраняют корректную навигацию.
26
+
27
+ Первый выпуск позволяет наблюдать как самостоятельные flow, так и flow внутри eval: стадии, работы, сессии, события, проверки, результаты и причины ожидания. Сервер наблюдения не владеет процессами раннеров; закрытие браузера и остановка сервера не останавливают выполнение.
28
+
29
+ Первый выпуск включает polling и чтение истории. Pause/resume/stop, запуск новых заданий, удалённый доступ и SSE — следующие этапы. Полные диалоги не загружаются автоматически: в первом выпуске доступны структурированные события, зарегистрированные результаты и безопасный просмотр текстовых артефактов порциями.
30
+
31
+ ## 2. Экраны и навигация
32
+
33
+ `source` в маршрутах — стабильный непрозрачный идентификатор зарегистрированного runtime. Короткие RUN/WORK ID могут совпадать в разных runtime и проектах, поэтому source и project обязательны для Flow. Его соответствие каноническому корню сохраняется в конфигурации консоли между перезапусками. При исчезновении источника ссылка показывает unavailable, а не переиспользуется для другого источника.
34
+
35
+ | Экран | Предлагаемый URL | Содержимое и переходы |
36
+ |---|---|---|
37
+ | Обзор | `/` | Активные и требующие внимания запуски; переходы в Flows/Evals |
38
+ | Источники | `/sources` | Подключённые sources, состояние чтения, диагностика и инструкции подключения через CLI |
39
+ | Flows | `/flows` | Фильтры: проект, источник, статус, harness; сортировка и список RUN |
40
+ | Flow | `/sources/:source/projects/:project/runs/:run` | Сводка, стадии/попытки, работы, сессии, проверки, события и артефакты |
41
+ | Стадия / попытка | `/sources/:source/projects/:project/runs/:run/stages/:stage/attempts/:attempt` | Результат конкретной попытки, её работы и проверки; предыдущая/следующая попытка |
42
+ | Работа | `/sources/:source/projects/:project/runs/:run/works/:work` | Статус, зависимости, родительская работа, сессии, проверки, результат |
43
+ | Сессия | `/sources/:source/projects/:project/runs/:run/sessions/:session` | Harness, роль, наблюдаемая модель, связанные работы, события и артефакты |
44
+ | Проверка | `/sources/:source/projects/:project/runs/:run/checks/:check` | Результат конкретной проверки, попытка, связанные работа/сессия и артефакты |
45
+ | Evals | `/evals` | Case, checkpoint, статус, время; фильтры и список EVAL |
46
+ | Eval | `/sources/:source/evals/:eval` | Параметры, executions, состояние candidate/Judge, версии и evidence |
47
+ | Execution | `/sources/:source/evals/:eval/executions/:execution` | Статус запуска, операции, recovery, результат и проверенная ссылка на вложенный flow |
48
+ | Артефакт | `/artifacts/:artifact` | Имя, тип, источник, ограниченный текстовый просмотр или скачивание |
49
+
50
+ Вкладки внутри сущности: `?tab=overview|works|sessions|checks|events|artifacts`. Использовать только применимые вкладки, не показывать пустую структуру ради единообразия. Фильтры, сортировка и pagination cursor списка находятся в URL. Состояние прокрутки хранится на запись browser history.
51
+
52
+ ### Breadcrumbs
53
+
54
+ - Самостоятельный flow: `Обзор → Flows → RUN → Работа`.
55
+ - Из eval: `Обзор → Evals → EVAL → Execution → RUN → Работа`.
56
+ - Сессия, открытая из работы: добавить работу только при подтверждённой связи; сессия может обслуживать несколько работ, поэтому это контекст навигации, а не единственный родитель сущности.
57
+ - Канонический URL Flow/Session остаётся один. Параметр `context` хранит проверяемые идентификаторы eval/execution и при необходимости work; сервер валидирует связи. Не использовать произвольный return URL.
58
+ - При прямом открытии без контекста строить каноническую цепочку. При удалённом/устаревшем контексте показать уведомление и доступную цепочку, не ломая экран.
59
+ - Последний элемент цепочки — текущая страница; остальные — ссылки. Длинную цепочку можно сворачивать, оставляя доступ к скрытым предкам с клавиатуры.
60
+ - Breadcrumbs ведут по иерархии, Back/Forward — по фактической истории. Обе модели должны работать независимо.
61
+
62
+ ### Общий каркас экрана
63
+
64
+ Шапка с разделами Flows/Evals и состоянием подключения; breadcrumbs; заголовок и статус сущности; текущая операция/причина ожидания; время обновления; основное содержимое и вкладки. Loading, empty, partial, unavailable, unsupported и not found имеют отдельные представления.
65
+
66
+ Автообновление не сбрасывает фокус, вкладку, фильтры, прокрутку и раскрытые детали. Для событий есть переключатель «Следить за новыми»; при ручной прокрутке вверх новые строки показываются счётчиком, без принудительного перемещения вниз. Статусы различаются текстом и значками, а не только цветом.
67
+
68
+ ### Информационная архитектура экранов
69
+
70
+ У каждой сущности сверху компактная сводка: идентификатор, статус, начало/последнее наблюдение, причина ожидания или ошибки и ближайшее полезное действие человека. Ниже — только относящиеся к ней данные. Список всегда показывает агрегированные строки; тяжёлые детали открываются отдельной страницей или вкладкой, не загружаются для всех строк заранее.
71
+
72
+ | Экран | Верхняя сводка | Детали |
73
+ |---|---|---|
74
+ | Обзор | активные, ожидающие и требующие внимания источники | последние изменения, ссылки на конкретные RUN/EVAL |
75
+ | Flow | status, stage, controller, last observation, duration | attempts, work graph, sessions, checks, timeline, artifacts |
76
+ | Работа | status, parent, dependencies, launch policy | попытки, сессии, результаты и связанные checks |
77
+ | Сессия | harness, role, observed model, status | связанные works, хронология, безопасные артефакты |
78
+ | Eval | case/checkpoint, definition/engine identity, candidate/Judge | executions, evidence, журнал и диагностика определения |
79
+ | Execution | status, active/pending operation, recovery, nested Flow | события операции, модель, результат, ссылка на derived Flow source |
80
+
81
+ Три независимые оси: `execution_status` (исходный статус и документированное отображение, включая planned/queued/running/waiting/paused/blocked/failed/completed/cancelled/stopped/unknown), `availability` (available/partial/unavailable/unsupported) и `control` (запрошенное действие, подтверждение, recovery). Например, completed остаётся последним известным статусом при unavailable; запрос отмены не подменяет running на cancelled. Неизвестный статус производителя показывается буквально с диагностикой, не считается успехом. Таблица отображения обязана иметь ссылку на source schema и fixture для каждого правила.
82
+
83
+ «Требует внимания» рассчитывается отдельно: подтверждённая ошибка, blocker, запрос участия человека или recovery; недоступность наблюдения отмечается как проблема чтения. Длительное молчание само по себе не означает зависание. Длительности берутся из подтверждённых timestamps; у незавершённой работы показывается elapsed, а при некорректных часах — unknown. Счётчики обзора разделяют EVAL и Flow, чтобы execution и его Flow не удваивали число запусков одного типа.
84
+
85
+ ## 3. Размещение кода и запуск
86
+
87
+ Проект **dd-console** размещён в отдельном репозитории `deksden-com/dd-console`, локально — `/Users/deksden/Documents/_Projects/dd-console`. В нём реализованы Node/TypeScript package, loopback HTTP server, registry, Flow/Eval readers, browser UI и contract/browser tests. Консоль — общий пользовательский продукт для dd-flow и dd-eval, поэтому её UI, HTTP API, тесты и релизы принадлежат одному репозиторию и не привязаны к релизу одного раннера. Это уточнение заменяет первоначальное предложение исследования размещать сервер в dd-flow-cli.
88
+
89
+ Команда: `dd-console serve --port 0 [--open] [--flow-home <path>] [--eval-home <path>]`. `--port 0` выбирает свободный порт; фиксированный порт позволяет сохранять browser bookmarks. Сервер печатает фактический URL и поддерживает JSON receipt. Запуск в foreground; остановка по Ctrl+C завершает только сервер наблюдения. Автоматическое открытие браузера выполняется только с --open. Возможные алиасы `dd-flow dashboard open` и `dd-eval dashboard open` обсуждать отдельно после рабочего выпуска; они не обязательны для запуска консоли.
90
+
91
+ Один Node/TypeScript пакет: сервер и собранный frontend поставляются вместе, одним процессом и на одном порту. Первый сервер: Node HTTP, тот же origin для API и веб-ресурсов, без новой БД. Минимальный History API router отвечает за URL/Back/Forward; сервер возвращает оболочку для допустимых UI routes и JSON ошибки для неизвестных API routes. Отдельные npm workspaces, frontend runtime framework и framework backend для первого выпуска не нужны.
92
+
93
+ ### Инструментарий первого выпуска
94
+
95
+ - Node.js `>=26.0.0`, TypeScript, ESM и npm package: первый выпуск проверяется на Node 26, как текущий dd-flow-cli. Предыдущее `>=22` было неточным: node:sqlite появился в 22.5, без специального флага доступен с 22.13 ([Node SQLite](https://nodejs.org/api/sqlite.html)). Поддержка других major не заявляется без CI; package manager — npm с lockfile.
96
+ - Frontend — TypeScript, обычные DOM-компоненты и History API, статическая CSS. Это сохраняет один понятный пакет, не добавляет runtime framework и не препятствует переходу на компонентный UI, если после первого выпуска появится реальная потребность.
97
+ - Отдельные tsconfig для server (Node ESM) и web (DOM, browser ESM); browser imports используют относительные `.js` пути. `tsc` компилирует TS, отдельный build script копирует HTML/CSS в `dist/web`. Сервер находит assets относительно своего модуля, не cwd. Development-режим использует тот же routing и политику доступа. Pack smoke запускается из постороннего каталога без исходников.
98
+ - Unit/contract tests — встроенный `node:test`; browser acceptance — Playwright как dev dependency. Проверки реальных fixture не зависят от сети и не требуют запущенного чужого раннера.
99
+ - ESLint и форматирование добавляются при bootstrap пакета. Скрипты: `build`, `typecheck`, `lint`, `test`, `test:browser`, `check` (последовательно запускает все обязательные проверки), `pack:check`.
100
+
101
+ Это намеренно не SPA framework и не отдельный API service: первый выпуск — локальная операционная консоль, а не продукт с удалёнными пользователями. Решение пересматривается только после измеримой проблемы с сложностью UI или производительностью.
102
+
103
+ Предлагаемая структура репозитория:
104
+
105
+ ```text
106
+ dd-console/
107
+ src/
108
+ cli.ts
109
+ server/ # HTTP, API, discovery источников и артефакты
110
+ readers/ # flow/eval, проверка версий, read-only доступ
111
+ contracts/ # публичные DTO консоли
112
+ web/ # экраны, router, breadcrumbs, стили
113
+ test/
114
+ fixtures/ # обезличенные snapshots поддерживаемых версий
115
+ docs/ # архитектура, контракты источников, эксплуатация
116
+ package.json
117
+ ```
118
+
119
+ Runtime-данные остаются в DD_FLOW_HOME/DD_EVAL_HOME. Собственная конфигурация источников и необязательный восстанавливаемый кэш — в DD_CONSOLE_HOME (по умолчанию `~/.dd-console`), никогда в исходниках. Консоль не копирует evidence в свою БД и не становится владельцем состояния RUN.
120
+
121
+ Границы репозиториев: dd-console владеет представлением, навигацией и адаптацией чтения; dd-flow-cli — механикой Flow и документированным контрактом наблюдения; dd-eval — механикой Eval и его контрактом наблюдения; dd-memorybank — принятой общей документацией. Не импортировать внутренние TypeScript-модули соседнего checkout, не использовать workspace symlinks и не дублировать reducers бизнес-состояния. Если имеющейся проекции недостаточно, добавлять компактный read-only export у владельца раннера, затем адаптер в консоли. Прямое чтение SQLite допускается только для проверенной поддерживаемой версии и документированных полей; это ограничение явно отражать в compatibility matrix.
122
+
123
+ Существующий dd-flow dashboard использовать как источник требований и проверенных полей; автоматически переносить большой renderer и его зависимости в новый репозиторий не следует. Сохраняется действующий статический dashboard раннера.
124
+
125
+ dd-eval отвечает за документирование своего формата наблюдения и producer contract tests; dd-console — за fixture/contract tests адаптера. Первый адаптер читает сохранённые manifest/journal/results, без импорта runner.mjs и без запуска закреплённых исполняемых файлов из старых manifests при открытии страницы. При необходимости дополнительный компактный экспорт добавляется в dd-eval; зависимость от checkout dd-eval для установленной консоли не допускается.
126
+
127
+ Изменения dd-memorybank нужны только для окончательного пользовательского контракта запуска и эксплуатации; самостоятельный runtime-компонент там не создаётся.
128
+
129
+ ### Дизайн и поведение интерфейса
130
+
131
+ Аудитория — человек, который сопровождает долгий запуск и должен быстро понять, где он остановился, что уже доказано и в какую деталь перейти. Основная работа страницы — дать честный ответ «что происходит сейчас» и путь к доказательству, а не нарисовать абстрактный процент прогресса.
132
+
133
+ Визуальная идея: **операционный журнал и карта исполнения**, а не маркетинговый dashboard. Основной фон — холодный `#F5F7FA`; поверхности — `#FFFFFF`; основной текст — `#18212B`; вторичный — `#5D6875`; рабочий акцент — индиго `#3157A8`; состояния внимания — янтарный `#9A5D00`; ошибка — бордовый `#A33A3A`. Цвет не является единственным носителем статуса. Для данных использовать моноширинный системный стек, для обычного текста — системный sans-serif; display-шрифт не нужен: читабельность и плотность данных важнее характерной типографики.
134
+
135
+ Макет: слева фиксированная навигация Sources / Flows / Evals с количеством требующих внимания; сверху один ряд breadcrumbs и статуса подключения; основная колонка шириной до 1440px; справа на широком экране узкая «наблюдение сейчас» панель с freshness и активной операцией. На планшете правая панель становится верхней сводкой; на телефоне навигация превращается в меню, таблицы — в карточки с раскрытием. Единственный выразительный элемент — **линия исполнения**: тонкая, доступная текстовая шкала «последнее подтверждённое событие → текущая операция → ожидаемое следующее условие», которая показывает причины ожидания без фиктивного процента выполнения.
136
+
137
+ ```text
138
+ ┌ Sources / Flows / Evals ┐ ┌ Overview › EVAL-… › Execution-… ───────────┐
139
+ │ • attention count │ │ EVAL-… waiting for provider │
140
+ │ • filters │ │ Line: completed CODE → observe repair │
141
+ │ │ ├ Overview ┬ Executions ┬ Events ┬ Evidence ┤
142
+ │ │ │ summary / table / selected detail │
143
+ └──────────────────────────┘ └─────────────────────────────────────────────┘
144
+ ```
145
+
146
+ Копирайт использует термины, знакомые пользователю: «ожидает провайдера», «нужно восстановление», «нет данных о Judge», «источник недоступен». Ошибка всегда содержит следующую безопасную проверку, если она известна. Анимация ограничивается одним коротким обновлением линии исполнения; `prefers-reduced-motion` полностью её отключает.
147
+
148
+ ## 4. Чтение данных и API
149
+
150
+ Источники: текущий DD_FLOW_HOME, указанный DD_EVAL_HOME и подтверждённые managed-runtime связи вложенных execution. Дополнительные homes регистрируются явно. Не сканировать весь диск и каталоги чужих сессий. Обнаружение новых запусков ограничено зарегистрированными корнями; каталог перечитывается периодически, по запросу списка и при явном refresh.
151
+
152
+ ### 4.1. Реестр источников и идентичность
153
+
154
+ При первом запуске `dd-console serve` создаёт либо открывает `DD_CONSOLE_HOME/sources.json`. Запись содержит ID, kind, canonical root, label, время регистрации и registry schema version. Версия reader не является свойством identity. Root хранится только на сервере; изменение root существующего ID запрещено, отключённые записи остаются tombstones. Правила подключения приведены ниже.
155
+
156
+ | Kind source | Кто создаёт | Корень | Идентичность сущности |
157
+ |---|---|---|---|
158
+ | `flow-home` | serve input или явная регистрация | один DD_FLOW_HOME | `source_id + project_id + run_id` |
159
+ | `eval-home` | serve input или явная регистрация | один DD_EVAL_HOME | `source_id + eval_run_id` |
160
+ | `eval-execution-flow` | только Eval reader после проверки managed-runtime | execution runtime_root | `source_id + parent_eval_id + execution_id + project_id + run_id` |
161
+
162
+ Derived `eval-execution-flow` не регистрируется как независимый глобальный source и не становится доступен по пути, присланному браузером. Сервер заново проверяет `managed-runtime.json`: ожидаемые `project_root`, `runtime_root`, schema и `run_id` должны совпасть с родительским execution. Именно эта проверка разрешает переход `Execution → Flow`.
163
+
164
+ Корневой source ID — UUID, сохраняемый в registry. Derived ID детерминирован из parent source ID, eval ID и execution ID; принадлежность runtime всё равно проверяется отдельно. Resolver восстанавливает связь при прямом открытии после перезапуска, без предварительного посещения EVAL. Artifact ID аналогично привязан к source/entity и зарегистрированной относительной ссылке; ID не является разрешением читать файл. При повторной регистрации того же kind/root используется существующий ID. Дубликат derived runtime, явно подключённый как flow-home, получает одну каноническую identity и несколько связей в UI.
165
+
166
+ ### Первое подключение и конфигурация
167
+
168
+ В первом выпуске настройка доступна через CLI: `sources list`, `sources add --kind flow|eval --path <path> [--label <name>]`, `sources remove <id>`. Remove отключает регистрацию и сохраняет tombstone для старых ссылок, не удаляет данные. `serve --flow-home/--eval-home` — сокращение идемпотентного add. Registry дополняется явно заданными flags; при отсутствии источников используются существующие каталоги из DD_FLOW_HOME/DD_EVAL_HOME либо стандартные `~/.dd-flow`/`~/.dd-eval`, отсутствующие каталоги не создаются. Пустой registry открывает экран подключения с готовыми командами.
169
+
170
+ Один сервер на DD_CONSOLE_HOME: process lock с проверкой владельца; второй запуск показывает действующий URL или сообщает конфликт, не перезаписывает registry. CLI-изменения registry используют краткую межпроцессную блокировку и atomic rename, сервер перечитывает ревизию. Невалидный config не заменяется пустым: запуск завершается с конкретной диагностикой, исходный файл сохранён. При ошибке перечитывания работающий сервер сохраняет последнюю валидную конфигурацию и показывает предупреждение.
171
+
172
+ Первый выпуск квалифицируется на macOS. Default bind — 127.0.0.1; --host не входит в CLI. Default port 0, фиксированный --port поддерживает bookmarks; новый случайный порт меняет origin, что явно указано в помощи. В --json stdout содержит один startup receipt, логи идут в stderr; --open выполняется после listen. Ctrl+C освобождает lock и закрывает только console resources.
173
+
174
+ ### 4.2. Публичная модель ответа
175
+
176
+ Все JSON endpoints возвращают общий envelope. Он отделяет успешное чтение от пригодности данных и не подменяет отсутствующие значения нулём:
177
+
178
+ ```json
179
+ {
180
+ "schema_version": "dd-console/api@1",
181
+ "request_id": "REQ-…",
182
+ "observed_at": "2026-09-13T12:00:00.000Z",
183
+ "source": {
184
+ "id": "SRC-…",
185
+ "kind": "flow-home",
186
+ "availability": "available",
187
+ "source_updated_at": "2026-09-13T11:59:59.000Z"
188
+ },
189
+ "data": {},
190
+ "diagnostics": []
191
+ }
192
+ ```
193
+
194
+ `diagnostics` — безопасные записи `{ code, severity, message, entity? }`. Для агрегатов `source: null`, отдельные части имеют собственную source/availability. У cached data `observed_at` — время фактического чтения, отдельно `served_at` — время ответа; source_updated_at и last_event_at не являются heartbeat. HTTP 200 содержит available/partial/unavailable/unsupported, включая detail известных источников; 404 — неизвестная identity, 400 — неверные параметры, 409 stale_cursor — потеря snapshot, 405 — неподдерживаемый method, 500 — ошибка консоли, 503 — занятая очередь чтения с Retry-After. Неверный breadcrumb context даёт diagnostic и каноническую цепочку при HTTP 200, не блокирует доступную сущность. Error envelope содержит request_id и безопасный code/message.
195
+
196
+ | DTO | Обязательные поля | Не включать |
197
+ |---|---|---|
198
+ | `SourceSummary` | ID, kind, label, availability, freshness | root path, token, raw DB config |
199
+ | `RunSummary` | identity, status, stage, created/updated, attention reason | полный index_json |
200
+ | `FlowDetail` | RunSummary, stages/attempts, work/session/check summaries, controller projection | owner/lease tokens, private transcript body |
201
+ | `EvalDetail` | identity, case/checkpoint, immutable definition identity, executions, candidate/Judge states | raw manifest и скрытые ответы subject |
202
+ | `ExecutionDetail` | identity, state, operations, recovery, derived Flow link | runtime env, executable paths |
203
+ | `WorkDetail` / `SessionDetail` | identity, state, safe relations, timestamps, results summary | raw adapter payloads без allowlist |
204
+ | `EventPage` | source-local cursor, events, has_more, next_cursor | глобальный причинный порядок между sources |
205
+ | `ArtifactInfo` | opaque ID, name, media type, byte length, preview eligibility | абсолютный filesystem path |
206
+
207
+ ### 4.3. API первого выпуска
208
+
209
+ Все list/event endpoints ограничивают `limit` диапазоном 1–200; default 50. Cursor привязан к endpoint, source и фильтрам. Список страниц использует замороженную ревизию каталога (TTL 5 минут), стабильную сортировку с полным identity как tie-breaker. Polling предлагает новые строки без перестановки просматриваемых страниц; явное обновление начинает новую ревизию. По истечении snapshot или перезапуске сервера — 409 stale_cursor; UI сохраняет фильтры, объясняет сброс страницы. Event cursor использует generation/offset, append его не инвалидирует; rotate/truncate требует reset. `refresh=1` — ограниченный hint кэшу, без обхода лимитов.
210
+
211
+ | Метод и endpoint | Ответ | Назначение |
212
+ |---|---|---|
213
+ | `GET /api/v1/overview` | обзор всех sources | домашний экран |
214
+ | `GET /api/v1/sources` | `SourceSummary[]` | статусы подключений |
215
+ | `GET /api/v1/flows` | paged `RunSummary[]` | список самостоятельных и доступных derived Flow |
216
+ | `GET /api/v1/sources/:source/projects/:project/runs/:run` | `FlowDetail` | экран Flow |
217
+ | `GET …/stages/:stage/attempts/:attempt` | stage attempt detail | детализация попытки |
218
+ | `GET …/works/:work` | `WorkDetail` | экран работы |
219
+ | `GET …/sessions/:session` | `SessionDetail` | экран сессии |
220
+ | `GET …/checks/:check` | `CheckDetail` | экран проверки |
221
+ | `GET /api/v1/evals` | paged Eval summary | список EVAL |
222
+ | `GET /api/v1/sources/:source/evals/:eval` | `EvalDetail` | экран EVAL |
223
+ | `GET …/executions/:execution` | `ExecutionDetail` | execution и ссылка на Flow |
224
+ | `GET /api/v1/events` | `EventPage` | events конкретной сущности, определяемой query identity |
225
+ | `GET /api/v1/artifacts/:artifact` | `ArtifactInfo` | метаданные |
226
+ | `GET /api/v1/artifacts/:artifact/preview` | ограниченная текстовая страница | безопасный просмотр |
227
+ | `GET /api/v1/artifacts/:artifact/download` | поток attachment | скачивание по явному действию |
228
+ | `GET /healthz` | `{ ok, version }` | доступность процесса, без состояния запусков |
229
+
230
+ В первом выпуске нет `POST`, `PUT`, `PATCH`, `DELETE` API. Любой незнакомый путь или HTTP method получает JSON error и не падает на filesystem fallback.
231
+
232
+ Слой наблюдения возвращает небольшие публичные структуры: RunSummary, FlowDetail, EvalDetail, ExecutionDetail, WorkDetail, SessionDetail, EventPage, ArtifactInfo. У каждого ответа — schema_version, source_id, observed_at, source_updated_at, availability и diagnostics. Источники ошибок различаются: ошибка раннера не равна ошибке чтения или отключению браузера.
233
+
234
+ Идентификаторы, статусы и связи берутся из источника. Производные поля явно обозначаются. Не вычислять общий «процент готовности» из номера стадии; отсутствующий usage/score возвращается null с причиной, а не 0. Показать configured и observed model отдельно, если обе доступны.
235
+
236
+ API повторяет экранные сущности: `/api/v1/sources`, `/overview`, `/flows`, `/evals` и detail endpoints с source/entity IDs. Для списков и событий обязательны limit/cursor с верхним пределом; для файлов — artifact ID и ограничение размера. API никогда не принимает произвольную команду или путь к файлу/БД.
237
+
238
+ Flow reader реализует readOnly/query_only самостоятельно, без импорта getDatabase из соседнего проекта. Наличие файла и обязательных таблиц проверяется до чтения: существующий getDatabase может вернуть пустую БД при отсутствующем файле, что нельзя показывать как «нет запусков». Ревизия публичной проекции определяет приоритет runtime snapshot над устаревшим index, как в producer authoritativeIndex; правила фиксируются в контракте. Совместимость определяется schema/capabilities, не только package version или SQLite schema_version.
239
+
240
+ Для Eval manifest и конечного report недостаточно, чтобы знать текущие pending operations: runnerStatus использует reduceEvents и запускает живые status-команды. Поэтому этап 1 обязан выделить у dd-eval публичную сохраняемую observation-проекцию с last_sequence, run/execution results, provenance и отдельным candidate/Judge состоянием. Её обновляет владелец раннера; консоль не копирует reducer и не вызывает runnerStatus. При отсутствии проекции у исторического запуска доступны проверенные manifest/report/journal, но текущий статус unknown/partial. Если полного актуального состояния нет и producer export не реализован, интеграция Eval считается незавершённой, а не «готовой с заглушкой».
241
+
242
+ Для первого выпуска запрещены автоматические вызовы CLI/provider probes при любом HTTP чтении, включая details. «Живое» означает чтение новых сохранённых фактов; состояние процесса без таких фактов помечается unknown. Дополнительный прямой опрос провайдера — отдельная будущая возможность.
243
+
244
+ Журнал JSONL читается инкрементально: хранить смещение и незавершённый хвост строки. Незавершённая последняя строка во время append ожидает следующего чтения; повреждённая законченная строка выдаёт диагностику, не пропускается молча. Усечение/замена файла сбрасывает cursor. Для SQLite использовать короткие read snapshots, не держать транзакцию между запросами.
245
+
246
+ Отдельные файлы и БД не образуют атомарный общий snapshot: сохранять revisions/timestamps источников и показывать partial при расхождении. Не объявлять остановку всех процессов доказанной на основании нескольких независимых чтений.
247
+
248
+ ### 4.4. Матрица совместимости и readers
249
+
250
+ Перед реализацией каждый reader получает собственную compatibility matrix: producer, supported schema/build ranges, минимальные признаки, fixtures, fallback, owner изменений. Матрица начинается с реально установленных/актуальных релизов и пополняется только после теста на fixture и реальном read-only source.
251
+
252
+ | Reader | Первичный источник | Проверка | Fallback при несовместимости |
253
+ |---|---|---|---|
254
+ | Flow list/detail | публичная проекция run + read-only store | contract/schema, project/run ownership | summary `unsupported`, без деталей |
255
+ | Flow timeline | timeline JSONL | event schema, cursor generation, boundary строки | доступная сводка без журнала |
256
+ | Eval list/detail | eval directory, manifest, events, report/candidate | schemas, run identity, event sequence | summary `unsupported` или `partial` |
257
+ | Eval execution Flow | managed-runtime + изолированный Flow store | schema, root/run identity и containment | execution остаётся видимым, link unavailable |
258
+ | Artifact | allowlisted registered artifact reference | realpath containment, size/media policy | metadata с diagnostic, без выдачи файла |
259
+
260
+ Readers ничего не мигрируют, не исправляют и не синхронизируют. Если текущие dd-flow-cli/dd-eval проекции не дают безопасного стабильно читаемого поля, решение — компактный producer export в репозитории владельца с его contract tests, а не копирование внутренней бизнес-логики в dd-console.
261
+
262
+ ### Большие источники и холодный запуск
263
+
264
+ Discovery выполняется порциями с прогрессом indexed/total-known; неполная индексация не выглядит как пустой список. Source error изолирован от других roots. Cold start журнала читает ограниченный хвост для событий, не выводит итог операции из неполной истории; состояние берётся из producer snapshot. Полная доступная история догружается страницами. Максимальная строка JSONL — 1 МиБ; превышение фиксируется как diagnostic, обработка продолжения строки не накапливает бесконечный buffer.
265
+
266
+ Синхронный SQLite и разбор больших журналов выполняются в ограниченном пуле worker_threads (начально 2), чтобы busy_timeout не блокировал HTTP и /healthz. Read task имеет deadline 2 секунды; timeout возвращает последнюю snapshot со stale/diagnostic, освобождает или пересоздаёт только console worker. Глобальный LRU кэш — целевые 64 МиБ, очередь — 32 задания, журнальные чанки — 256 КиБ. Это стартовые бюджеты для измерения; превышение не должно молча увеличивать память или создавать новые workers.
267
+
268
+ SQLite читается с существующим WAL; нельзя копировать только .sqlite активного источника и нельзя выставлять immutable на живой базе. Fixture создаётся согласованным backup либо производителем тестовой БД. Read-only SQL не гарантирует отсутствие SQLite служебных файлов: отдельно проверить WAL/SHM поведение на fixture. Строго неизменяемый архив при необходимости читается через согласованную копию в console cache, а не через запись рядом с evidence.
269
+
270
+ ## 5. Живое обновление и нагрузка
271
+
272
+ Начальные целевые интервалы: выбранный экран — 2 секунды, обзор — 5 секунд; скрытая вкладка замедляет polling. Запросы не перекрываются; при уходе с экрана отменяются. После ошибок — backoff, после восстановления — новый snapshot. Это параметры для проверки, не измеренные характеристики текущего кода.
273
+
274
+ Один серверный reader/cache на источник обслуживает все вкладки. Чтение старой части большого журнала повторно не выполняется для каждого refresh. Первичная загрузка истории имеет ограничение объёма; старые страницы читаются по запросу. Состояние «подключено» означает доступность сервера; отдельно показывать возраст исходных данных.
275
+
276
+ SSE добавлять после первого выпуска, если это оправдано измерениями. Использовать snapshot + cursor + update/reset и reconnect. Порядок событий гарантировать только внутри источника. Не вводить единую шину событий и не переписывать раннеры ради интерфейса.
277
+
278
+ ## 6. Границы локального сервера
279
+
280
+ Bind только loopback, проверка Host и Origin, без permissive CORS. Не отдавать owner/lease tokens, env, сырые DB rows и manifests целиком. Артефакты разрешать по realpath в зарегистрированных корнях; проверять symlinks и traversal. HTML/JS артефакты отдавать как скачивание либо экранированный текст, не исполнять в origin консоли. Логи и имена сущностей экранировать.
281
+
282
+ Просмотр не вызывает migrations, reconcile, usage recalculation, resume и обновление run projections. Полезные существующие getters перед подключением проверяются по цепочке вызовов. В первом выпуске HTTP API только для чтения.
283
+
284
+ Отсутствующий Origin допустим для обычного GET; присутствующий чужой Origin отклоняется. Host проверяется с фактическим портом, API выдаёт application/json и nosniff. Артефакты разрешены только по зарегистрированным публичным ссылкам, не по любому файлу внутри home: env, credentials, private prompts и полные transcripts исключены. Metadata/preview/download имеют одну политику. Preview — до 256 КиБ за страницу, Markdown/JSON отображаются текстом, HTML/SVG не исполняются. Download идёт потоком с backpressure; подмена symlink между регистрацией и открытием входит в тесты.
285
+
286
+ Persistent cache payloads по умолчанию отключён; локальные logs ограничены 3 файлами по 5 МиБ, содержат timings/bytes/codes/opaque IDs. Registry доступен только текущему пользователю. Assets имеют build revision; после обновления UI выполняет согласованный reload. API/data не сохраняются service worker или offline cache.
287
+
288
+ ## 7. Полный план поставки
289
+
290
+ Каждый этап заканчивается работающей, проверяемой частью. Нельзя начинать следующую интеграцию на допущении, что незафиксированный контракт раннера «наверняка подойдёт».
291
+
292
+ ### Этап 1. Основание и contract audit
293
+
294
+ **Цель:** доказать, что консоль может только читать нужные данные актуальных раннеров.
295
+
296
+ 1. Зафиксировать актуальные commit/version dd-flow-cli и dd-eval, их публичные schemas и расположение поддерживаемых artefacts.
297
+ 2. Выписать для каждого экрана поля, их producer, schema/version, safe display transformation и поведение при отсутствии.
298
+ 3. Проверить read-only путь Flow: открыть только `read_existing`, выполнить разрешённые queries и сравнить до/после файлы store/run projection.
299
+ 4. Проверить read-only путь Eval: manifest, journal, report/candidate и `managed-runtime.json`; не вызывать `runnerStatus`, если он запускает live child commands для массового списка.
300
+ 5. Сформировать fixtures из копий разрешённых малых артефактов: `flow-completed`, `flow-running`, `flow-retry`, `eval-multi-execution`, `eval-recovery`, `unavailable`, `unsupported`. Удалить secrets, личные пути, токены, ответы subject и большие transcript/log blobs.
301
+ 6. Создать compatibility matrix и contract tests readers на fixtures. Для каждого требуемого, но недоступного поля открыть отдельную задачу в dd-flow-cli или dd-eval с предложением producer export.
302
+ 7. Согласовать с владельцами раннеров изменения публичного контракта до написания адаптера, если они нужны.
303
+
304
+ **Артефакты в dd-console:** `docs/compatibility.md`, `docs/data-contract.md`, `test/fixtures/…`, `test/contracts/…`.
305
+ **Готово, когда:** fixture readers дают стабильные DTO; unknown/unsupported не падают; hash/mtime/read-only assertions подтверждают отсутствие записей.
306
+ **Не делать здесь:** UI, polling, migrations, совместимость «на глаз» с историческими runtime.
307
+
308
+ ### Этап 2. Bootstrap пакета, serve и безопасный shell UI
309
+
310
+ **Цель:** запустить установленную консоль с настоящим браузерным маршрутом, но пока на fixture source.
311
+
312
+ 1. Добавить `package.json`, Node/TS configs, scripts, lint, build output и package files allowlist.
313
+ 2. Добавить CLI parser: `dd-console serve --port --open --flow-home --eval-home --json` и `sources list/add/remove`; нераспознанные флаги завершаются с usage error. --host отсутствует, bind только loopback.
314
+ 3. Реализовать config/load/save source registry атомарной записью в `DD_CONSOLE_HOME`; проверять canonical path только на сервере.
315
+ 4. Реализовать Node HTTP router: static assets, History fallback только для allowlisted UI routes, JSON API errors, `/healthz`, graceful shutdown.
316
+ 5. Реализовать Host/Origin policy, request ID, response envelope, content-security-policy для собственных assets и запрет directory listing.
317
+ 6. Собрать shell: sidebar, header, source badge, breadcrumbs component, empty/loading/error states; добавить fixture overview и прямые URL для каждого базового экрана.
318
+ 7. Реализовать browser state: `popstate`, URL query filters, scroll restoration, focus в h1 после navigation, без полной перезагрузки при внутренних ссылках.
319
+
320
+ **Артефакты:** `src/cli.ts`, `src/server/`, `src/contracts/`, `src/web/`, server/router/browser tests.
321
+ **Готово, когда:** `npm pack` и установка tarball дают `dd-console serve`; `/`, Flow, Eval и несуществующий route ведут себя определённо; Back/Forward и direct link проходят browser test.
322
+ **Не делать здесь:** доступа к production homes и живого обновления.
323
+
324
+ ### Этап 3. Flow reader и вертикальный путь Flow
325
+
326
+ **Цель:** сделать самостоятельный flow полезным для диагностики от списка до причины ожидания.
327
+
328
+ 1. Реализовать Flow source reader с source registry, version gate и read-only SQLite/file adapter.
329
+ 2. Реализовать paged Flow list с filters `source`, `project`, `status`, `harness`, `from/to`, sort `updated|created|attention`.
330
+ 3. Реализовать `FlowDetail`: верхняя сводка, raw/normalized status, stage attempts, controller projection, work graph summary, sessions/checks count, last observation/freshness.
331
+ 4. Реализовать отдельные readers/details для stage attempt, work, session и check. Graph отображать как список зависимостей в первом срезе; визуальную диаграмму добавлять только если она реально ускоряет поиск блокировки.
332
+ 5. Реализовать event pagination и artifact registry. Viewer отображает text только при safe MIME/type/size; binary — метаданные и controlled download.
333
+ 6. Добавить Flow UI: таблица списка, cards для узкого экрана, tabs detail, «линия исполнения», breadcrumbs и links между работой/сессией/check.
334
+ 7. Запустить read-only integration на одном доступном текущем Flow, записать observed results и проверить, что его runtime неизменён.
335
+
336
+ **Готово, когда:** пользователь может открыть RUN, увидеть точный статус, последнюю подтверждённую операцию, работу/проверку-источник блокировки и вернуться назад без потери фильтра. Legacy `current_stage` не требуется для отображения works.
337
+
338
+ ### Этап 4. Eval reader и сквозной путь Eval → Flow
339
+
340
+ **Цель:** связать executions EVAL с их изолированными Flow, не смешивая runtime и не интерпретируя результаты неверно.
341
+
342
+ 1. Реализовать discovery только зарегистрированных EVAL directories и eval list с cursor.
343
+ 2. Реализовать `EvalDetail`: case/checkpoint, definition identity, execution cards, journal state, candidate/Judge/evidence summaries, diagnostics валидности.
344
+ 3. Реализовать `ExecutionDetail`: normalized state, source event sequence, pending/terminal operations, recovery/control state, observed model only where source подтверждает её.
345
+ 4. Реализовать validation `managed-runtime.json` и derived Flow source; contained path + identities проверяются до того, как Flow reader откроет store.
346
+ 5. Добавить links и context token: Eval → execution → derived Flow → work/session; при возвращении context создаёт полный breadcrumb, но не даёт доступа к постороннему runtime.
347
+ 6. Добавить явные UI состояния: Judge отсутствует, candidate не сформирован, provider observation lost, recovery required, nested Flow unavailable.
348
+ 7. Проверить Eval с несколькими executions и fixture с одинаковыми Run ID в разных derived source.
349
+
350
+ **Готово, когда:** пользователь видит, что произошло в Eval, где именно находится каждый execution и какой Flow действительно ему принадлежит. «Нет Judge» не выводится как score 0, а failed observation не выдаётся за completed.
351
+
352
+ ### Этап 5. Живое наблюдение, производительность и устойчивость
353
+
354
+ **Цель:** экран остаётся быстрым и честным во время работающего процесса.
355
+
356
+ 1. Ввести ReaderCache: source/entity key, in-flight deduplication, TTL, LRU и ограниченную очередь workers.
357
+ 2. Читать file/DB snapshots и сохранённую Eval observation-проекцию. Ни списки, ни details не запускают CLI/provider процессы; при отсутствии свежих фактов возвращаются предыдущая snapshot и diagnostics.
358
+ 3. Добавить polling scheduler: selected detail 2 s, overview 5 s, hidden document 30 s, exponential backoff при ошибках, cancel через AbortController при смене route.
359
+ 4. Реализовать JSONL tailer с file identity, offset, partial line, parser diagnostics и reset при rotate/truncate. Добавить cursor pagination истории без загрузки целого файла.
360
+ 5. Сохранить UI state при данных: ключевые списки обновляются diff-ом; вкладки, focus, scroll и opened disclosures сохраняются. Новые events не принудительно скроллят пользователя.
361
+ 6. Добавить telemetry только в локальный log консоли: reader duration, cache hit, bytes, error code. Никакой внешней отправки; экран диагностики может показывать агрегат.
362
+ 7. Запустить performance suite на 100 MB journal и трёх browser clients; зафиксировать memory/latency и откорректировать defaults только по результатам.
363
+
364
+ **Готово, когда:** новые данные появляются в выбранной детали не позднее 5 s в управляемом fixture; одна и та же source не перечитывается отдельно каждой вкладкой; недоступный source не делает интерфейс неотзывчивым.
365
+
366
+ ### Этап 6. Hardening, документация и релиз кандидата
367
+
368
+ **Цель:** проверить границы локального инструмента и возможность установки без checkout.
369
+
370
+ 1. Пройти security tests для traversal, symlink escape, invalid source/context/cursor, Host/Origin и HTML/JS в artifact/log fields.
371
+ 2. Пройти accessibility/browser tests: клавиатурная навигация, skip link, focus order, `aria-current` breadcrumbs, contrast, reduced motion, 320px и широкий экран.
372
+ 3. Пройти compatibility fixtures и read-only integration tests на закреплённых поддерживаемых версиях.
373
+ 4. Проверить graceful shutdown, занятый порт, port 0, stale registry entry, отсутствующий DD_FLOW_HOME/DD_EVAL_HOME, corrupt JSON config и update во время append journal.
374
+ 5. Сформировать usage docs: install, `serve`, sources, URL, где лежит config/log, безопасное завершение, известные ограничения и troubleshooting.
375
+ 6. Выполнить clean install + `pack:check`, полный `check`, browser acceptance и manual smoke на macOS в Chrome/встроенном browser.
376
+ 7. Подготовить release notes, version, changelog и PR review; публикация и тег — отдельное действие после review.
377
+
378
+ **Готово, когда:** package запускается без соседних репозиториев, все критерии раздела 8 проходят, а known limitations документированы без выдачи за ошибки пользователя.
379
+
380
+ ### Разбиение на PR и порядок интеграции
381
+
382
+ | PR | Содержание | Зависит от |
383
+ |---|---|---|
384
+ | 1 | package bootstrap, contracts base, fixture harness | — |
385
+ | 2 | source registry, server shell, routing, safe HTTP policy | PR 1 |
386
+ | 3 | Flow reader + Flow API + contract tests | PR 1, PR 2; producer contract если необходим |
387
+ | 4 | Flow UI/screens/events/artifacts | PR 2, PR 3 |
388
+ | 5 | Eval reader + derived Flow identity | PR 1–3; producer observation contract |
389
+ | 6 | Eval UI, context breadcrumbs | PR 2, PR 4, PR 5 |
390
+ | 7 | polling/cache/tailer/performance | PR 3–6 |
391
+ | 8 | hardening, accessibility, docs, pack/release candidate | PR 1–7 |
392
+
393
+ Изменения public contract у dd-flow-cli или dd-eval получают отдельный PR в проекте-владельце и объединяются до или вместе с зависящим адаптером. dd-console не меняет рабочие деревья раннеров без явной задачи на этот контракт. Работу вести в feature branches и независимых worktrees; перед параллельной работой закреплять владение общими файлами `contracts`, router и source registry.
394
+
395
+ ## 8. Приёмка первого выпуска
396
+
397
+ 1. Самостоятельный Flow: обзор → RUN → работа → сессия → артефакт; возврат breadcrumbs и Back восстанавливает ожидаемый экран, фильтры и прокрутку.
398
+ 2. Eval с несколькими executions: переход в правильный изолированный Flow и обратно; одинаковые короткие RUN ID в разных runtime не смешиваются.
399
+ 3. Refresh/direct link на каждом detail экране сохраняет сущность и проверенный контекст.
400
+ 4. Running/waiting/failed/recovery/completed, повтор стадии, отсутствующий Judge и отсутствующий usage имеют различимые корректные представления.
401
+ 5. Отключённый/удалённый runtime, неподдерживаемая версия и частично записанный журнал не ломают другие источники.
402
+ 6. На замороженной fixture до/после просмотра логическое содержимое БД, run projections и evidence совпадает; read-only вызовы не запускают migrations/children. На живом запуске закрытие UI и сервера не меняет жизненный цикл раннера.
403
+ 7. Большой журнал (тестовая цель: 100 МБ) читается порциями; refresh читает приращение; три вкладки не создают три независимых перечитывания источника. Замерить память/задержки и записать фактические результаты.
404
+ 8. При локально доступном источнике новое событие появляется в выбранном экране в пределах целевых 5 секунд; при задержке чтения отображается freshness. Проверить с управляемым writer fixture и затем живым RUN.
405
+ 9. Traversal, symlink за пределы корня, неподдерживаемый Host/Origin и HTML в логе не дают доступ к лишним файлам и исполнение кода.
406
+ 10. Навигация клавиатурой, focus после переходов, aria-current в breadcrumbs, читаемые статусы и узкое окно проходят браузерную проверку.
407
+ 11. Pack/install smoke подтверждает наличие UI assets и запуск сервера из установленного пакета. Существующие CLI checks, typecheck/lint/build и релевантные тесты проходят.
408
+
409
+ Для browser CI использовать выбранный Playwright; интерактивная проверка дополняет CI. Read fixtures не содержат секретов и скрытых ответов eval. Проверки Markdown не подтверждают работоспособность readers/UI.
410
+
411
+ Дополнительные обязательные сценарии приёмки:
412
+
413
+ 12. Пустой старт, add/remove/re-add, второй serve, повреждённый config и исчезнувший source не теряют registry и не создают runtime каталоги.
414
+ 13. После перезапуска прямые ссылки на derived Flow и artifact работают без посещения родителя; неверный breadcrumb context не блокирует доступную сущность.
415
+ 14. Два проекта в одном source с одинаковыми Run ID различаются. Queued/cancelled/stopped и неизвестные producer статусы не смешиваются с availability.
416
+ 15. Ни один UI request не запускает CLI/provider process. Eval без observation export показывает ограниченную доступность; актуальный поддерживаемый Eval проходит полный producer contract test.
417
+ 16. Во время индексации 100 МиБ и SQLite lock /healthz остаётся отзывчивым (цель p95 <200 мс); очередь/cache ограничены бюджетом. Пагинация frozen списка не даёт дублей при обновлениях.
418
+ 17. Fixture с WAL подтверждает чтение committed данных. На frozen fixture сравниваются логические данные и evidence; на живом RUN отдельно проверяются read-only handles, отсутствие write calls и console child processes: изменение файлов самим раннером не доказывает вмешательство консоли.
419
+
420
+ Первый release candidate квалифицируется на macOS/Node 26, Chromium и WebKit в Playwright. Package name планируется @deksden-com/dd-console, bin dd-console; доступность имени и видимость проверяются до публикации. Приватность GitHub не определяет видимость npm. До этого поставка проверяется tarball install, публикация не является критерием реализации. Непроверенные версии не объявляются поддерживаемыми.
421
+
422
+ ### Матрица проверок
423
+
424
+ | Область | Unit/contract | Интеграция | Browser acceptance |
425
+ |---|---|---|---|
426
+ | Source registry/identity | create/load/collision/stale tests | flow/eval fixture | direct URL и bad context |
427
+ | Flow reader | fixtures каждого статуса | read-only real Flow | list → Run → Work → Session |
428
+ | Eval reader | fixtures execution/Judge/recovery | read-only real Eval | Eval → Execution → derived Flow |
429
+ | HTTP/artifacts | URL/method/path/realpath tests | local server | error and attachment behaviour |
430
+ | Navigation | router and context validation | — | breadcrumbs, Back/Forward, focus, scroll |
431
+ | Live updates | cache/tailer/cursor tests | managed writer fixture | freshness and no forced scroll |
432
+ | Package | build manifest test | tarball install | `serve --open` smoke |
433
+
434
+ Минимум одна проверка в каждой строке обязательна до merge соответствующего PR; полный набор — до release candidate. Результаты real integration не копируют в fixtures, если они могут содержать личные данные или ответы eval.
435
+
436
+ ## 9. Риски и правила принятия решений
437
+
438
+ | Риск | Ранний сигнал | Решение |
439
+ |---|---|---|
440
+ | Проекции раннеров меняются без совместимого schema | reader fixture перестаёт читаться | заблокировать новый version как unsupported; запросить producer contract, не эвристически угадывать поля |
441
+ | Просмотр вызывает запись | меняется hash/mtime/runtime revision | убрать вызов и перейти на read-only adapter; release блокируется |
442
+ | Журнал большой или пишется одновременно | рост задержки/memory, partial JSON | incremental tail + cursor; ограничить initial page; не читать файл целиком |
443
+ | Два runtime дают одинаковые IDs | ссылка открывает другой Run | source + project обязательны во всех routes/API keys; regression fixture |
444
+ | Неатомарные snapshots создают противоречие | timestamp/revision расходятся | `partial` + freshness; не делать вывод о settlement |
445
+ | Артефакт раскрывает файл за пределами source | realpath выходит из allowlist | отказ с diagnostic, тест traversal/symlink; никаких браузерных путей |
446
+ | UI маскирует отсутствие данных | score/usage/status выглядит окончательным | null + причина + source status; UX test на partial/unavailable |
447
+ | Polling перегружает чтение | рост очереди, несколько вкладок | shared cache, TTL, worker cap; CLI/provider вызовы запрещены |
448
+ | Домашний каталог консоли повреждён | registry/config parse error | сохранить файл; на старте завершиться с diagnostic, при reload удержать последнюю валидную конфигурацию |
449
+
450
+ Любое решение, расширяющее scope до удалённого доступа, нескольких пользователей, автоматического запуска/остановки процессов, загрузки transcript целиком или хранения evidence, требует отдельного design review. Оно не может войти в первый выпуск как «малое улучшение».
451
+
452
+ ## 10. После первого выпуска
453
+
454
+ Следующий этап — pause/resume/stop через существующие команды: request ID, expected generation, durable receipt, состояние requested → observed → settled. Scope команды явно показан: весь Eval или отдельный Flow. Не запускать процессы через произвольный shell из API и не считать получение HTTP 200 завершением операции.
455
+
456
+ Далее по фактической потребности: SSE; расширенный просмотр transcripts; запуск новых flow/eval; удалённые источники с отдельной моделью доступа. Эти возможности не блокируют выпуск живой панели наблюдения.
@@ -0,0 +1,33 @@
1
+ # Ревью и исправления — 2026-09-13
2
+
3
+ Все пять незакрытых категорий предыдущего ревью исправлены в dd-console.
4
+ Изменения соседних рабочих checkout не включались в этот коммит.
5
+
6
+ | Недостаток | Исправление | Проверка |
7
+ | --- | --- | --- |
8
+ | Несогласованное копирование SQLite/WAL | Копирование и временный кеш удалены. `readOnly`, `query_only` и короткая транзакция обеспечивают согласованность запросов одной детали | Конкурентный writer меняет RUN/Work одной транзакцией и выполняет checkpoint; 60 чтений не смешивают ревизии. INSERT/UPDATE через reader запрещены |
9
+ | Только последние 256 KiB/200 событий | Полная история читается обратными страницами от новых событий к старым. Cursor содержит scope/generation/offset и подпись; append его сохраняет, rotate/truncate сбрасывает | 550 событий больше 256 KiB без пропусков/дубликатов, append/partial line, rotate, truncate, неверный scope, oversized/invalid строки |
10
+ | Неполные экраны и навигация | Поиск/фильтры/сортировка в URL; отдельный artifact viewer и фрагменты preview; work/session/check/attempt links; Eval и execution в канонических breadcrumbs вложенного Flow | Chromium/WebKit: переходы до артефакта и обратно, фильтры/Back, клавиатура, polling и экран 320 px |
11
+ | TOCTOU файлов и некорректный derived source | JSON, журналы и артефакты открываются через `openat`/`O_NOFOLLOW` с удержанием дескрипторов каталогов. Чтение, stat и download используют открытый файл. Derived source проверяет manifest и ограничен своим RUN | Traversal, symlink наружу, symlink каталога reports, подмена каталога после открытия, повторное открытие после подмены, metadata/preview/download одной политики |
12
+ | Только ручные SQLite/observation fixtures | Добавлены captures схемы и проекций, созданные producers на пустом временном home, с версиями и commits | Настоящая схема Flow, failure/wait/recovered execution проекции Eval; тесты автономны от соседних checkout |
13
+
14
+ Дополнительно исправлены: перезапись повреждённого registry из кеша, валидация registry и observation identity, HTML-инъекция через href, потеря фокуса, обрезка таблиц, коллизии составных ключей кеша, кеш страниц без лимита памяти, некорректные API suffix/limit/Host, Unicode на границе preview и освобождение ресурсов при остановке.
15
+
16
+ Файлы lock используют kernel `flock`: блокировка освобождается при закрытии дескриптора или смерти процесса. Файл lock остаётся на диске для сохранения единственного inode; его наличие не означает работающий сервер. Пустые/старые PID-файлы не мешают новому запуску.
17
+
18
+ `observed_at` сохраняет время фактического чтения; `served_at` — время ответа. При ошибке чтения доступен предыдущий успешный результат с `reader_stale`. Неизвестное время состояния не заменяется текущим временем. Неподтверждённый Eval context игнорируется с видимым пояснением.
19
+
20
+ ## Проверки
21
+
22
+ - `npm run check`: typecheck, 25 Node integration/contract tests, 18 Chromium/WebKit tests — пройдено.
23
+ - `npm run pack:check`: чистая установка tarball в другой каталог, native dependency, UI assets, API, запуск и SIGTERM — пройдено.
24
+ - `git diff --check`.
25
+ - Agent-browser: ручной маршрут списка/детали Eval и axe WCAG 2 A/AA, 0 нарушений на проверенном экране списка.
26
+
27
+ ## Уточнения контракта
28
+
29
+ Read-only SQLite может обновлять служебный SHM read-mark или создавать необходимые sidecars. Это поведение SQLite для согласованного WAL-чтения, разрешённое исходным планом (§4.5, §8): тест сравнивает содержимое основной БД и WAL, а не запрещает штатную работу SHM. Консоль не выполняет миграции или SQL-записи в данные раннера. Байтово неизменяемый архив следует готовить согласованным backup производителя, не копированием живой пары DB/WAL. Основание: [SQLite WAL](https://www.sqlite.org/wal.html) и [Online Backup API](https://sqlite.org/backup.html).
30
+
31
+ Бюджеты явные: JSON — до 8 MiB, строка журнала — до 1 MiB, чтение journal page — до 4 MiB чанками 256 KiB, preview — до 256 KiB за фрагмент. Превышения не выдаются за полную историю: есть диагностика, признак усечения и ссылка продолжения. Все законченные строки допустимого размера доступны через страницы.
32
+
33
+ Поддерживаемые версии закреплены в `test/fixtures/contracts/provenance.json`; автоматическая совместимость со всеми будущими версиями не обещается. Отсутствующие у producer факты и связи остаются unknown/partial, а не вычисляются по догадке. Pause/resume/stop и удалённые источники остаются за рамками read-only выпуска.
@@ -0,0 +1,27 @@
1
+ # Результаты перепроверки плана
2
+
3
+ Первоначально были проверены implementation-plan.md, work-plan.md и выбранные пути текущего кода dd-flow-cli/dd-eval. После review реализованы отдельный dd-console package, read-only сервер/UI, worker pool, cursor snapshots, producer-owned Eval observation и contract/browser tests. Оставшиеся пункты отмечены в work-plan.md и не считаются закрытыми только по наличию документации.
4
+
5
+ ## Исправленные противоречия
6
+
7
+ - Запрет запускать процессы противоречил live probes в details. Теперь все HTTP readers читают только сохранённые данные, одинаково для списков и деталей.
8
+ - Eval runnerStatus использует reduceEvents и runnerControlStatus. Чтение manifest/report не заменяет это состояние. В плане обязательный producer-owned observation export, а исторические запуски без него имеют явно ограниченную детализацию. Бизнес-reducer в консоли не дублируется.
9
+ - Статусы выполнения были смешаны с availability. Разделены execution_status, availability и control; добавлены queued/cancelled/stopped и правила unknown.
10
+ - Ошибка context раньше блокировала detail через 409, хотя breadcrumbs обещали fallback. Теперь canonical breadcrumb + diagnostic; 409 оставлен для stale cursor.
11
+ - Unsupported source ранее одновременно требовал 422 и видимой сводки. Теперь известные sources возвращают typed availability в 200, ошибочные запросы — отдельные HTTP errors.
12
+ - Node >=22 не соответствовал заявленной доступности node:sqlite. Первый выпуск ограничен Node 26, как текущий dd-flow-cli; отсутствующая поддержка старых Node не подразумевается.
13
+ - tsc не копирует HTML/CSS. Добавлен явный asset build, separate server/web tsconfig и install smoke вне cwd репозитория.
14
+ - Registry corruption больше не ведёт к запуску с пустой конфигурацией и риску её перезаписи.
15
+ - Зависимости PR Flow API/Eval derived reader дополнены зависимостями от server/Flow reader.
16
+
17
+ ## Добавленные сценарии
18
+
19
+ Первое подключение и отключение источника; пустой старт; повторная регистрация; один сервер на console home; стабильные deep links и derived identities после restart; source/check screens; metadata/preview/download; обновление assets; пагинация меняющегося списка; cold indexing; bounded workers/cache/queue; WAL/SHM; занятый store; лимит строки журнала; дедупликация явно подключённого и вложенного runtime; различие event age и времени последнего успешного чтения.
20
+
21
+ Артефакты доступны только по публичным зарегистрированным ссылкам, а не по всему содержимому home. Preview и download имеют одну политику. Приёмка расширена проверками restart/config/WAL/no-process/pagination/load и macOS/Node 26/Chromium/WebKit.
22
+
23
+ ## Граница готовности
24
+
25
+ План готов как последовательность реализации. Это не подтверждение совместимости существующих runtime. Этап 1 обязан произвести compatibility matrix, доказательство read-only чтения и контракт observation export Eval. Если export отсутствует, требуется изменение у владельца dd-eval; этот шаг теперь виден в зависимостях, а не скрыт в адаптере UI.
26
+
27
+ Источники проверки: dd-flow-cli/src/services/runs.ts (getFlowRunStatus, authoritativeIndex), src/storage/database.ts (getDatabase/read_existing), dd-eval/lib/runner.mjs (runnerStatus/runnerControlStatus), текущий dd-flow-cli/package.json. Доступность SQLite API сверена с [документацией Node](https://nodejs.org/api/sqlite.html).