@goodandready/dsh-kanban 0.2.2 → 0.2.7

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.
package/lib/store.js CHANGED
@@ -70,6 +70,8 @@ CREATE TABLE IF NOT EXISTS tasks (
70
70
  comments TEXT NOT NULL DEFAULT '[]',
71
71
  rejectReason TEXT NOT NULL DEFAULT '',
72
72
  rejectedAt INTEGER NOT NULL DEFAULT 0,
73
+ parentId TEXT NOT NULL DEFAULT '',
74
+ isEpic INTEGER NOT NULL DEFAULT 0,
73
75
  updatedAt INTEGER NOT NULL
74
76
  );
75
77
  CREATE INDEX IF NOT EXISTS tasks_board_col ON tasks(board, col, position);
@@ -98,7 +100,8 @@ const WRITABLE = [
98
100
  'title', 'body', 'owner', 'repo', 'issueNumber', 'issueUrl', 'labels',
99
101
  'branch', 'worktree', 'workspaceId', 'sessionId', 'model', 'provider', 'waiting', 'syncedAt', 'plan',
100
102
  'labelColors', 'queuedAt', 'createdAt', 'author', 'assignee', 'milestone', 'priority', 'dueAt',
101
- 'cron', 'nextRunAt', 'lastRunAt', 'progressDump', 'permission', 'reasoningEffort', 'permissionConfirmedAt', 'runs', 'checklist', 'report', 'comments', 'rejectReason', 'rejectedAt',
103
+ 'cron', 'nextRunAt', 'lastRunAt', 'progressDump', 'permission', 'reasoningEffort', 'permissionConfirmedAt',
104
+ 'runs', 'checklist', 'report', 'comments', 'rejectReason', 'rejectedAt', 'parentId', 'isEpic',
102
105
  ]
103
106
 
104
107
  /** Источники перехода; чужое значение не пишем, чтобы журнал оставался читаемым. */
@@ -106,7 +109,7 @@ const SOURCES = new Set(['manual', 'session', 'gitea', 'tool'])
106
109
 
107
110
  function rowToTask(row) {
108
111
  if (row === undefined) return undefined
109
- const { col, labels, waiting, labelColors, runs, checklist, report, comments, rejectReason, rejectedAt, ...rest } = row
112
+ const { col, labels, waiting, labelColors, runs, checklist, report, comments, rejectReason, rejectedAt, isEpic, parentId, ...rest } = row
110
113
  let parsedChecklist = []
111
114
  try {
112
115
  const value = JSON.parse(checklist ?? '[]')
@@ -150,6 +153,8 @@ function rowToTask(row) {
150
153
  rejectReason: rejectReason ?? '',
151
154
  rejectedAt: Number(rejectedAt) || 0,
152
155
  reasoningEffort: rest.reasoningEffort ?? '',
156
+ isEpic: isEpic === 1,
157
+ parentId: parentId ?? '',
153
158
  }
154
159
  }
155
160
 
@@ -194,6 +199,8 @@ export function openStore({ dir }) {
194
199
  ['comments', "TEXT NOT NULL DEFAULT '[]'"],
195
200
  ['rejectReason', "TEXT NOT NULL DEFAULT ''"],
196
201
  ['rejectedAt', "INTEGER NOT NULL DEFAULT 0"],
202
+ ['parentId', "TEXT NOT NULL DEFAULT ''"],
203
+ ['isEpic', "INTEGER NOT NULL DEFAULT 0"],
197
204
  ]) {
198
205
  try { db.exec(`ALTER TABLE tasks ADD COLUMN ${column} ${definition}`) } catch { /* столбец уже есть */ }
199
206
  }
@@ -202,6 +209,15 @@ export function openStore({ dir }) {
202
209
  // заведения: это честнее нуля, который читался бы как «в 1970 году».
203
210
  db.exec('UPDATE tasks SET columnAt = createdAt WHERE columnAt = 0')
204
211
 
212
+ // Индексы для ускорения выборок активных задач, поиска по Gitea issue и cron-задач
213
+ db.exec('CREATE INDEX IF NOT EXISTS tasks_board_active ON tasks(board, archivedAt, col, position)')
214
+ db.exec('CREATE INDEX IF NOT EXISTS tasks_gitea_issue ON tasks(owner, repo, issueNumber)')
215
+ db.exec('CREATE INDEX IF NOT EXISTS tasks_cron_next ON tasks(archivedAt, nextRunAt)')
216
+ db.exec('CREATE INDEX IF NOT EXISTS tasks_queued ON tasks(archivedAt, queuedAt)')
217
+ db.exec('CREATE INDEX IF NOT EXISTS tasks_parent_id ON tasks(parentId)')
218
+ db.exec('PRAGMA synchronous = NORMAL')
219
+ try { db.exec('PRAGMA optimize') } catch {}
220
+
205
221
  const q = {
206
222
  boards: db.prepare('SELECT * FROM boards ORDER BY position'),
207
223
  boardById: db.prepare('SELECT * FROM boards WHERE id = ?'),
@@ -227,6 +243,9 @@ export function openStore({ dir }) {
227
243
  "SELECT * FROM tasks WHERE issueNumber IS NOT NULL AND col NOT IN ('done') ORDER BY updatedAt DESC"),
228
244
  scheduledDue: db.prepare(
229
245
  "SELECT * FROM tasks WHERE cron != '' AND nextRunAt > 0 AND nextRunAt <= ? AND archivedAt = 0 ORDER BY nextRunAt ASC"),
246
+ subtasksOf: db.prepare('SELECT * FROM tasks WHERE parentId = ? AND archivedAt = 0 ORDER BY createdAt ASC, position ASC'),
247
+ countSubtasks: db.prepare(
248
+ "SELECT COUNT(*) AS total, SUM(CASE WHEN col = 'done' THEN 1 ELSE 0 END) AS completed FROM tasks WHERE parentId = ? AND archivedAt = 0"),
230
249
  }
231
250
 
232
251
  function ensureBoard(id, title, kind) {
@@ -358,8 +377,8 @@ export function openStore({ dir }) {
358
377
  labels, branch, worktree, workspaceId, sessionId, model, provider, waiting, syncedAt,
359
378
  labelColors, author, assignee, milestone, priority, dueAt, columnAt, createdAt, updatedAt,
360
379
  cron, nextRunAt, lastRunAt, progressDump, permission, permissionConfirmedAt, runs,
361
- checklist, report, comments, rejectReason, rejectedAt, reasoningEffort)
362
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(
380
+ checklist, report, comments, rejectReason, rejectedAt, reasoningEffort, parentId, isEpic)
381
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(
363
382
  id, board, column, position,
364
383
  input.title ?? '', input.body ?? '',
365
384
  input.owner ?? null, input.repo ?? null,
@@ -389,6 +408,8 @@ export function openStore({ dir }) {
389
408
  input.rejectReason ?? '',
390
409
  Number(input.rejectedAt) || 0,
391
410
  typeof input.reasoningEffort === 'string' ? input.reasoningEffort : '',
411
+ input.parentId ?? '',
412
+ input.isEpic ? 1 : 0,
392
413
  )
393
414
  return rowToTask(q.taskById.get(id))
394
415
  },
@@ -411,6 +432,8 @@ export function openStore({ dir }) {
411
432
  else if (key === 'rejectReason') values.push(String(patch[key] ?? ''))
412
433
  else if (key === 'waiting') values.push(patch[key] ? 1 : 0)
413
434
  else if (key === 'syncedAt') values.push(Number(patch[key]) || 0)
435
+ else if (key === 'isEpic') values.push(patch[key] ? 1 : 0)
436
+ else if (key === 'parentId') values.push(String(patch[key] ?? ''))
414
437
  else values.push(patch[key] ?? null)
415
438
  }
416
439
  if (fields.length > 0) {
@@ -546,6 +569,21 @@ export function openStore({ dir }) {
546
569
  return rowToTask(q.byIssue.get(owner, repo, issueNumber))
547
570
  },
548
571
 
572
+ listSubtasks(parentId) {
573
+ if (!parentId) return []
574
+ return q.subtasksOf.all(parentId).map(rowToTask)
575
+ },
576
+
577
+ countSubtasks(parentId) {
578
+ if (!parentId) return { total: 0, completed: 0 }
579
+ const res = q.countSubtasks.get(parentId)
580
+ return { total: res?.total ?? 0, completed: res?.completed ?? 0 }
581
+ },
582
+
583
+ setParent(id, parentId) {
584
+ return this.updateTask(id, { parentId: parentId ?? '' })
585
+ },
586
+
549
587
  /** Задачи в архиве: свой список, а не колонка. */
550
588
  listArchived() {
551
589
  return q.archived.all().map(rowToTask)
package/lib/sync.js CHANGED
@@ -9,6 +9,29 @@ import { watchedRepos, shouldTake, archiveBefore, isWatched } from './intake.js'
9
9
  import { colorsOfIssue } from './filters.js'
10
10
  import { issueToTask, issueBornAt, issueAuthor, issueAssignee, issueMilestone } from './import.js'
11
11
 
12
+ /**
13
+ * Выполнить операцию с экспоненциальной задержкой (exponential backoff) при сбоях сети.
14
+ * @template T
15
+ * @param {() => Promise<T>} fn асинхронная операция
16
+ * @param {{retries?: number, baseMs?: number, maxMs?: number}} [opts]
17
+ * @returns {Promise<T>}
18
+ */
19
+ export async function withBackoff(fn, { retries = 2, baseMs = 50, maxMs = 500 } = {}) {
20
+ let attempt = 0
21
+ let firstErr
22
+ while (true) {
23
+ try {
24
+ return await fn()
25
+ } catch (err) {
26
+ if (!firstErr) firstErr = err
27
+ if (attempt >= retries) throw (firstErr || err)
28
+ const delay = Math.min(baseMs * (2 ** attempt), maxMs)
29
+ attempt += 1
30
+ await new Promise((resolve) => setTimeout(resolve, delay))
31
+ }
32
+ }
33
+ }
34
+
12
35
  /**
13
36
  * Наблюдения по одному репозиторию.
14
37
  *
@@ -202,7 +225,7 @@ export async function syncAll({ gitea, store, logger, only, now = Date.now() })
202
225
  for (const group of byRepo.values()) {
203
226
  let repoState
204
227
  try {
205
- repoState = await readRepo({ gitea, owner: group.owner, repo: group.repo })
228
+ repoState = await withBackoff(() => readRepo({ gitea, owner: group.owner, repo: group.repo }), { retries: 2, baseMs: 40 })
206
229
  } catch (error) {
207
230
  failed += group.tasks.length
208
231
  reposFailed += 1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-kanban",
3
- "version": "0.2.2",
3
+ "version": "0.2.7",
4
4
  "description": "Visual Kanban board for DeepSeek Harness: Gitea-backed tasks, workflow columns, and a dedicated agent session per task.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -14,7 +14,8 @@
14
14
  "lib/",
15
15
  "cordis.patch.yml",
16
16
  "README.md",
17
- "docs/",
17
+ "README.zh.md",
18
+ "README.ru.md",
18
19
  "LICENSE"
19
20
  ],
20
21
  "scripts": {
@@ -1,94 +0,0 @@
1
- # DESIGN.md — dsh-kanban
2
-
3
- ## Product / Purpose
4
- - Назначение: Интегрированная доска задач для DeepSeek Harness с двусторонней сверкой с Gitea, автоматическим подхватом сессий агентов и управлением жизненным циклом.
5
- - Аудитория: Разработчики и операторы DeepSeek Harness, ведущие проектные задачи и сессии агентов.
6
- - Статус: Production, версия 0.1.23+.
7
-
8
- ## User Surfaces
9
- - Web/UI: Экран доски в центральной колонке интерфейса DSH (mountBoard), выдвигающаяся панель задач, фильтры, группировки (по проектам, по исполнителю), метрики переходов.
10
- - DSH UI / settings / slots:
11
- - Карточка настроек в слоте settings.plugin.item (пространство dsh-kanban).
12
- - Чип активной задачи в conversation.session.header.utilities.
13
- - Пункт бокового меню для вызова экрана доски.
14
- - API: HTTP эндпоинты /dsh-kanban/* (board, snapshot, task, sync, webhook, metrics, whoami, checklist, report, comments, accept, reject).
15
- - CLI: node tools/compat.mjs (проверка совместимости экспортов ядра).
16
- - Документация: README.md, docs/README.ru.md, docs/README.zh.md, index.md.
17
-
18
- ## Visual Direction
19
- - Атмосфера: Строгий утилитарный инструментальный интерфейс в едином стиле темы DeepSeek Harness.
20
- - Утверждённые референсы и что из них берём: Системные карточки DSH (стили заголовков, отступы, шрифты, цветовые токены --dsw-alias-*).
21
- - Не копировать: Внешние неинтегрированные темы, разнородные цвета рамок и теней.
22
-
23
- ## Foundations
24
- - Цвета и роли: Использование CSS-переменных DSH темы (--dsw-alias-bg-layer-3, --dsw-alias-border-l2, --dsw-alias-label-primary, --dsw-alias-label-secondary).
25
- - Типографика: Системный стек шрифтов DSH (inherit, 13-14px базовый размер, 11-12px для бейджей и служебных меток).
26
- - Сетка, отступы, responsive: 5 стандартных колонок (backlog, ready, progress, review, done) с горизонтальным скроллом и адаптивными карточками.
27
- - Accessibility: Нативные кнопки button, role="status" для сообщений, семантичные поля ввода с aria-label/label.
28
-
29
- ## Components And States
30
- - Компоненты: BoardScreen, Column, Card, TaskDrawer (включая секции DoD Checklist, Execution Report, Comments Thread, Review Acceptance Gate), KanbanSettingsCard, TaskChip, MetricsModal.
31
- - Loading / empty / error / success:
32
- - Загрузка: состояние скелетона/лоадера без блокировки скролла.
33
- - Пустые колонки: пунктирная область перетаскивания.
34
- - Ошибки: всплывающий баннер с ключом локализации t('error.' + err).
35
- - Формы, валидация и действия: Валидация обязательных полей при создании задачи, блокировка повторных кликов при сохранении.
36
-
37
- ## User Flows
38
- - Критические сценарии:
39
- 1. Создание задачи / подхват из Gitea Issue.
40
- 2. Перемещение карточки между колонками человеком или через инструмент агента board_move.
41
- 3. Запуск сессии агента по карточке с автоматическим назначением ответственного.
42
- 4. Автоматическое обновление статуса (waiting) по событиям ядра (session/event).
43
- 5. Экспорт и импорт снимка доски в JSON.
44
-
45
- ## Do / Don't
46
- - Do: Использовать только токены темы --dsw-alias-*, очищать все слушатели событий при размонтировании, соблюдать порядок хуков React.
47
- - Don't: Не переводить карточку в done агентом (колонка done фиксируется только по факту закрытия issue и ветки).
48
-
49
- ## Locked Design Decisions
50
- - 2026-08-20 — Карточка настроек монтируется в settings.plugin.item (резерв в settings.section исключён по #238).
51
- - 2026-08-25 — Использование дробной индексации ($O(1)$) для позиционирования карточек.
52
- - 2026-09-02 — Снимок доски объединён на маршруте /dsh-kanban/snapshot (GET: выгрузка, POST: загрузка).
53
- - 2026-09-03 — Изоляция сессий задач в git worktree ($DSH_HOME/worktrees/<repo-key>/<task-id>/ на ветке task/<id>-<slug>), регистрация воркдерева в workspaceRegistry, проверка dirtyFiles и безопасная очистка в фазе cleanup (#193).
54
- - 2026-09-03 — Серверная инфраструктура автономности и безопасности (#195): Host-Side 5-field Cron, PowerInhibitor (кроссплатформенная блокировка сна системы во время работы задач), PROGRESSDUMP (фиксация срезов контекста, маскирование секретов и передача эстафеты), Permission Confirmation Gate (ручное одобрение прав выше базовых), инъекция секции доски в systemPrompt и хранение истории запусков прямо в карточке задачи (tasks.runs).
55
-
56
- - 2026-09-07 — Контур качества и приёмка человеком (#205–#209):
57
- - Протокольный шлюз: колонка done является исключительной границей приёмки человеком; инструменту board_move запрещён прямой перевод задачи в done.
58
- - DoD Acceptance Checklist: перевод в review блокируется до закрытия обязательных пунктов чеклиста с фиксацией доказательств (evidence).
59
- - Structured Report: фиксация отчёта выполнения (summary, changedFiles, checksRun, artifacts, risks) в карточке задачи.
60
- - Двусторонний цикл приёмки: явные действия человека Accept (в done) и Reject (возврат в in-progress с обязательной причиной rejectReason, инжектируемой в следующую сессию агента).
61
- - Ветка комментариев к задаче с поддержкой ролей user и agent для сквозного диалога прямо в Task Drawer.
62
-
63
- - 2026-09-08 — Стабильность сессий и воркдеревьев (#227, #226, #213, #215):
64
- - Привязка проекта сессии: при создании агента с воркдеревом поле meta.cwd сохраняет канонический projectRoot (repoDir), предотвращая выпадение сессий в категорию «Ungrouped» в сайдбаре DSH; путь воркдерева передается в meta.worktreePath и инструкциях агента (#227).
65
- - Режим «↻ Продолжить» (Resume Execution): эндпоинт POST /dsh-kanban/tasks/:id/resume и кнопка в TaskDrawer; сохраняет существующее воркдерево, ветку и незакоммиченные файлы, передавая агенту преамбулу продолжения и актуальный PROGRESSDUMP (#226).
66
- - Фильтрация structural noise в проверках dirtyFiles: в checkWorktreeDirty добавлен флаг --ignore-submodules=all, исключающий ложные блокировки удаления воркдеревьев из-за субмодулей и дрейфа gitlinks (#213).
67
- - Фильтрация сессий субагентов: сессии с маркером subagent (origin === 'subagent' или parentId !== undefined) исключаются из liveSessions и не перезаписывают состояние корневых задач (#215).
68
- ## Locked Decisions
69
- - **0.2.2 (Issue #238)**: Удалён устаревший фоллбэк на `settings.section` в пользу строгого размещения карточки в `settings.plugin.item`; все обращения к службам Cordis (в частности к `llm`) приведены к каноническому `ctx.get('...')`.
70
- - **0.2.1**: Карточка настроек плагина перенесена строго в слот `settings.plugin.item` (раздел «Настройки → Плагины → Настройки плагинов») без одновременной дублирующей регистрации в `settings.section` (Issue #236).
71
-
72
- ### 2026-09-08: Релиз v0.2.0 — Milestone (SSE, Chat Sync, Templates, Two-Column Drawer, Multi-Repo) (#210, #219, #220, #222, #225)
73
- - **Live SSE Channel (`/dsh-kanban/events`)**: Постоянное SSE-соединение с автоматическим переподключением для мгновенного пуша обновлений доски (`task:change`, `board:change`) без избыточного поллинга (#222).
74
- - **Auto-sync Sessions (`autoSyncSessions`)**: Опциональная автоматическая синхронизация стандартных диалогов чата DSH в карточки доски канбана с надежной фильтрацией внутренних сессий и субагентов (#225).
75
- - **Task Templates Library (`/dsh-kanban/templates`)**: Библиотека из 5 встроенных типовых шаблонов (Feature, Bugfix, Refactor, Security, Release) с предзаполненными чеклистами DoD и шаблонами промптов (#220).
76
- - **Two-Column Drawer Layout**: Адаптивный двухколоночный макет для TaskDrawer на экранах шириной от 960px с разделением метаданных и контентной области (#219).
77
- - **Multi-Repo Workspace Mirroring**: Сканирование нескольких вложенных git-репозиториев (`findWorkspaceGitRepos`) и подготовка зеркальных воркдеревьев на единой ветке задачи (`prepareMultiRepoMirror`) (#210).
78
-
79
- ### 2026-09-08: Релиз Vector 4 (UX оператора, управление моделями и аналитика) (#217, #218, #221, #223, #228)
80
- - **reasoningEffort**: Добавлен селектор глубины рассуждения (low, medium, high) в карточке `TaskDrawer`, сохраняется в SQLite и передается в метаданные сессии DSH для моделей с рассуждением (DeepSeek-R1, OpenAI o1/o3).
81
- - **Экспорт в CSV с UTF-8 BOM**: Добавлен эндпоинт `GET /dsh-kanban/export.csv` и кнопка в тулбаре для выгрузки аналитики задач в Microsoft Excel без искажения кодировки.
82
- - **Адаптивный 36x36 Rail Icon**: Кнопка канбана в сайдбаре автоматически принимает компактный размер 36×36 px с центрированием иконки при сворачивании панели в rail.
83
- - **Lightbox для изображений**: Превью Markdown-изображений в карточке открываются в модальном полноэкранном лайтбоксе.
84
-
85
-
86
- ### 2026-09-08: Релиз Vector 3 (Просмотр Git Diff / Коммитов и Однокликовый Merge) (#211, #212)
87
- - **Git Diff & Commits Viewer**: В `TaskDrawer` интегрирован блок просмотра истории коммитов сессии и unified diff измененных файлов воркдерева (`GET /tasks/:id/diff` и `GET /tasks/:id/commits`). Diff безопасно ограничен 250 КБ.
88
- - **One-Click Merge (--no-ff)**: В секции Review Acceptance Gate добавлена кнопка «✓ Принять и смержить» (`POST /tasks/:id/merge`). Выполняется проверка на отсутствие незакоммиченных изменений (`checkWorktreeDirty`), слияние `git merge --no-ff` в целевую ветку (`main`), автоматическая дерегистрация и удаление воркдерева с переводом задачи в `done`.
89
-
90
-
91
- ### 2026-09-08: Релиз Vector 2 (Управление параллелизмом и безопасность) (#214, #216, #224)
92
- - **maxConcurrentSessions**: Лимит параллельных сессий агентов (по умолчанию 3) предотвращает исчерпание памяти/CPU на хосте. Задачи сверх лимита переводятся в очередь (`queuedAt > 0`) и автоматически запускаются диспетчером при завершении активных сессий.
93
- - **workspaceClaimBoundaries**: Инструмент `board_move` проверяет принадлежность задачи к воркспейсу текущей сессии агента, предотвращая случайный межпроектный захват карточек.
94
- - **Страховочный бэкап при импорте снимка**: `importBoard` в `/dsh-kanban/snapshot` автоматически делает снимок базы SQLite в `backups/kanban-backup-<timestamp>.db` с ротацией последних 10 копий перед перезаписью данных.
@@ -1,169 +0,0 @@
1
- # dsh-kanban — план реализации
2
-
3
- > **Для исполнителя:** это план для агента, у которого нет контекста обсуждения.
4
- > Перед любой правкой плагина DSH загрузи `dsh-plugin-authoring`.
5
- > Перед правкой профиля `web` — `dsh-web-profile-repair`.
6
- > Работа ведётся по `gitea-project-workflow`: issue → worktree от свежего
7
- > `origin/main` → WIP PR → ревью → merge → deploy → cleanup → закрытие issue.
8
-
9
- **Цель:** доска задач внутри DeepSeek Harness, которая берёт задачи из Gitea,
10
- показывает по ним ход работы и умеет запускать по каждой задаче отдельную
11
- сессию агента с выбранной моделью.
12
-
13
- **Архитектура:** отдельный плагин `@goodandready/dsh-kanban` со своим
14
- хранилищем задач. Gitea-доступ не дублируется — используется существующий
15
- плагин `@goodandready/dsh-gitea`, которому для этого добавляется сервис cordis.
16
- Запуск работы идёт через штатные сервисы ядра `agents`/`sessions`; плагин
17
- поднимает сессию и отдаёт первое сообщение, дальше работу ведёт агент по
18
- действующему воркфлоу.
19
-
20
- **Стек:** Node (ESM), cordis, `@deepseek-ai/schemastery`, React в браузерной
21
- половине через `window.__ModuleLoader__`, хранилище — `node:sqlite`.
22
-
23
- ## Глобальные ограничения
24
-
25
- - Имя пакета `@goodandready/dsh-kanban` обязано совпадать в трёх местах:
26
- `package.json` → `name`, `cordis.patch.yml` → `name:`, `lib/client.js` →
27
- `load({ id })`. Короткий id патча — `dsh-kanban`.
28
- - Пространство настроек: `dsh-kanban`. Ключ слота карточки настроек обязан быть
29
- равен пространству настроек, иначе карточка молча не появится.
30
- - Настройки — карточкой в `settings.plugin.item`. Свой раздел настроек НЕ
31
- заводить. Раздел верхнего уровня заводится только для экрана доски.
32
- - Токен Gitea плагин не хранит, не спрашивает и не логирует. Доступ — только
33
- через сервис `gitea` из `dsh-gitea`.
34
- - Ни абсолютных путей, ни адресов инстансов, ни имён машин в коде и README.
35
- Машинно-зависимое живёт в настройках с пустым значением по умолчанию.
36
- - Все Git-операции — только через wrapper `git-<agent>`. Bare `git` запрещён.
37
- - Conventional Commits обязательны: `<type>(<scope>)!: <действие>`, footer
38
- `Refs: #<номер>`.
39
- - Тесты идут без харнесса и без сети: `node --test test/*.test.mjs`.
40
- - Всё поднятое в `ctx.effect` гасится возвращённым уборщиком.
41
- - Хуки React объявляются выше всех возвратов.
42
-
43
- ## Колонки доски
44
-
45
- Колонки повторяют реальный воркфлоу, а не абстрактный «в работе → готово».
46
-
47
- | Колонка | Признак входа |
48
- |---|---|
49
- | `backlog` | issue есть, никто не взял |
50
- | `in-progress` | сессия поднята, в issue есть стартовый комментарий с планом и веткой |
51
- | `review` | PR снят с WIP, ревьюер даёт PASS/FAIL |
52
- | `deploy` | PR влит в `main`, ждём согласованный deploy и production-проверку |
53
- | `cleanup` | ветка в Gitea, worktree и локальная ветка ещё не удалены |
54
- | `done` | issue закрыт И cleanup записан в итоговом комментарии |
55
-
56
- Колонка `cleanup` существует потому, что по воркфлоу задача не завершена, пока
57
- cleanup не сделан и не записан. Доска, отправляющая карточку в `done` по merge,
58
- будет врать ровно там, где копятся забытые ветки.
59
-
60
- WIP PR НЕ двигает карточку в `review`: он открывается рано и означает «работа
61
- идёт», а не «смотрите».
62
-
63
- ## Что плагин НЕ делает
64
-
65
- Эти границы заданы воркфлоу и нарушать их нельзя:
66
-
67
- - Не создаёт ветки и worktree. По воркфлоу агент сначала делает preflight
68
- (открытые ветки, `git status --short`) и только потом заводит worktree от
69
- свежего `origin/main`. Плагин, создающий ветку заранее, ломает preflight.
70
- - Не пишет стартовый комментарий в issue — это делает агент после preflight.
71
- - Ветка и worktree на карточке — НАБЛЮДЕНИЕ, вычитанное из issue и событий
72
- Gitea, а не намерение, заданное плагином.
73
- - Не трогает Hermes Kanban. Это другая система, и она этому CLI запрещена.
74
-
75
- ## Выпуски
76
-
77
- **0.1 — доска, импорт, запуск.** Задачи хранятся локально, импортируются из
78
- Gitea по кнопке, карточки двигаются вручную, по кнопке «Начать» поднимается
79
- сессия с выбранной моделью. Синхронизация односторонняя: Gitea → канбан, в
80
- момент импорта и по кнопке обновления.
81
-
82
- **0.2 — автоперемещение.** События сессии агента, события Gitea, журнал
83
- переходов с источником. Инструмент `board_move` для агента пишется, но по
84
- умолчанию выключен — включается вместе с правкой скилла
85
- `gitea-project-workflow`.
86
-
87
- **0.3 — двусторонняя синхронизация.** Вебхуки Gitea, разрешение конфликтов по
88
- времени изменения, очередь повторов, деградация до опроса.
89
-
90
- ## Состав 0.1
91
-
92
- | Issue | Репозиторий | Тема |
93
- |---|---|---|
94
- | G1 | dsh-gitea | Выставить сервис `gitea` наружу |
95
- | G2 | dsh-gitea | Техдолг: `gitea_worktree_add` зовёт bare git |
96
- | K1 | dsh-kanban | Каркас плагина и карточка настроек |
97
- | K2 | dsh-kanban | Хранилище задач |
98
- | K3 | dsh-kanban | HTTP-маршруты доски |
99
- | K4 | dsh-kanban | Импорт задачи из Gitea |
100
- | K5 | dsh-kanban | Экран доски |
101
- | K6 | dsh-kanban | Запуск работы по задаче |
102
- | K7 | dsh-kanban | Чип задачи в шапке чата |
103
- | K8 | dsh-kanban | Документация и релизный гейт 0.1 |
104
-
105
- Порядок: G1 → K1 → K2 → K3 → (K4, K5 параллельно) → K6 → K7 → K8.
106
- G2 независим и может идти когда угодно.
107
-
108
- ## Структура файлов
109
-
110
- ```
111
- lib/index.js точка входа хоста: Config, apply, регистрация маршрутов
112
- lib/store.js хранилище задач: схема, CRUD, дробный индекс
113
- lib/order.js дробный индекс (чистая функция, без БД)
114
- lib/import.js перенос issue Gitea в задачу доски
115
- lib/launcher.js запуск сессии по задаче
116
- lib/routes.js обработчики HTTP
117
- lib/client.js браузерная половина: раздел доски, карточка настроек, чип
118
- test/*.test.mjs тесты без харнесса и без сети
119
- ```
120
-
121
- Разделение по ответственности, а не по слою. `order.js` отделён от `store.js`
122
- потому, что дробный индекс — единственная нетривиальная логика в хранилище и
123
- проверяется без БД вообще.
124
-
125
- ## Проверенные интерфейсы ядра
126
-
127
- Ниже — реальные сигнатуры, снятые с исходников, а не пересказ. Исполнитель
128
- может опираться на них как есть.
129
-
130
- Создание сессии с выбранной моделью и первое сообщение:
131
-
132
- ```js
133
- import { SessionId } from '@deepseek-ai/dsh-session'
134
- import { createUserMessage } from '@deepseek-ai/dsh-llm'
135
-
136
- const handle = await ctx.agents.create({
137
- sessionId: SessionId(`kanban-${taskId}-${randomUUID()}`),
138
- meta: { cwd }, // ОБЯЗАТЕЛЬНО абсолютный путь
139
- agentOptions: { provider, model }, // maxTokens необязателен
140
- })
141
- await handle.agent.whenIdle()
142
- handle.agent.followup(createUserMessage({
143
- content,
144
- source: { kind: 'plugin', plugin: 'dsh-kanban', form: 'task-start' },
145
- }))
146
- ```
147
-
148
- - `ctx.agents.create(options)` возвращает `AgentHandle` с полями `agent` и
149
- `dispose`. Живой образец — `dsh-messenger-gateway/lib/gateway.js:465`.
150
- - `meta.cwd` валидируется ядром: неабсолютный путь бросает исключение.
151
- - `agent.followup(message)` ставит обычный следующий ход и будит драйвер.
152
- - `agent.session.id` — идентификатор сессии, по нему карточка связывается с
153
- чатом.
154
- - Модель по умолчанию берётся из сервиса `agentDefaultModel`; образец
155
- разрешения провайдера и модели — `gateway.js:455`.
156
-
157
- События сессии, нужные в 0.2: `assistant/message`, `turn/end`,
158
- `approval/asked` (несёт `id`, имя инструмента, причину), `question/requested`.
159
-
160
- ## Открытый технический риск
161
-
162
- `node:sqlite` — встроенный модуль Node, но он появился не во всех версиях.
163
- ПЕРВЫЙ шаг задачи K2 — проверить, что модуль есть в той версии Node, на которой
164
- крутится харнесс. Если модуля нет, хранилище делается на одном JSON-файле с
165
- атомарной записью (запись во временный файл рядом, затем `rename`), а схема
166
- таблиц из K2 переносится в форму объекта один-в-один. Дробный индекс и весь
167
- остальной план от выбора хранилища не зависят.
168
-
169
- Решение принимается фактом проверки, а не догадкой, и записывается в issue K2.
@@ -1,25 +0,0 @@
1
- # План реализации: Human-in-the-Loop Quality & Acceptance Gateway (#205–#209)
2
-
3
- Пакетная реализация сквозного контура контроля качества и человеческой приёмки для `@goodandready/dsh-kanban`:
4
- - **#205**: DoD Acceptance Checklist с фиксацией доказательств (`board_checklist`)
5
- - **#206**: Структурированный отчёт выполнения задачи (`board_report`)
6
- - **#207**: Двусторонний цикл приёмки человеком (Accept / Send back with reason)
7
- - **#208**: Ветка комментариев к задаче и синхронизация с агентом (`board_comments`, `board_comment_add`)
8
- - **#209**: Протокольный шлюз запрета перевода задачи в Done агентом
9
-
10
- ## 1. Архитектурные требования
11
- 1. **Протокольный шлюз Done (#209)**: агент через инструмент `board_move` не может перевести задачу в `done`. Статус `done` может быть установлен только человеком.
12
- 2. **Шлюз перехода в Review (#205)**: если в задаче заданы обязательные пункты Definition of Done, переход в `review` блокируется до закрытия всех обязательных пунктов с предоставлением evidence.
13
- 3. **Отчёт выполнения (#206)**: агент формирует структурированный отчёт (`board_report`) с полями `summary`, `changed_files`, `checks_run`, `artifacts`, `risks`.
14
- 4. **Приёмка и возврат (#207)**: в колонке `review` человек может нажать «Принять» (в `done`) или «Вернуть» (в `in-progress`) с обязательным указанием причины, которая инжектируется в следующую сессию агента.
15
- 5. **Обсуждение задачи (#208)**: карточка содержит тред комментариев с поддержкой ролей `user` и `agent`, доступный через HTTP API и инструменты `board_comments`, `board_comment_add`.
16
-
17
- ## 2. Пошаговые задачи
18
- - [ ] 1. Модель данных и миграции SQLite в `lib/store.js`.
19
- - [ ] 2. Инструменты агента `board_checklist`, `board_report`, `board_comments`, `board_comment_add`, валидаторы `board_move` в `lib/board-tool.js`.
20
- - [ ] 3. Регистрация инструментов в `lib/index.js`.
21
- - [ ] 4. Маршруты `/accept`, `/reject`, `/checklist`, `/report`, `/comments` в `lib/routes.js`.
22
- - [ ] 5. Инъекция замечаний возврата и DoD в `lib/launcher.js`.
23
- - [ ] 6. Пользовательский интерфейс Task Drawer и карточек в `lib/client.js`.
24
- - [ ] 7. Юнит-тесты: `test/checklist.test.mjs`, `test/report.test.mjs`, `test/acceptance.test.mjs`, `test/comments.test.mjs`.
25
- - [ ] 8. Актуализация `docs/design/DESIGN.md`.
@@ -1,8 +0,0 @@
1
- # Implementation Plan: Vector 2 — Concurrency & Safety (#214, #216, #224)
2
-
3
- Implementation of resource protection, workspace claim boundaries, and database backup before snapshot import in `@goodandready/dsh-kanban` (v0.1.32).
4
-
5
- ## Issues Scope
6
- 1. **#214**: Global concurrent session limit and task queue (`maxConcurrentSessions`).
7
- 2. **#216**: Project workspace claim boundaries (`workspaceClaimBoundaries`).
8
- 3. **#224**: Automatic database safety backup before board snapshot import.
@@ -1,7 +0,0 @@
1
- # Implementation Plan: Vector 3 — Git Diff / Commit Viewer & One-Click Merge (#211, #212)
2
-
3
- Implementation of in-board git diff viewer and one-click worktree merge in `@goodandready/dsh-kanban` (v0.1.33).
4
-
5
- ## Issues Scope
6
- 1. **#211**: In-drawer Git Diff and commits viewer for human acceptance.
7
- 2. **#212**: One-click worktree branch merge upon acceptance (`--no-ff`) with automated cleanup.
@@ -1,33 +0,0 @@
1
- # План: Вектор 1 — Стабильность сессий и воркдеревьев (#227, #226, #213, #215)
2
-
3
- ## 1. Контекст и цели
4
- Повышение отказоустойчивости жизненного цикла сессий задач в DSH Kanban:
5
- - Сохранение привязки сессии к проекту в левом сайдбаре DSH (meta.cwd = projectRoot) при изоляции в воркдереве (#227).
6
- - Возможность продолжить работу над задачей («↻ Продолжить / Resume») в существующем воркдереве без деструктивного сброса ветки (#226).
7
- - Игнорирование структурного шума субмодулей и gitlink drift в проверках чистоты воркдерева checkWorktreeDirty (#213).
8
- - Исключение дочерних сессий субагентов (invoke_subagent) из списка живых сессий и трекинга карточек (#215).
9
-
10
- ## 2. Архитектурные изменения
11
- - **lib/worktree.js**:
12
- - checkWorktreeDirty: вызов gitRunner(['status', '--porcelain', '--ignore-submodules=all'], worktreePath) для устранения ложного dirty-статуса от субмодулей.
13
- - **lib/launcher.js**:
14
- - obtainAgent: meta.cwd фиксируется как projectRoot, meta.worktreePath = worktreePath.
15
- -
16
- - liveSessions: фильтрация сессий с origin === 'subagent' или parentId !== undefined.
17
- - **lib/lifecycle.js**:
18
- - handleSessionEvent: игнорирование событий от сессий-субагентов.
19
- - **lib/routes.js**:
20
- - Эндпоинт POST /tasks/:id/resume.
21
- - **lib/index.js**:
22
- - Регистрация маршрута /dsh-kanban/tasks/:id/resume.
23
- - **lib/client.js**:
24
- - Кнопка «↻ Resume» в TaskDrawer.
25
-
26
- ## 3. Верификация
27
- - Юнит-тесты: est/worktree-dirty.test.mjs, est/launcher-cwd.test.mjs, est/resume.test.mjs, est/subagent-filter.test.mjs.
28
- - Полный прогон
29
- pm test и
30
- pm run compat.
31
- - Проверка пакета
32
- pm pack --dry-run --json.
33
- - Тест на MiniPC test runner через dsh-test-plugin.
@@ -1,13 +0,0 @@
1
- # Issue #238: Убрать settings.section fallback и перевести обращение к службам на ctx.get
2
-
3
- ## Контекст
4
- Issue #238 (priority/low, type/tech-debt, scope/webui) указывает:
5
- 1. Карточка настроек регистрирует запасной слот settings.section (строку в боковом списке настроек). Согласно дизайн-контракту и политике DSH-плагинов, боковой список настроек плоский и перегружен, а набор полей плагина должен жить исключительно как карточка внутри «Настройки → Плагины → Настройки плагинов» (settings.plugin.item).
6
- 2. В коде обнаружено обращение ctx.llm вместо канонического ctx.get('llm'). Контекст Cordis оборачивается в прокси, и свойства-поля могут возвращать undefined, поэтому доступ к службам должен выполняться строго через ctx.get('...').
7
-
8
- ## План работ
9
- - [ ] 1. Исправить вызовы ctx.llm на ctx.get('llm') в lib/index.js (GET /dsh-kanban/models).
10
- - [ ] 2. В lib/client.js убрать запасную регистрацию settings.section, оставив карточку только в слоте settings.plugin.item.
11
- - [ ] 3. Актуализировать тесты в test/board-ui.test.mjs и test/identity.test.mjs.
12
- - [ ] 4. Обновить docs/design/DESIGN.md (зафиксировать решение #238).
13
- - [ ] 5. Выполнить полный тестовый прогон npm test (все 651 тест) и npm run compat.