@nitra/cursor 4.1.2 → 5.0.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/bin/docs/n-cursor.md +1 -9
  3. package/bin/n-cursor.js +3 -25
  4. package/package.json +1 -1
  5. package/rules/docker/lib/docs/docker-mirror.md +1 -1
  6. package/rules/docker/lib/docs/docker-native-addon.md +1 -1
  7. package/rules/test/coverage/coverage.mjs +9 -19
  8. package/rules/test/test.mdc +1 -1
  9. package/scripts/dispatcher/trace.mjs +4 -16
  10. package/scripts/docs/build-agents-commands.md +1 -1
  11. package/scripts/docs/worktree-cli.md +1 -1
  12. package/scripts/lib/changed-files.mjs +19 -3
  13. package/scripts/lib/sync-gitignore-worktree.mjs +4 -5
  14. package/scripts/worktree-cli.mjs +1 -2
  15. package/skills/docgen/js/docgen-gen.mjs +7 -7
  16. package/docs/flow.MD +0 -1364
  17. package/scripts/dispatcher/docs/graph.md +0 -346
  18. package/scripts/dispatcher/docs/index.md +0 -236
  19. package/scripts/dispatcher/docs/trace.md +0 -296
  20. package/scripts/dispatcher/graph/lib/cmd-init.mjs +0 -112
  21. package/scripts/dispatcher/graph/lib/cmd-invalidate.mjs +0 -96
  22. package/scripts/dispatcher/graph/lib/cmd-kill.mjs +0 -141
  23. package/scripts/dispatcher/graph/lib/cmd-plan.mjs +0 -142
  24. package/scripts/dispatcher/graph/lib/cmd-run.mjs +0 -328
  25. package/scripts/dispatcher/graph/lib/cmd-scan.mjs +0 -115
  26. package/scripts/dispatcher/graph/lib/cmd-setup.mjs +0 -111
  27. package/scripts/dispatcher/graph/lib/cmd-signals.mjs +0 -328
  28. package/scripts/dispatcher/graph/lib/cmd-status.mjs +0 -131
  29. package/scripts/dispatcher/graph/lib/cmd-verify.mjs +0 -100
  30. package/scripts/dispatcher/graph/lib/cmd-watch.mjs +0 -128
  31. package/scripts/dispatcher/graph/lib/config.mjs +0 -103
  32. package/scripts/dispatcher/graph/lib/frontmatter.mjs +0 -224
  33. package/scripts/dispatcher/graph/lib/nnn.mjs +0 -127
  34. package/scripts/dispatcher/graph/lib/node-state.mjs +0 -157
  35. package/scripts/dispatcher/graph/lib/scanner.mjs +0 -235
  36. package/scripts/dispatcher/graph/lib/worktree-ops.mjs +0 -193
  37. package/scripts/dispatcher/graph-tasks.mjs +0 -92
  38. package/scripts/dispatcher/graph.mjs +0 -212
  39. package/scripts/dispatcher/index.mjs +0 -45
  40. package/scripts/dispatcher/lib/docs/active.md +0 -348
  41. package/scripts/dispatcher/lib/docs/artifact.md +0 -232
  42. package/scripts/dispatcher/lib/docs/budget.md +0 -167
  43. package/scripts/dispatcher/lib/docs/capability.md +0 -196
  44. package/scripts/dispatcher/lib/docs/commands.md +0 -210
  45. package/scripts/dispatcher/lib/docs/events.md +0 -183
  46. package/scripts/dispatcher/lib/docs/executor.md +0 -190
  47. package/scripts/dispatcher/lib/docs/gate.md +0 -231
  48. package/scripts/dispatcher/lib/docs/level.md +0 -335
  49. package/scripts/dispatcher/lib/docs/plan-panel.md +0 -181
  50. package/scripts/dispatcher/lib/docs/plan.md +0 -200
  51. package/scripts/dispatcher/lib/docs/planner.md +0 -269
  52. package/scripts/dispatcher/lib/docs/review.md +0 -255
  53. package/scripts/dispatcher/lib/docs/reviewer.md +0 -240
  54. package/scripts/dispatcher/lib/docs/snapshot.md +0 -247
  55. package/scripts/dispatcher/lib/docs/spec.md +0 -203
  56. package/scripts/dispatcher/lib/docs/state-store.md +0 -303
  57. package/scripts/dispatcher/lib/docs/subagent-runner.md +0 -173
  58. package/scripts/dispatcher/lib/events.mjs +0 -67
  59. package/scripts/dispatcher/lib/executor.mjs +0 -107
  60. package/scripts/dispatcher/lib/plan-panel.mjs +0 -76
  61. package/scripts/dispatcher/lib/state-store.mjs +0 -173
  62. package/scripts/dispatcher/lib/subagent-runner.mjs +0 -53
  63. package/scripts/graph/index.mjs +0 -115
  64. package/scripts/graph/lib/config.mjs +0 -62
  65. package/scripts/graph/lib/dag.mjs +0 -161
  66. package/scripts/graph/lib/frontmatter.mjs +0 -70
  67. package/scripts/graph/lib/nnn.mjs +0 -77
  68. package/scripts/graph/lib/state.mjs +0 -110
  69. package/scripts/graph/scan.mjs +0 -64
  70. package/scripts/graph/status.mjs +0 -86
package/docs/flow.MD DELETED
@@ -1,1364 +0,0 @@
1
- # Архітектура: Динамічний Самомодифікований Граф Задач
2
-
3
- ## Назва структури
4
-
5
- **Рекурсивний складений ОАГ** (орієнтований ациклічний граф) — із динамічним розкладом вузлів та файловим сховищем стану.
6
-
7
- ---
8
-
9
- ## Концепція
10
-
11
- ### Вузол
12
-
13
- Кожен вузол або є атомарним, або розкладається на підграф — рішення приймається **динамічно в Stage 1** на основі вхідних даних.
14
-
15
- ```
16
- Вузол
17
- ├── реалізація:
18
- │ ├── Атомарний — fn(вхідні) → вихідні
19
- │ └── Складений — Граф{ вхід, вузли[], ребра[], виходи[] }
20
- ├── стан: unassigned | pending | waiting | blocked | running | stalled | pending-audit | resolved | failed
21
- ├── вхідні: Map<portId, Value>
22
- └── вихідні: Map<portId, Value> ← заповнюється при resolved
23
- ```
24
-
25
- Для батьківського вузла інтерфейс однаковий: він чекає `resolved` не знаючи що всередині (**інкапсуляція чорної скриньки**).
26
-
27
- ### Граф
28
-
29
- - **ОАГ** — орієнтований, без циклів
30
- - **Ребра** — потік даних: виходи одного вузла стають входами наступного
31
- - **Вхідний вузол** — отримує вхідні дані батьківського складеного вузла
32
- - **Вихідні вузли** — кілька, їх виходи зливаються у виходи батька
33
- - Складений вузол може бути замінений атомарним і навпаки без змін у батьківському графі
34
-
35
- ```
36
- СкладенийВузол
37
- └── Граф
38
- ├── вхід → отримує вхідні дані батька
39
- ├── вузли[]: Вузол[]
40
- ├── ребра[]: { від: ВузолId+portId, до: ВузолId+portId }
41
- └── виходи[]: Вузол[] ← кілька, злиття → виходи батька
42
- ```
43
-
44
- **Топологія живе у `deps/` директорії кожного дочірнього вузла.** Жодного центрального файлу графу. Оркестратор відновлює повний граф скануванням усіх `task.md` і `deps/`.
45
-
46
- ---
47
-
48
- ## Naming convention вузлів
49
-
50
- - Дозволені символи: `a-z`, `0-9`, `-`
51
- - Роздільник: `-`
52
- - Унікальність: в межах батька (серед сусідів в тій самій директорії)
53
- - Приклади: `collect-data`, `analyze-results`, `synthesize`
54
- - `id` вузла = назва директорії (не дублюється у фронтматері)
55
- - Атрибути фронтматеру — англійські, snake_case
56
- - **Всі імена файлів і директорій — англійська** (обробляються скриптами)
57
- - Заголовки секцій що парсить скрипт — англійські; секції з довільними даними — будь-яка мова
58
-
59
- ---
60
-
61
- ## Файловий контракт вузла
62
-
63
- ### Структура
64
-
65
- Дочірні вузли живуть **безпосередньо** в директорії батька. Якщо директорія містить `task.md` — це вузол.
66
-
67
- ```
68
- tasks/
69
- <node-id>/
70
- task.md ← місія (immutable після graph init)
71
- a.md ← прапор: виконує агент (model_tier, skills)
72
- h.md ← прапор: виконує людина (qualification)
73
- deps/ ← залежності: кожен файл = назва dep-вузла (з .md розширенням)
74
- <dep-node-id>.md ← порожній або містить ref: для контексту
75
- plan_NNN.md ← Stage 1 output (numbered, immutable; 001 при першому або після kill)
76
- running_<pid>_until_<ts> ← git-ignored; пише wrapper при старті, видаляє при cleanup
77
- run-summary.md ← mutable; LLM-generated summary попередніх failed run_*.md
78
- run_NNN.md ← спроба виконавця: agent | engineer | human (аудитор НЕ пише)
79
- fact_NNN.md ← успішний результат; NNN = NNN відповідного run_NNN.md
80
- pending-audit_NNN.md ← запит аудиту; NNN = NNN відповідного fact_NNN.md
81
- audit-result_NNN.md ← відповідь аудитора (окремий трек); NNN = NNN pending-audit_NNN.md
82
- run_summary.md ← синтез всіх run_NNN.md після N failed спроб (пише аудитор)
83
- clarification_NNN.md ← уточнення агента після needs-clarification аудиту
84
- amended_NNN.md ← виправлена відповідь агента на зауваження аудитора
85
- history/ ← локальний аудит-trail: invalidate/kill архіви
86
- <ts>-invalidate/ ← архів fact_*.md + run_*.md при graph invalidate
87
- <child-node-id>/ ← дочірній вузол (composite spawn)
88
- task.md
89
- a.md | h.md
90
- deps/
91
- ... ← та сама структура рекурсивно
92
- ```
93
-
94
- `a.md` і `h.md` — **мутабельні прапори**, НЕ immutable артефакти. Визначають **хто** виконує: `a.md` = агент, `h.md` = людина. Можна видалити `h.md` і створити `a.md` для перемикання режиму. Ніколи обидва одночасно.
95
-
96
- ### Інваріанти
97
-
98
- **Формат:** Markdown з YAML-фронтматером.
99
-
100
- - Атрибути фронтматеру → **англійські, snake_case**
101
- - Заголовки секцій що парсить скрипт → **англійські**
102
- - Секції з довільними даними → **будь-яка мова**
103
-
104
- **Immutable файли** (не змінюються після створення): `task.md`, `plan_NNN.md`, `run_NNN.md`, `fact_NNN.md`, `pending-audit_NNN.md`, `clarification_NNN.md`, `amended_NNN.md`. Новий факт = новий файл.
105
-
106
- **Мутабельні прапори** (НЕ immutable): `a.md`, `h.md`, `running_<pid>_until_*`, `approved`, `run-summary.md`.
107
-
108
- **Mutable/deletable:** `audit-result_NNN.md` — виняток з immutability: перезаписується при clarification-циклі (максимум 1 раз). Також deletable: `graph audit-retry <path>` — архівує `audit-result_NNN.md` → `tasks/<node>/history/<ts>-audit-retry/` (де NNN = pending без result), потім видаляє. Watch на наступному тіку бачить `pending-audit_NNN.md` без `audit-result_NNN.md` → reschedule аудиту. Audit trail зберігається у `history/`.
109
-
110
- `schema_version:` — перше поле у всіх файлах з YAML-фронтматером. Поточна версія: `1`. Оркестратор відмовляє читати файли з невідомою версією. Немає `schema_version:` у файлах як мультиверсійного маркера: при релізі нової версії `n-cursor` — `graph migrate` оновлює всі файли до поточної схеми. На будь-який момент часу всі файли одної версії.
111
-
112
- **`deps/`** — директорія залежностей. Файли в ній: immutable після worktree. `deps/` може бути вкладеною директорією, яка дзеркалює структуру `tasks/`. Всі файли мають розширення `.md`. `ls -R deps/` → шлях відносно `tasks/` (після обрізання `.md` суфіксу) = dep-id. Вміст опціональний: `ref:` для контексту. Відсутня `deps/` або порожня = немає залежностей.
113
-
114
- **Cross-level залежності:** `deps/` підтримує вкладену структуру для крос-рівневих залежностей. Сусідній dep: `deps/collect-data.md` → dep-id = `collect-data`. Cross-level dep: `deps/research/analyze.md` → dep-id = `research/analyze` → `tasks/research/analyze/fact_*.md`. `ls -R deps/` дає повний перелік — без читання вмісту.
115
-
116
- **Naming convention deps/:** всі файли у `deps/` мають розширення `.md`. Агент завжди пише `<dep-node-id>.md` — ніколи без розширення.
117
-
118
- **Виняток:** `running_<pid>_until_<ts>` — **git-ignored**, ephemeral runtime marker. Пишеться wrapper при старті, видаляється при cleanup (merge, kill, або cleanup після краху). Deadline закодований у назві файлу: `ts = started_at + budget_hard_sec` (unix timestamp в секундах UTC, `Math.floor(Date.now() / 1000)`). PID закодований у назві файлу — дозволяє `kill -0 <pid>` для перевірки чи процес живий без читання вмісту файлу. Wrapper при новому запуску виконує `kill -0 <pid>` на ім'я файлу; якщо процес мертвий — cleanup running_* + worktree → продовжує старт. Watch робить `kill -0 <pid>` перед оголошенням stalled — якщо процес живий, не stalled незалежно від ts. Не відстежується git — стан `running`/`stalled` visible у директорії але не в history.
119
-
120
- **Межа immutability:**
121
-
122
- - До створення worktree — файли вузла можна вільно редагувати і видаляти
123
- - Після створення worktree — лише новий вміст, нічого не змінюється
124
-
125
- **Синтаксис посилань** (у `## Inputs` секції `task.md` та в `fact_NNN.md`):
126
-
127
- ```
128
- ref: ../collect-data/fact_001.md # весь файл (відносно поточного файлу)
129
- ref: ../collect-data/fact_001.md#results # секція за заголовком
130
- ref: ../collect-data/fact_001.md lines 5-20 # діапазон рядків
131
- ```
132
-
133
- **Єдина NNN-шкала — version chain.**
134
-
135
- `run_NNN.md` є коренем: wrapper рахує існуючі `run_*.md` → наступний NNN. Всі похідні файли успадковують NNN від run:
136
-
137
- | Файл | Звідки NNN |
138
- |---|---|
139
- | `run_NNN.md` | sequential counter (N-та спроба) |
140
- | `fact_NNN.md` | NNN = NNN run що її створив |
141
- | `pending-audit_NNN.md` | NNN = NNN відповідного `fact_NNN.md` |
142
- | `audit-result_NNN.md` | NNN = NNN відповідного `pending-audit_NNN.md` |
143
- | `clarification_NNN.md` | NNN = NNN відповідного `audit-result_NNN.md` (needs-clarification) |
144
- | `amended_NNN.md` | NNN = NNN відповідного `audit-result_NNN.md` (needs-clarification) |
145
-
146
- `fact_NNN.md` може не існувати для певного NNN — "дірка" означає що спроба N завершилась з `result: failed`.
147
-
148
- Watch перевіряє "чи оброблено" без читання файлів: `pending-audit_003.md` оброблено ↔ існує `audit-result_003.md`.
149
-
150
- Zero-padded до 3 цифр: `001`, `002`, …
151
-
152
- `plan_NNN.md` — окрема логіка:
153
- - Worktree **merged** або робота **продовжується** в ньому → нумерація продовжується (`002`, `003`, …)
154
- - `graph kill` → видаляє всі `plan_*.md` файли вузла → наступний старт з `plan_001.md`
155
-
156
- Актуальний `plan_NNN.md` — файл з найбільшим номером.
157
-
158
- ---
159
-
160
- ### Схеми файлів
161
-
162
- #### `task.md`
163
-
164
- Тільки місія. **Immutable після `graph init`**. Не містить інформацію про виконавця — вона в `a.md`/`h.md`.
165
-
166
- ```markdown
167
- ---
168
- schema_version: 1
169
- created_at: 2026-06-06T10:00:00Z
170
- budget_sec: 600 # м'який ліміт — агент перевіряє через ENV
171
- budget_hard_sec: 0 # hard kill; 0 = вимкнено; default = budget_sec × budget_hard_sec_multiplier
172
- progress_timeout_sec: 300 # kill якщо немає змін у worktree N сек (опціонально)
173
- hint: atomic # опціонально: atomic | composite — підказка агенту
174
- parent: research/collect-data # відносно tasks/; відсутній у кореневого
175
- ---
176
- ## Task
177
- Що саме має виконати цей вузол.
178
-
179
- ## Done when
180
- Чіткий критерій: що означає "resolved".
181
-
182
- ## Inputs
183
- Контекст від батька або inline-дані (не залежності — ті у `deps/`).
184
-
185
- ### instruction
186
- Обробити лише перші 50 результатів, ігнорувати дублікати.
187
- ```
188
-
189
- | Поле / секція | Обов'язкове | Примітка |
190
- |---|---|---|
191
- | `created_at` | так | ISO 8601, перше поле |
192
- | `budget_sec` | так | секунди; м'який ліміт — агент сам перевіряє залишок через ENV |
193
- | `budget_hard_sec` | ні | hard kill; 0 = вимкнено; default = `budget_sec × budget_hard_sec_multiplier` |
194
- | `progress_timeout_sec` | ні | kill якщо немає змін `mtime` у worktree N сек |
195
- | `hint` | ні | підказка агенту щодо типу вузла (atomic/composite) |
196
- | `parent` | ні | відносно `tasks/`; відсутній у кореневого |
197
- | `## Task` | так | — |
198
- | `## Done when` | так | — |
199
- | `## Inputs` | ні | відсутній якщо батько нічого не передає і немає inline-даних |
200
-
201
- Пріоритет budget-полів: CLI-аргумент > `plan_NNN.md` > `.n-cursor-override.json` > `task.md` > `.n-cursor.json`.
202
-
203
- ---
204
-
205
- #### `a.md`
206
-
207
- Мутабельний прапор. Якщо є — вузол виконує **агент**. При перемиканні: видалити `h.md`, створити `a.md`.
208
-
209
- ```yaml
210
- ---
211
- schema_version: 1
212
- created_at: ISO8601
213
- model_tier: AVG # MIM | AVG | MAX
214
- skills:
215
- - bash
216
- - write-files
217
- context_runs: auto # auto = structured summary + 50% fallback; або явне число
218
- ---
219
- ```
220
-
221
- | Поле | Обов'язкове | Примітка |
222
- |---|---|---|
223
- | `schema_version` | так | завжди `1` (перше поле) |
224
- | `created_at` | так | ISO 8601 |
225
- | `model_tier` | ні | `MIM` \| `AVG` \| `MAX`; default: `AVG` |
226
- | `skills` | ні | список інструментів агента (bash, write-files, web-search, …) |
227
- | `context_runs` | ні | `auto` = structured summary + 50% fallback; або явне число |
228
-
229
- ---
230
-
231
- #### `h.md`
232
-
233
- Мутабельний прапор. Якщо є — вузол виконує **людина**. При перемиканні: видалити `a.md`, створити `h.md`.
234
-
235
- ```yaml
236
- ---
237
- schema_version: 1
238
- created_at: ISO8601
239
- email: engineer@example.com
240
- notify: true
241
- qualification: "senior backend engineer"
242
- ---
243
- ```
244
-
245
- | Поле | Обов'язкове | Примітка |
246
- |---|---|---|
247
- | `schema_version` | так | завжди `1` (перше поле) |
248
- | `created_at` | так | ISO 8601 |
249
- | `email` | ні | email для notify |
250
- | `notify` | ні | `true` — надсилати нагадування на email |
251
- | `qualification` | ні | рівень і спеціалізація виконавця |
252
-
253
- ---
254
-
255
- #### `deps/` — залежності вузла
256
-
257
- Директорія присутня якщо вузол має попередників. Кожен файл = одна залежність. `deps/` може бути вкладеною директорією — дзеркалює структуру `tasks/`.
258
-
259
- ```
260
- deps/
261
- collect-data.md ← сусід (tasks/<node>/../collect-data/)
262
- fetch-sources.md
263
- research/
264
- analyze.md ← крос-рівневий (tasks/research/analyze/)
265
- ```
266
-
267
- Ім'я файлу (відносний шлях від `deps/`) = шлях dep-вузла відносно `tasks/`. Всі файли в `deps/` мають розширення `.md`. `ls -R deps/` → шляхи → обрізати `.md` → dep-id.
268
-
269
- | Елемент | Значення |
270
- |---|---|
271
- | Назва файлу (або шлях у вкладеній директорії) | Ідентифікатор dep-вузла відносно `tasks/`. Сусідні deps: `deps/collect-data.md`. Крос-рівневі deps: `deps/research/analyze.md`. `ls -R deps/` → обрізати `.md` → dep-id. |
272
- | Наявність файлу | dep існує; `ls -R deps/` → список dep-id без читання вмісту |
273
- | Вміст | опціональний: `ref:` на `fact_NNN.md` попередника + контекст для агента |
274
-
275
- **Deps satisfaction:** `ls -R deps/` → для кожного path → обрізати `.md` суфікс → dep-id = відносний шлях від `tasks/` → перевірити `tasks/<dep-id>/fact_*.md`.
276
-
277
- **Приклад `deps/collect-data.md`:**
278
-
279
- ```markdown
280
- ref: ../collect-data/fact_001.md#results
281
- Використовувати лише перші 50 записів, ігнорувати дублікати.
282
- ```
283
-
284
- ---
285
-
286
- ---
287
-
288
- #### `plan_NNN.md`
289
-
290
- Stage 1 output. Immutable. `001` при першому плануванні або після `graph kill`; `002`, `003`, … якщо worktree merged або продовжується.
291
-
292
- ```markdown
293
- ---
294
- schema_version: 1
295
- created_at: ISO8601
296
- decision: atomic | composite
297
- budget_sec: 3600 # уточнений бюджет (перекриває task file; опціонально)
298
- budget_hard_sec: 10800 # уточнений hard limit (0 = без kill; опціонально)
299
- progress_timeout_sec: 600 # kill якщо немає змін у worktree N сек (опціонально)
300
- ---
301
-
302
- ## Context
303
- Чому саме такий підхід.
304
-
305
- ## Approach
306
- <!-- atomic: покроковий план виконання -->
307
- <!-- composite: список дочірніх вузлів з описами та dep-зв'язками -->
308
-
309
- ## Risks
310
- Що може піти не так.
311
- ```
312
-
313
- Пріоритет budget-полів: `plan_NNN.md` > `.n-cursor-override.json` > task file > `.n-cursor.json`.
314
-
315
- `plan_NNN.md` не містить поле `mode:` — mode визначається `a.md`/`h.md`, які є єдиним джерелом правди щодо виконавця.
316
-
317
- Після запису `plan_NNN.md` для composite вузлів — стан переходить у `plan-review`. `graph spawn --approve` або `graph spawn --reject`. Без approve — жодних дочірніх вузлів. Для атомарних вузлів `plan-review` не застосовується.
318
-
319
- ---
320
-
321
- #### `fact_NNN.md`
322
-
323
- Immutable. Успішний вихід вузла. NNN = NNN відповідного `run_NNN.md` (wrapper записує з тим самим NNN). Якщо `run_NNN.md` має `result: failed` — `fact_NNN.md` не існує (дірка у нумерації). Скрипт читає файл з найбільшим NNN як актуальний.
324
-
325
- `## Summary` — обов'язкова перша секція для observability. Решта — довільні порти для наступників.
326
-
327
- ```markdown
328
- ---
329
- schema_version: 1
330
- created_at: 2026-06-06T10:05:00Z
331
- hash: sha256:<hash секції ## Result>
332
- ---
333
- ## Summary
334
- Проаналізовано 847 записів, виявлено 3 аномалії у секції payments.
335
-
336
- ## anomalies
337
- ref: data/anomalies.json
338
-
339
- ## full-report
340
- ref: reports/payment-report.md
341
- ```
342
-
343
- | Секція | Обов'язкова | Правило |
344
- |---|---|---|
345
- | `## Summary` | так | одне речення, inline |
346
- | довільні секції | ні | `ref:` якщо є файл, інакше inline |
347
-
348
- Агент вільно створює файли де завгодно в проєкті; `fact_NNN.md` посилається через `ref:`.
349
-
350
- ---
351
-
352
- #### `run_NNN.md`
353
-
354
- Immutable. Один файл на одну спробу виконавця. Записується **після** завершення. Аудитор `run_NNN.md` не пише.
355
-
356
- `result: success` → wrapper також пише `fact_NNN.md` з тим самим NNN; `run_NNN.md` містить `## Ref → fact_NNN.md`.
357
- `result: failed` → `fact_NNN.md` не створюється; `run_NNN.md` містить весь контент спроби: `## Reasoning` + `## Partial Work` якщо є часткові результати.
358
-
359
- ```markdown
360
- ---
361
- schema_version: 1
362
- created_at: 2026-06-06T10:01:00Z
363
- actor: agent
364
- result: failed
365
- worktree: .worktrees/research-analyze-1749200400 # тільки якщо failed — для debug
366
- ---
367
- ## Reasoning
368
- Спробував обробити весь датасет одразу — задача занадто велика.
369
-
370
- ## Script
371
- exit_code: 1
372
- stderr: Error: context length exceeded 200k tokens
373
-
374
- ## Ref
375
- ref: fact_001.md
376
- ```
377
-
378
- | Поле / секція | Хто пише | Коли | Що містить |
379
- |---|---|---|---|
380
- | `created_at` | wrapper | завжди | ISO 8601 |
381
- | `actor` | wrapper | завжди | `agent` \| `engineer` \| `human` |
382
- | `result` | wrapper | завжди | `success` \| `failed` |
383
- | `worktree` | wrapper | якщо failed | шлях до worktree (для debug) |
384
- | `## Reasoning` | виконавець | failed: обов'язково; success: рекомендовано | чому так, що вирішив, що спробував |
385
- | `## Completed` | виконавець | **обов'язково при failed** | що вдалось виконати до зупинки |
386
- | `## Blockers` | виконавець | **обов'язково при failed** | конкретні блокери для швидкої діагностики |
387
- | `## Partial Work` | виконавець | опціонально (failed) | часткові результати, дані, незавершений код |
388
- | `## Next Attempt` | виконавець | **обов'язково при failed** | рекомендація для наступного агента/інженера |
389
- | `## Script` | wrapper | якщо crash | `exit_code:`, `stderr:` |
390
- | `## Changes` | engineer | опціонально | список файлів змінених у патчі |
391
- | `## Ref` | виконавець | якщо success | `ref: fact_NNN.md` (обов'язково при success) |
392
-
393
- Агент може додавати довільні секції — схема фіксує відомі з конкретними правилами.
394
-
395
- ---
396
-
397
- #### `pending-audit_NNN.md`
398
-
399
- Immutable. NNN = NNN відповідного `fact_NNN.md`. Створюється через `graph audit`.
400
-
401
- ```markdown
402
- ---
403
- schema_version: 1
404
- created_at: ISO8601
405
- actor: agent | human
406
- ---
407
- ```
408
-
409
- ---
410
-
411
- #### `audit-result_NNN.md`
412
-
413
- **Deletable** (на відміну від решти файлів). `graph audit-retry <path>` архівує файл → `tasks/<node>/history/<ts>-audit-retry/` і видаляє — дозволяє повторний аудит того самого `fact_NNN.md` без нового run. Watch бачить `pending-audit_NNN.md` без `audit-result_NNN.md` → reschedule аудиту. Audit trail зберігається у `history/`. NNN = NNN відповідного `pending-audit_NNN.md`. Пишеться виключно аудитором.
414
-
415
- ```markdown
416
- ---
417
- schema_version: 1
418
- created_at: ISO8601
419
- actor: auditor
420
- result: success | failed | needs-clarification
421
- ---
422
- ## Reasoning
423
- Що перевірено, що схвалено або що конкретно не відповідає ## Done when.
424
- ```
425
-
426
- | Поле | Значення |
427
- |---|---|
428
- | `actor` | завжди `auditor` |
429
- | `result` | `success` → wrapper мержить worktree; `failed` → агент отримує зауваження і доробляє; `needs-clarification` → агент пише `amended_NNN.md` |
430
- | `## Reasoning` | обов'язковий; чіткий зв'язок із критерієм `## Done when` |
431
-
432
- ---
433
-
434
- #### `amended_NNN.md`
435
-
436
- Immutable. NNN = NNN відповідного `audit-result_NNN.md` (needs-clarification). Пишеться агентом у відповідь на `needs-clarification`. Містить відповідь на зауваження аудитора, уточнення, виправлення. Дозволяється лише 1 раз: якщо після `amended_NNN.md` аудит знову `needs-clarification` → treat as `rejected` → новий run.
437
-
438
- ```markdown
439
- ---
440
- schema_version: 1
441
- created_at: ISO8601
442
- audit_ref: audit-result_NNN.md
443
- ---
444
- ## Response
445
- Відповідь на зауваження аудитора.
446
- ```
447
-
448
- ---
449
-
450
- #### `run_summary.md`
451
-
452
- Пишеться аудитором після N (дефолт: 5) поспіль failed спроб. Вміст: стислий аналіз всіх попередніх `run_*.md` — що пробували, що не вийшло, ключові висновки. Оркестратор при наступних запусках передає `run_summary.md` у context замість всіх `run_*.md` — збереження context window.
453
-
454
- ```markdown
455
- ---
456
- schema_version: 1
457
- created_at: ISO8601
458
- actor: auditor
459
- runs_analyzed: 5
460
- ---
461
- ## Summary
462
- Стислий аналіз що пробували, що не вийшло.
463
- ```
464
-
465
- ---
466
-
467
- #### `history/` — аудит-архів вузла
468
-
469
- `history/` — вкладена директорія у вузлі для зберігання архівних артефактів. **Не є дочірнім вузлом** — не містить `task.md`, тому scan ігнорує.
470
-
471
- ```
472
- tasks/<node>/
473
- history/ ← аудит-архів вузла (немає task.md → не вузол)
474
- <ts>-invalidate/ ← архів після graph invalidate
475
- fact_001.md
476
- run_001.md
477
- <ts>-audit-retry/ ← архів видаленого audit-result
478
- audit-result_003.md
479
-
480
- <tasks-root>/
481
- .history/ ← архів видалених вузлів (після graph kill)
482
- <ts>-kill-<path>/
483
- task.md
484
- fact_001.md
485
- ...
486
- ```
487
-
488
- `history/` не має `task.md` → scan не плутає з дочірніми вузлами. `<tasks-root>/.history/` — глобальний архів kill-операцій на рівні tasks root.
489
-
490
- ---
491
-
492
- ## Стани вузла
493
-
494
- Стан вузла — **derived** (визначається наявністю файлів, не зберігається явно).
495
-
496
- **Інваріант: всі стани визначаються виключно переліком файлів і директорій — без читання вмісту.**
497
-
498
- | Умова (file presence) | Стан |
499
- |---|---|
500
- | `task.md` є, немає `a.md`/`h.md` | `unassigned` |
501
- | `h.md` є, немає `fact_*`, немає `running_*` | `pending` |
502
- | `a.md` є, deps resolved, немає `running_*`, немає `fact_*` | `waiting` |
503
- | `a.md` є, deps НЕ resolved | `blocked` |
504
- | `running_<pid>_until_<ts>`, `ts > now()` | `running` |
505
- | `running_<pid>_until_<ts>`, `ts ≤ now()` | `stalled` |
506
- | `pending-audit_N` є, `audit-result_N` немає | `pending-audit` |
507
- | `fact_*.md` є | `resolved` |
508
- | `run_*.md` є, `fact_*.md` немає, `running_*` немає | `failed` |
509
-
510
- Пріоритет: `resolved` > `pending-audit` > `stalled` > `running` > `waiting`/`blocked` > `pending` > `unassigned` > `failed`
511
-
512
- Семантика станів очікування:
513
- - `a.md`/`h.md` = **хто** виконує (агент або людина)
514
- - Стан `waiting` = a.md є, deps resolved — runner: немає `plan_*.md` → `graph plan`; є `plan_*.md` → `graph run`
515
- - Стан `pending` = h.md є — runner завжди пропускає; людина сама вирішує
516
-
517
- Примітки:
518
- - `blocked` → тільки для `a.md` (deps не resolved)
519
- - `failed` → найнижчий пріоритет, пасивний
520
-
521
- `unassigned` — `task.md` є але не призначено виконавця. Оркестратор пропускає. Watch нагадує (TODO: Telegram) якщо вузол у `unassigned` > `stale_worktree_min` хвилин.
522
-
523
- `pending` — `h.md` присутній (з планом або без). Runner завжди пропускає; watch виводить нагадування + notify email з `h.md` (Telegram TODO). Людина сама вирішує: `graph plan` якщо потрібен план, або `graph run --actor human` якщо план вже є.
524
-
525
- `waiting` — `a.md` є, deps resolved. Runner: немає `plan_*.md` → auto `graph plan`; є `plan_*.md` → auto `graph run`.
526
-
527
- `blocked` — тільки для `a.md`; deps satisfaction перевіряється при `graph scan` (batch). Відображається в `graph status` як `[blocked: <dep-id>]`. Якщо dep-вузол не має `fact_*.md` → `blocked-invalid-dep`, skip.
528
-
529
- `stalled` — `running_<pid>_until_<ts>` присутній, `ts ≤ now() - grace_period`, і `kill -0 <pid>` повертає помилку (процес не існує). Watch виявляє без читання вмісту. Wrapper пише цей файл при старті, видаляє при cleanup.
530
-
531
- Watch перевіряє: `ts + grace_period ≤ now()` (grace_period: 60s — конфіг у `.n-cursor.json`).
532
- При `ts > now() + grace_period`: `kill -0 <pid>` — якщо процес мертвий → cleanup + `failed`.
533
-
534
- Пріоритет перевірки: `resolved` > `pending-audit` > `stalled` > `running` > `waiting`/`blocked` > `pending` > `unassigned` > `failed`.
535
-
536
- **Deps check — batch scan, не per-node:** Список залежностей = `ls -R tasks/<node>/deps/` → шляхи → обрізати `.md` → dep-id. Deps satisfaction — обчислюється в пам'яті при `graph scan` (один раз за цикл, будує граф). Без читання вмісту файлів.
537
-
538
- **Composite вузол** (є хоча б одна дочірня директорія з `task.md`):
539
-
540
- Стан composite вузла визначається так само як атомарного: `fact_*.md` є → `resolved`. O(1) — без рекурсивного сканування дітей.
541
-
542
- **Composite `fact_NNN.md`** пише **wrapper** (не агент) — тригер: `graph done <child>` після успішного merge:
543
- ```
544
- parent = tasks/<child>/..
545
- якщо всі children мають fact_*.md:
546
- → пише tasks/<parent>/fact_NNN.md (NNN = count(існуючих fact_*.md) + 1)
547
- → ## Summary = агрегація ## Summary всіх дочірніх вузлів
548
- → рекурсивно перевіряє батька батька (propagation вгору)
549
- ```
550
-
551
- Один merge може закрити весь ланцюг composite вузлів вгору. Уніфікована перевірка: будь-який `fact_*.md` → resolved (атомарні і composite — однаковий механізм).
552
-
553
- #### Видимість станів у статусі
554
-
555
- ```
556
- graph — waiting:2 pending:1 running:1 blocked:2 [slots: 1/5]
557
-
558
- 👤 design-api [pending: h.md — run: graph run tasks/design-api/ --actor human]
559
- ○ implement [blocked: design-api]
560
- ○ deploy [blocked: implement]
561
- ◉ collect-data [running]
562
- ○ analyze [waiting]
563
- ✓ prepare-data [resolved]
564
- ```
565
-
566
- ---
567
-
568
- ## CLI контракт (`n-cursor graph`)
569
-
570
- Конфіг: `NCURSOR_TASKS_DIR` env або `.n-cursor.json` → поле `tasks_dir`, дефолт `./tasks/`.
571
-
572
- Всі команди підтримують `--json` для machine-readable виводу.
573
-
574
- ### Список команд
575
-
576
- ```
577
- n-cursor graph
578
- setup ← ініціалізація проєкту
579
- init <name> [--task "..."] ← створити task.md (unassigned; потім додати a.md або h.md)
580
- plan [<path>] [--mode agent] ← Stage 1: spec + decompose → plan_NNN.md або spawn
581
- status [<path>] [--json] ← стан графу
582
- scan [--json] ← повне сканування + exit 1 якщо є failed
583
- run [<path>] [--actor a] [--auto] ← запустити вузол або оркестратор
584
- kill <path> [--no-cascade] ← archive + git rm вузла і нащадків; SIGTERM живих процесів
585
- invalidate <path> ← архівує fact_*/run_* у history/; cascade вниз по нащадках
586
- audit-retry <path> ← видаляє audit-result_NNN.md (NNN = найновіший pending без result) → watch підхоплює повторний аудит
587
- migrate ← reads graph_schema з .n-cursor.json → applies migration chain
588
-
589
- # сигнали агента:
590
- done <path> ← успіх → merge
591
- audit <path> ← хоче аудит → pending-audit_NNN.md
592
- (NNN = NNN останнього fact_NNN.md; auto-detected)
593
- ВИМАГАЄ наявного fact_NNN.md; помилка якщо відсутній
594
- failed <path> ← провал
595
- spawn <path> --mode agent|human [--approve] ← composite → зареєструвати дочірні (--mode обов'язковий)
596
- --approve: схвалити plan-review (plan_NNN.md)
597
- --reject: відхилити plan-review
598
-
599
- n-cursor watch ← periodic rescan: черга аудиту + попередження (TODO: daemon + Telegram)
600
- ```
601
-
602
- ### Опис команд
603
-
604
- ```
605
- graph setup
606
- ```
607
- - Ініціалізує проект: `.n-cursor.json`, `.n-cursor/system-prompt.md`, `.n-cursor/engineer-prompt.md`, `tasks/`
608
- - Встановлює git `post-merge` hook
609
-
610
- ```
611
- graph init <name> [--task "..."] [--budget-sec N] [--mode agent|human]
612
- ```
613
- - Генерує `tasks/<name>/task.md` через LLM на основі опису `--task` + `a.md` (якщо `--mode agent`) або залишає `unassigned`
614
- - `--mode agent|human` — strongly recommended; без нього → CLI warning + вузол у стані `unassigned` (не пише `a.md`/`h.md`)
615
- - `budget_sec` з `--budget-sec` або `default_budget_sec` конфігу
616
- - Без `--mode` → читає `default_mode` з конфігу (дефолт: `agent`), пише відповідний `a.md`/`h.md` автоматично
617
- - Людина доповнює `## Inputs` і `## Done when` вручну; може додати `a.md` або `h.md` для перевизначення
618
-
619
- ```
620
- graph plan [<path>] [--mode agent]
621
- ```
622
- - Stage 1: spec + decompose; planning temperature: `plan_temperature` з конфігу (дефолт: `0`)
623
- - `mode` з прапорів: `h.md` = human, `a.md` = agent; `--mode agent` — перевизначає (видаляє `h.md`, створює `a.md`)
624
- - `h.md` — IDE-діалог; `a.md` — автономний
625
- - Вихід atomic: `plan_NNN.md` (decision: atomic) → `touch .n-cursor/wake` → watch підхоплює на наступному скані (≤5 хв) → `graph run --auto`
626
- - Вихід composite: `plan_NNN.md` (decision: composite) → стан `plan-review`. Дочірні вузли НЕ створюються до `graph spawn --approve`. `graph spawn --approve` → дочірні `<child>/task.md` + `<child>/<mode>.md` + `deps/` → `graph run --auto` підхоплює дітей
627
- - **Composite planning — агент пише a.md/h.md для дітей:** при composite `graph plan` агент пише `a.md` або `h.md` для **кожного** дочірнього вузла. `a.md` містить `model_tier` і `skills` визначені агентом індивідуально. Агент може написати `h.md` якщо підзавдання потребує людини. Після spawn: жодних `unassigned` дітей. Батько `h.md` → дочірні отримують `unassigned`, людина вирішує mode через `graph init child --mode`.
628
-
629
- ```
630
- graph status [path] [--json]
631
- ```
632
- - без `path` → весь граф від `tasks/`
633
- - з `path` → вузол і нащадки
634
- - виводить: state, останній `run_NNN`, активний worktree, `[slots: X/agent_concurrency]`; `pending` — з підказкою команди
635
- - exit: `0` ok | `1` вузол не знайдено
636
-
637
- ```
638
- graph run [<path>] [--actor agent|engineer|human|auditor] [--auto]
639
- ```
640
- - **З `path`**: один вузол — перевірка deps → mkdir worktree (lock) → агент → timeout → `run_NNN.md` → merge
641
- - **`--auto` / без `path`**: one-shot оркестратор — знаходить всі `waiting` вузли з `a.md`; немає плану → auto `graph plan`; є план → auto `graph run`; spawns через mkdir lock; `pending` (h.md) і `unassigned` — пропускає
642
- - **`--actor auditor`**: wrapper аудитора — spawns auditor subprocess, чекає, читає `audit-result_NNN.md`, success → merge
643
- - **`--actor human`**: створює worktree і виводить шлях; людина працює вручну у цьому worktree; по завершенні викликає `graph done|audit|failed` сама; wrapper пише `run_NNN.md` (actor: human) після сигналу
644
- - **дефолтний `--actor`**: `a.md` → agent; `h.md` → human
645
- - exit: `0` всі resolved | `1` є failed | `2` timeout | `3` system error
646
-
647
- ```
648
- graph kill <path>
649
- ```
650
- - Cascade завжди повний; `--no-cascade` — escape hatch для примусового одиночного kill.
651
- - Послідовність:
652
- 1. SIGTERM процесу якщо є живий PID (`kill -0 <pid>` перевірка через `running_<pid>_until_<ts>`)
653
- 2. `git worktree remove --force <worktree>` якщо є
654
- 3. Архівує весь вузол і нащадків: `<tasks-root>/.history/<ts>-kill-<node-path>/`
655
- 4. `git rm -r tasks/<path>/` + `git commit "graph: kill tasks/<path>"`
656
- - Вимагає clean working tree. При незакомічених змінах → error: "commit or stash changes before graph kill".
657
- - **Undo kill** = `git revert <kill-commit>` відновлює весь піддерево.
658
- - exit: `0` all killed | `1` часткова помилка
659
-
660
- ```
661
- graph invalidate <path>
662
- ```
663
- - Архівує `fact_*.md`, `run_*.md` → `tasks/<node>/history/<ts>-invalidate/`
664
- - `task.md`, `a.md`/`h.md`, `deps/` залишаються; `plan_*.md` — якщо є, вузол повертається до `waiting`
665
- - Cascade вниз по нащадках (кожен отримує `graph invalidate` рекурсивно)
666
- - Не пише `invalidated` sentinel — стан derived автоматично з відсутності `fact_*.md`
667
-
668
- ```
669
- graph migrate
670
- ```
671
- - Виконується при upgrade `n-cursor` — приводить всі файли до поточної схеми
672
- - Reads `graph_schema` з `.n-cursor.json` → applies migration chain до поточної версії
673
- - Обов'язковий крок при релізі нової версії `n-cursor` перед запуском watch/run
674
-
675
- ```
676
- graph scan [--json]
677
- ```
678
- - Сканує всі `task.md`, реконструює DAG через `deps/`, виводить стан кожного вузла
679
- - exit: `0` граф чистий | `1` є вузли у стані `failed`
680
-
681
- ---
682
-
683
- ## Вузол як агент
684
-
685
- Кожен вузол — це запуск Claude. Оркестратор передає протокол через `system_prompt`; файли вузла — лише місія та дані.
686
-
687
- **Запуск:**
688
-
689
- ```
690
- Claude(
691
- system_prompt = <протокол оркестратора>,
692
- context = [task.md] + [a.md|h.md] + [deps/] + [plan_*.md] + [Prior attempts резюме (якщо є failed runs)] + [audit-result_*.md]
693
- )
694
- ```
695
-
696
- > **Prior attempts резюме:** Агент отримує компактне **Prior attempts** резюме замість повних `run_NNN.md`. Wrapper витягує з усіх failed runs три секції: `## Completed`, `## Blockers`, `## Next Attempt`. Склеює у резюме фіксованого розміру незалежно від кількості спроб.
697
- >
698
- > `## Completed`, `## Blockers`, `## Next Attempt` — **обов'язкові** при `result: failed`.
699
- > При `result: success` — `## Completed` + `## Summary` (для composite агрегації).
700
- >
701
- > Повні `run_NNN.md` залишаються у директорії для людського аудиту.
702
-
703
- > **Примітка:** Wrapper генерує `run-summary.md` через LLM (audit_model tier) якщо є 2+ failed `run_NNN.md`. Це transient file — видаляється при `graph kill`.
704
-
705
- **System prompt оркестратора** (агент ніколи не бачить у своїх файлах):
706
-
707
- ```
708
- Твій файл місії: task.md. Режим виконання: a.md (агент) або h.md (людина). Залежності у deps/.
709
-
710
- Крок 1 — якщо plan_NNN.md ще немає: виклич graph plan <шлях>
711
- → atomic: план готовий → переходь до роботи
712
- → composite: ти вже створив дочірні task.md + a.md|h.md + deps/ → виклич graph spawn → завершуй
713
-
714
- Крок 2 — є plan_NNN.md (decision: atomic): виконай роботу, потім:
715
- → впевнений: запиши fact_NNN.md (NNN із ENV NCURSOR_RUN_NNN)
716
- виклич: graph done <шлях> ← wrapper запише run_NNN.md (success)
717
- → потрібна перевірка: запиши fact_NNN.md (NNN із ENV NCURSOR_RUN_NNN)
718
- виклич: graph audit <шлях> ← wrapper запише run_NNN.md (success)
719
- → помилка: НЕ пиши fact_NNN.md
720
- виклич: graph failed <шлях> ← wrapper запише run_NNN.md (failed)
721
- ```
722
-
723
- **Агент може читати файли будь-яких вузлів** (батька, братів, дочірніх) без обмежень.
724
-
725
- **Всі агенти запускаються через wrapper-скрипт** — відстежує таймаут і може кілити процес.
726
-
727
- ---
728
-
729
- ## Два етапи виконання вузла
730
-
731
- **Етап 1 — `graph plan`**
732
-
733
- ```
734
- --mode human # інтерактивний діалог з людиною
735
- --mode agent # автономно, без участі людини (default якщо є a.md)
736
- ```
737
-
738
- Вихід:
739
- - **Atomic** → `plan_NNN.md` (decision: atomic) → Етап 2
740
- - **Composite** → `plan_NNN.md` (decision: composite) → стан `plan-review` → `graph spawn --approve` → дочірні вузли → `graph spawn`
741
-
742
- **Етап 2 — Execution**
743
-
744
- ```
745
- агент виконує роботу (спираючись на plan_NNN.md)
746
- → пише fact_NNN.md
747
- → graph done ← впевнений → merge
748
- → graph audit ← хоче зовнішню перевірку → pending-audit_NNN.md → черга
749
- → graph failed ← задача невирішувана
750
- ```
751
-
752
- ---
753
-
754
- ## Оркестрація
755
-
756
- **`graph run --auto`** (one-shot, тригерується post-merge hook) і **`n-cursor watch`** (periodic rescan) — обидва шукають `waiting` вузли з `a.md`. Координація через `mkdir .worktrees/<node>-<hash>/` — атомарна FS-операція; перший runner отримує директорію, другий — `EEXIST` → skip.
757
-
758
- ```
759
- merge → post-merge hook:
760
- n-cursor graph run --auto (one-shot)
761
- touch .n-cursor/wake
762
-
763
- n-cursor watch (periodic, кожні 5 хв або по wake):
764
- → сканує граф знизу вверх
765
- → waiting вузли з a.md → немає плану: auto graph plan; є план: auto graph run
766
- 0a. Рахує агентські `running_<pid>_until_*` (вузли з `a.md`) де `ts + grace_period > now()` → якщо `count ≥ agent_concurrency` → skip до наступного тіку
767
- 0b. Перевіряє: вільне місце на диску >= min_free_disk_gb → якщо ні: Telegram алерт + skip
768
- 0c. `--auto` сортує `waiting` вузли: leaf nodes першими (розблоковують залежні), потім за nearest deadline → якщо `running_count ≥ agent_concurrency` → skip решту до наступного тіку
769
- mkdir .worktrees/<node>-<deadline-unix>/
770
- EEXIST → skip
771
- success → graph run --actor agent (при відсутності plan_*.md → auto-планує перед run)
772
- → pending вузли з h.md →
773
- повідомляє людину (TODO: Telegram); runner пропускає
774
- → pending-audit_NNN.md без audit-result_NNN.md →
775
- graph run --actor auditor
776
- → plan-review вузли → пропускає (чекають approve)
777
- ```
778
-
779
- ```
780
- waiting + a.md (немає плану) → auto: graph plan
781
- waiting + a.md (є план) → auto: graph run
782
- pending + h.md → skip + notify людину
783
- ```
784
-
785
- **`unassigned`** (немає `a.md`/`h.md`): runner пропускає; watch нагадує (TODO: Telegram).
786
-
787
- **`waiting`** + `a.md`: watch запускає `graph plan` (немає плану) або `graph run` (є план) автономно.
788
-
789
- **`pending`** + `h.md`: watch виводить нагадування + notify.
790
-
791
- **`pending`** + `h.md`: watch виводить нагадування + notify email. Runner пропускає.
792
-
793
- **`pending`** (h.md): watch виводить нагадування + notify email з `h.md`. Runner пропускає.
794
-
795
- **`plan-review`** вузли (composite з `plan_NNN.md` але без `approved` sentinel) — watch пропускає. Людина викликає `graph spawn --approve <path>` або `graph spawn --reject <path>`.
796
-
797
- **Orphan worktree** (wrapper впав до merge): watch при скані знаходить `resolved + orphan worktree` → idempotent merge + cleanup.
798
-
799
- **EngineerAgent:** watch при скані знаходить вузол у стані `failed` → запускає `graph run <path> --actor engineer`.
800
-
801
- ---
802
-
803
- ## Аудит (async черга)
804
-
805
- Якісний гейт на вимогу агента або людини. Іде через файлову чергу — не синхронно.
806
-
807
- ### Потік
808
-
809
- ```
810
- агент/людина:
811
- → пише fact_NNN.md ← ОБОВ'ЯЗКОВО перед audit
812
- → graph audit <path>
813
- wrapper: перевіряє наявність fact_NNN.md → помилка якщо відсутній
814
- wrapper: створює pending-audit_NNN.md (NNN = NNN fact_NNN.md)
815
- wrapper: merge worktree → видалити worktree ← pending-audit_NNN.md тепер у main
816
-
817
- n-cursor watch:
818
- → знаходить pending-audit_NNN.md без audit-result_NNN.md у main
819
- → graph run --actor auditor <path>
820
- wrapper: новий worktree з main (читає fact_NNN.md + pending-audit_NNN.md + amended_NNN.md якщо є)
821
- auditor → пише audit-result_NNN.md (NNN = NNN pending-audit)
822
- success → merge + delete auditor worktree + touch .n-cursor/wake
823
- → для composite: wrapper пише fact_NNN.md у батьківський вузол
824
- failed → merge audit-result → agent стартує новий worktree → бачить зауваження:
825
- fact_(N+1).md → graph audit → pending-audit_(N+1).md → черга знову
826
- needs-clarification → merge audit-result →
827
- agent пише amended_NNN.md у директорію вузла (NNN = NNN audit-result)
828
- watch виявляє: pending-audit_NNN.md + audit-result_NNN.md(needs-clarif) + amended_NNN.md
829
- → graph run --actor auditor (повторний)
830
- аудитор читає fact_NNN.md + amended_NNN.md → перезаписує audit-result_NNN.md
831
- якщо знову needs-clarification → treat as failed → новий run (amended лише 1 раз)
832
-
833
- → рахує run_*.md без fact_*.md: якщо ≥ run_summary_threshold (дефолт: 5) →
834
- auditor пише run_summary.md (аналіз всіх run_NNN.md)
835
- оркестратор при наступних запусках передає run_summary.md замість всіх run_NNN.md
836
- ```
837
-
838
- **Ліміт циклів:** після 3 поспіль `audit-result_*.md (result: failed)` — watch ескалює (TODO: через Telegram).
839
-
840
- **run_summary.md:** після `run_summary_threshold` (дефолт: 5) поспіль failed спроб — аудитор пише `run_summary.md`. Оркестратор передає його в context замість всіх `run_NNN.md` — збереження context window.
841
-
842
- Аудитор може використовувати дешевшу модель: `audit_model` у `.n-cursor.json` або per-node у `.n-cursor-override.json`.
843
-
844
- ---
845
-
846
- ## Wrapper-скрипт
847
-
848
- **Звичайний запуск** (`graph run <path> [--actor agent|engineer|human]`):
849
-
850
- > **Startup cleanup:** При старті `graph run <path>`: якщо є `running_<pid>_until_<ts>` → виконати `kill -0 <pid>` на PID з імені файлу. Якщо процес мертвий → cleanup running_* + worktree → продовжити старт. Якщо живий + `ts > now()` → skip (справді running). На distributed FS: якщо `ts + stale_grace_period_sec ≤ now()` → cleanup.
851
-
852
- 1. Читає `task.md` → `budget_sec`, `budget_hard_sec`, `interactive`; читає `a.md`/`h.md` → mode, `model_tier`, `skills`/`qualification`
853
- 2. `ls -R deps/` → список dep-id (strip `.md`); перевіряє що всі deps мають `fact_*.md` → `resolved`; якщо dep не має `fact_*.md` → стан `blocked-invalid-dep`, exit з помилкою
854
- 3. Перевіряє: є `pending-audit_*.md` без відповідного `audit-result_*.md` → **exit з помилкою** "audit pending, retry blocked"
855
- 4. `git worktree add .worktrees/<sanitized-path>-<epoch> main` (атомарний mkdir-lock)
856
- 4a. Пише `tasks/<node>/running_<pid>_until_<started_at + budget_hard_sec>` (git-ignored sentinel; ts = unix epoch UTC)
857
- 5. Визначає NNN = `count(run_*.md) + 1`
858
- 6. Запускає агента (cwd = worktree) з ENV:
859
- ```
860
- NCURSOR_BUDGET_SEC=<sec> NCURSOR_HARD_BUDGET_SEC=<sec|0> \
861
- NCURSOR_STARTED_AT=<unix> NCURSOR_RUN_NNN=<NNN> \
862
- claude --system-prompt .n-cursor/system-prompt.md \
863
- --message "solve task at task.md"
864
- ```
865
- Агент сам обчислює залишок: `remaining = started_at + budget_sec - now()`
866
- 7. Поллінгує worktree кожні 5 сек:
867
- - жодних змін `mtime` > `progress_timeout_sec` → SIGKILL + `result: progress-timeout`
868
- - elapsed > `budget_hard_sec` (якщо > 0) → SIGKILL + `result: budget-exceeded`
869
- - stalled check: `now() > ts + stall_grace_sec` та `kill -0 <pid>` fails → SIGKILL + `result: stalled`
870
- 8. Після виходу агента:
871
- - є `fact_NNN.md` (агент записав) → пише `run_NNN.md` з `result: success` + `## Ref → fact_NNN.md`
872
- - інакше → пише `run_NNN.md` з `result: failed`; агент мав писати `## Reasoning` + `## Partial Work`
873
- 9. `result: success` → **merge protocol** → видаляє `running_<pid>_until_*` → `touch .n-cursor/wake`
874
- 10. `result: failed` або `result: progress-timeout` → worktree залишається для debug → видаляє `running_<pid>_until_*`
875
-
876
- **Запуск аудитора** (`graph run --actor auditor <path>`):
877
-
878
- 1. Перевіряє наявність `pending-audit_NNN.md` без `audit-result_NNN.md` у main
879
- 2. `git worktree add .worktrees/<node>-audit-<epoch> main` → новий worktree з main
880
- (pending-audit_NNN.md і fact_NNN.md вже в main після merge агента)
881
- 3. Spawns auditor subprocess у цьому worktree
882
- 4. Чекає виходу → аудитор пише `audit-result_NNN.md`
883
- 5. Читає `result`:
884
- - `success` → **merge protocol** → `touch .n-cursor/wake`
885
- - `failed` → рахує існуючі `audit-result_*.md (result: failed)` у main:
886
- - < 3 → **merge protocol** → agent стартує новий worktree, бачить `audit-result_NNN.md`
887
- - ≥ 3 → worktree залишається, watch ескалює через Telegram
888
-
889
- **Merge protocol** (використовується скрізь — агент і аудитор):
890
- ```bash
891
- # атомарний push локальної гілки до main
892
- git push . <worktree-branch>:main
893
-
894
- # якщо rejected (HEAD змінився поки worktree працював):
895
- git -C .worktrees/<node>-<ts>/ merge main # завжди conflict-free — різні директорії
896
- git push . <worktree-branch>:main # retry
897
- # max 3 спроби; далі — error + worktree залишається
898
-
899
- # після успішного push:
900
- git worktree remove .worktrees/<node>-<ts>/
901
- git branch -d <worktree-branch>
902
- ```
903
-
904
- **Чому conflicts неможливі:** кожен вузол пише виключно у свою піддиректорію `tasks/<node>/`. Паралельні merge різних вузлів ніколи не торкаються спільних файлів.
905
-
906
- **Git hook** (`.git/hooks/post-merge`):
907
- ```bash
908
- #!/bin/sh
909
- n-cursor graph run --auto
910
- touch .n-cursor/wake
911
- ```
912
-
913
- ---
914
-
915
- ## `n-cursor watch`
916
-
917
- **Поточна реалізація:** простий periodic rescan раз на 5 хвилин (або по `touch .n-cursor/wake`). Не persistent daemon.
918
-
919
- ```bash
920
- n-cursor watch # запускає один scan-цикл; cron або loop-скрипт викликає кожні 5 хв
921
- ```
922
-
923
- **При кожному скані:**
924
- - `kill -0 <pid>` для кожного `running_<pid>_until_*` → якщо процес мертвий → cleanup + `run_NNN.md (result: failed, crash)`
925
- - Знаходить `stalled` вузли (`running_<pid>_until_<ts>`: `ts ≤ now() - grace_period` та `kill -0 <pid>` fails) → виводить попередження (TODO: Telegram)
926
- - Знаходить `pending-audit_NNN.md` без `audit-result_NNN.md` → `graph run --actor auditor`
927
- - Знаходить `failed` вузли (без активного процесу) → `graph run <path> --actor engineer`
928
- - Знаходить `resolved + orphan worktree` → idempotent merge + cleanup
929
- - Composite вузол: `graph done <child>` wrapper перевіряє чи всі siblings resolved → якщо так, пише `fact_NNN.md` батька автоматично (без окремого watch-кроку)
930
- - Знаходить `pending` (h.md) вузли → виводить нагадування + notify email з `h.md` (Telegram TODO)
931
- - Знаходить `unassigned` (немає a.md/h.md) → виводить нагадування (TODO: Telegram)
932
- - Знаходить `plan-review` вузли → пропускає (чекають approve від людини)
933
-
934
- **TODO (майбутній daemon):**
935
-
936
- | Умова | Повідомлення |
937
- |---|---|
938
- | `pending` (h.md) вузол > `stale_worktree_min` хв | потрібна участь людини |
939
- | ≥ 3 поспіль `audit-result_*.md (result: failed)` | audit loop — потрібна людина |
940
- | `actor: engineer, result: failed` на кореневому рівні | engineer budget exhausted |
941
- | `stalled` вузол (`running_<pid>_until_<ts>`, deadline + grace пройшли, PID не існує) | deadline перевищено — процес завис |
942
- | граф blocked (всі remaining: `unassigned`/`pending`+h.md або failed-deps) | граф заблокований |
943
- | вільне місце на диску < `min_free_disk_gb` | disk space alert |
944
-
945
- Конфіг: `stale_worktree_min` у `.n-cursor.json` (дефолт `30`).
946
-
947
- exit: `0` проблем немає | `1` є вузли що потребують уваги
948
-
949
- ---
950
-
951
- ## Протокол spawn (розкладання вузла на підграф)
952
-
953
- Агент вирішує "composite" в Stage 1 (`graph plan`). **Структуру підграфу визначає виключно агент** — оркестратор не втручається.
954
-
955
- **`graph spawn` вимагає `--mode agent|human`** — обов'язковий параметр. Без нього → validation error. Агент при composite плані зобов'язаний вказати `--mode` для кожного дочірнього вузла.
956
-
957
- ```
958
- graph plan <path> → decision: composite → стан plan-review:
959
- → людина: graph spawn --approve <path> --mode agent|human
960
- 1. для кожного дочірнього (з plan_NNN.md ## Approach):
961
- mkdir <node-id>/
962
- пише <node-id>/task.md ← місія
963
- touch <node-id>/a.md або h.md ← sentinel mode (відповідно до --mode)
964
- пише <node-id>/deps/<dep-node-id>.md ← по одному на кожну залежність (тільки siblings)
965
- 2. пише approved sentinel
966
- → або: graph spawn --reject <path> → видаляє plan_NNN.md → повернення до plan
967
- ```
968
-
969
- **Cross-level deps при spawn:** `deps/` підтримує вкладену структуру — сусіди (`deps/sibling.md`) або крос-рівневі (`deps/other-branch/node.md`). Оркестратор визначає dep-id через `ls -R deps/` + strip `.md`.
970
-
971
- **Динамічний spawn** (новий вузол під час виконання):
972
-
973
- ```
974
- агент (будь-коли в Stage 2) →
975
- mkdir <new-node-id>/
976
- пише <new-node-id>/task.md + a.md/h.md ← місія + mode
977
- пише <new-node-id>/deps/<dep-node-id>.md ← по одному (тільки siblings)
978
- graph spawn <шлях-вузла> --mode agent|human ← --mode обов'язковий
979
- ```
980
-
981
- Новий вузол підхоплюється оркестратором при наступному скануванні.
982
-
983
- ---
984
-
985
- ## Протокол патчу вузла що вже має залежних
986
-
987
- Перед тим як патчити `collect-data` (вже resolved) — інженер зобов'язаний:
988
-
989
- ```
990
- 1. знайти всі running worktrees наступників:
991
- collect-data → analyze (running) → synthesize (running)
992
- 2. kill від листів до цілі (топологічний порядок):
993
- graph kill synthesize (process + worktree + cascade invalidate)
994
- graph kill analyze
995
- 3. застосувати патч до collect-data у власному worktree
996
- 4. merge worktree
997
- 5. restart каскаду від collect-data
998
- ```
999
-
1000
- Інженер пише `run_NNN.md` (actor: engineer) з `## Reasoning` — єдиний запис наміру і результату.
1001
-
1002
- ---
1003
-
1004
- ## Паралельне виконання: ворктрі
1005
-
1006
- Незалежні вузли ОАГ виконуються паралельно — кожен агент у своєму git worktree.
1007
-
1008
- **Worktree-межа = межа атомарності.** Наступник стартує лише після merge попередника.
1009
-
1010
- **Кожен патч інженера — у своєму worktree.** Видаляється після завершення.
1011
-
1012
- ### Щасливий шлях
1013
-
1014
- Незалежні вузли пишуть у різні директорії — merge чисте, без конфліктів.
1015
-
1016
- ### Конфлікт при злитті
1017
-
1018
- ```
1019
- wrapper: git merge → конфлікт → result: failed (merge-conflict)
1020
- → worktree залишається для debug
1021
- → watch: EngineerAgent вирішує як звичайний failed вузол
1022
- ```
1023
-
1024
- Конфлікт злиття = звичайний `failed`. Той самий патерн відновлення що і при будь-якій іншій помилці.
1025
-
1026
- ### Ліміти worktree
1027
-
1028
- `agent_concurrency` у конфізі — ліміт паралельних агентських worktrees. Людські worktrees (h.md) не рахуються. `warn_worktrees_above` — поріг попередження. Черга очікування якщо ліміт вичерпано.
1029
-
1030
- ---
1031
-
1032
- ## Самовідновлення: агент-інженер
1033
-
1034
- Інженер — **мета-рівень поза графом**, без власного стану. Викликається при `стан = failed`.
1035
-
1036
- ```
1037
- EngineerAgent(run_NNN.md, full_path_from_root) →
1038
- GraphPatch:
1039
- ├── замінити вузол (atomic → composite або навпаки)
1040
- ├── вставити вузли (додати проміжні кроки)
1041
- ├── перепідключити ребра
1042
- └── змінити вхідні дані
1043
- ```
1044
-
1045
- Може змінювати **будь-який рівень** — від вузла що впав до кореня.
1046
-
1047
- ### Перевірка попередника перед стартом
1048
-
1049
- ```
1050
- predecessor/fact_*.md існує
1051
- → можна стартувати
1052
- → інакше (fact_*.md відсутній або вузол у waiting) — очікувати
1053
- ```
1054
-
1055
- ### Каскад інвалідації
1056
-
1057
- ```
1058
- patch(analyze) →
1059
- graph invalidate analyze →
1060
- архівує analyze/fact_*.md → analyze повертається у waiting
1061
- каскад: graph invalidate synthesize, report → архівує їх fact_*.md → ...
1062
- ```
1063
-
1064
- `graph kill` — завжди cascade: видаляє вузол і весь downstream через `git rm -r` + архів у `.history/`.
1065
-
1066
- **Differential cascade при re-run після invalidate:** `graph done` порівнює hash нового `fact_NNN.md` з попереднім (поле `hash: sha256:<hash секції ## Result>`). Однаковий hash → залежні вузли залишаються `resolved` (їх `fact_*.md` не зачіпається). Різний hash → каскад `graph invalidate` продовжується вниз.
1067
-
1068
- ---
1069
-
1070
- ## Протокол агента-інженера
1071
-
1072
- Інженер = той самий вузол, перезапущений з `--actor engineer`.
1073
-
1074
- ```
1075
- graph run <node-path> --actor engineer
1076
- ```
1077
-
1078
- 1. Wrapper збирає контекст: task-файл + `deps/*.md` + всі `run_NNN.md` відсортовані за `created_at`
1079
- 2. Запускає агента з `.n-cursor/engineer-prompt.md` (окремий system prompt)
1080
- 3. Агент читає контекст, визначає причину і план дій
1081
- 4. Якщо потрібен патч залежного вузла:
1082
- - `graph kill <dep-node>`
1083
- - Створює worktree, вносить зміни в `dep-node/task.md` (+ `a.md`/`h.md`)
1084
- - Мержить worktree
1085
- - Перезапускає залежний вузол
1086
- 5. Перезапускає себе якщо потрібно
1087
- 6. Wrapper пише `run_NNN.md` з `actor: engineer`
1088
-
1089
- Відмінності від звичайного агента: інший system prompt; увесь `run_NNN.md` history як контекст; дозволені `graph kill`, зміни через worktree.
1090
-
1091
- ---
1092
-
1093
- ## Збіжність: часовий бюджет
1094
-
1095
- Замість ліміту спроб — **часовий бюджет**. Інженер адаптує стратегію до залишку:
1096
-
1097
- - Багато часу — складна спроба
1098
- - Мало часу — швидке виправлення
1099
- - `залишок <= 0` — `стан = невирішуваний`, ескалація
1100
-
1101
- ---
1102
-
1103
- ## Ієрархія ескалації
1104
-
1105
- Кожен рівень отримує **свіжий** часовий бюджет:
1106
-
1107
- ```
1108
- вузол впав
1109
- → EngineerAgent (budget: 10хв)
1110
- → timeout → ескалація
1111
- → батько: EngineerAgent (свіжий budget: 10хв)
1112
- → ...
1113
- → корінь: timeout
1114
- → n-cursor watch репортить людині через Telegram
1115
- ```
1116
-
1117
- **Максимальний час до людини:** `глибина × бюджет`
1118
-
1119
- ---
1120
-
1121
- ## Конфігурація (`.n-cursor.json`)
1122
-
1123
- ```json
1124
- {
1125
- "tasks_dir": "./tasks",
1126
- "worktrees_dir": "./.worktrees",
1127
- "warn_worktrees_above": 4,
1128
- "agent_concurrency": 5,
1129
- "max_worktree_age": 14400,
1130
- "min_free_disk_gb": 10,
1131
- "default_budget_sec": 1800,
1132
- "default_budget_hard_sec": 3600,
1133
- "budget_hard_sec_multiplier": 3,
1134
- "progress_timeout_sec": 300,
1135
- "stall_grace_sec": 300,
1136
- "stderr_lines": 50,
1137
- "default_mode": "agent",
1138
- "default_model_tier": "AVG",
1139
- "plan_temperature": 0,
1140
- "run_summary_threshold": 5,
1141
- "claude_model": "claude-sonnet-4-6",
1142
- "audit_model": "claude-haiku-4-5-20251001",
1143
- "model_map": {
1144
- "MIM": "claude-haiku-4-5-20251001",
1145
- "AVG": "claude-sonnet-4-6",
1146
- "MAX": "claude-opus-4-8"
1147
- },
1148
- "stale_worktree_min": 30,
1149
- "system_prompt": ".n-cursor/system-prompt.md",
1150
- "graph_schema": 1,
1151
- "stale_grace_period_sec": 30,
1152
- "max_parallel_runs": 4,
1153
- "grace_period": 60
1154
- }
1155
- ```
1156
-
1157
- `agent_concurrency: 5` — ліміт паралельних **агентських** worktrees. Людські worktrees (actor: human, h.md) не рахуються і не мають ліміту. Watch перед spawn перевіряє: count(живих агентських worktrees з `a.md`) < `agent_concurrency` → spawn; інакше → queue до наступного тіку.
1158
-
1159
- `grace_period: 60` (секунд) — буфер для stalled detection. `stalled` якщо `ts + grace_period ≤ now()`. Покриває clock skew на distributed FS. Default: 60.
1160
-
1161
- Кожен task-файл (`task.md` + `a.md`/`h.md`) може перевизначити `budget_sec` локально.
1162
-
1163
- `budget_hard_sec: 0` заборонено — означає "використати global default" (`default_budget_hard_sec` з конфігу).
1164
-
1165
- Per-node override: `tasks/<node>/.n-cursor-override.json`
1166
-
1167
- ```json
1168
- {
1169
- "budget_sec": 7200,
1170
- "audit_model": "claude-opus-4-8",
1171
- "escalation_blacklist": ["pending-audit"]
1172
- }
1173
- ```
1174
-
1175
- ---
1176
-
1177
- ## Bootstrap
1178
-
1179
- ```bash
1180
- # 1. Ініціалізація проекту (один раз)
1181
- n-cursor graph setup
1182
- # → .n-cursor.json, .n-cursor/system-prompt.md, tasks/
1183
- # → .git/hooks/post-merge встановлено
1184
-
1185
- # 2. Запустити periodic scan (кожні 5 хв)
1186
- # cron: */5 * * * * n-cursor watch
1187
- # або loop: while true; do n-cursor watch; sleep 300; done
1188
-
1189
- # 3. Створення кореневого вузла
1190
- n-cursor graph init my-project \
1191
- --task "Розробити API для обробки платежів" \
1192
- --budget-sec 3600
1193
- # → tasks/my-project/task.md (unassigned — без a.md/h.md)
1194
- # Призначити виконавця: touch tasks/my-project/h.md (або a.md)
1195
-
1196
- # 4. Людина доповнює ## Inputs і ## Done when в task.md
1197
-
1198
- # 5. Stage 1: людина планує (mode: human)
1199
- n-cursor graph plan tasks/my-project/
1200
- # → plan_001.md
1201
-
1202
- # 6. Запуск
1203
- n-cursor graph run tasks/my-project/
1204
- ```
1205
-
1206
- Кореневий task-файл — немає `parent:`. Кореневий вузол не має `deps/`.
1207
-
1208
- ---
1209
-
1210
- ## Монорепо: множинні `tasks/` директорії
1211
-
1212
- **Bun монорепо** з кількома workspace-ами може мати окремий `tasks/` у кожному пакеті:
1213
-
1214
- ```
1215
- monorepo/
1216
- tasks/ ← глобальний tasks/ (cross-workspace завдання)
1217
- .history/
1218
- packages/
1219
- api/
1220
- tasks/ ← api-специфічні задачі
1221
- .history/
1222
- frontend/
1223
- tasks/
1224
- .history/
1225
- .worktrees/ ← завжди в git root (один для всіх workspace)
1226
- ```
1227
-
1228
- - `NCURSOR_TASKS_DIR` — вказує на конкретний `tasks/` для поточного контексту (env або CLI arg)
1229
- - Один `n-cursor watch` на один `tasks/` root — запускати окремий watch для кожного tasks/
1230
- - Worktrees — завжди в `.worktrees/` відносно git root (спільні для всіх workspace)
1231
- - `graph setup` у workspace-піддиректорії ініціалізує локальний `tasks/` без зміни кореневого
1232
-
1233
- ---
1234
-
1235
- ## Контракт для моніторингу
1236
-
1237
- Скрипт відновлює стан графу скануванням файлової структури:
1238
-
1239
- ```
1240
- для кожного tasks/**/task.md:
1241
- визначити стан (які файли існують поруч)
1242
- визначити залежності (ls deps/ — назви файлів = dep-id)
1243
- зібрати run_NNN.md, fact_NNN.md, plan_NNN.md, pending-audit_NNN.md, audit-result_NNN.md
1244
-
1245
- вивести:
1246
- - дерево вузлів зі станами
1247
- - вузли у стані failed (є run_*.md без fact_*.md і без активного worktree)
1248
- - вузли у стані pending (h.md) або unassigned (немає a.md/h.md)
1249
- - вузли у стані pending-audit (є pending-audit без audit-result)
1250
- - активні worktrees
1251
- - вузли у стані `waiting` (очікують виконання)
1252
- ```
1253
-
1254
- ---
1255
-
1256
- ## SWOT-аналіз
1257
-
1258
- ### Сильні сторони
1259
-
1260
- - **Інкапсуляція:** батько не знає що всередині — замінюваність без змін у батьківському графі
1261
- - **Файловий стан:** безкоштовна персистентність, відновлення після збоїв, повний аудит через git history
1262
- - **Immutable файли + numbered:** будь-який збій відновлюється скануванням `run_*.md` / `fact_*.md`
1263
- - **LLM-first формат:** `run_NNN.md` — інженер читає і продовжує природно
1264
- - **Worktree = межа атомарності:** наступник стартує лише після merge — гонки неможливі за архітектурою
1265
- - **Git-native паралельність:** worktree — вже знайомий інструмент
1266
- - **Симетрія:** конфлікт злиття = вузол що впав — той самий патерн відновлення
1267
- - **Часовий бюджет** замість лічильника — реалістичне обмеження
1268
- - **Audit-трек окремо:** `audit-result_NNN.md` не засмічує `run_NNN.md` виконавців
1269
-
1270
- ### Слабкі сторони
1271
-
1272
- - **Scan без індексу:** при великих графах сканування всіх task-файлів і `deps/` — дорого
1273
- - **Drift намірів:** після N патчів оригінальна місія розмивається (частково вирішено незмінним `## Task`)
1274
- - **Merge conflict** вимагає ручного або engineer-втручання — same as будь-який failed вузол
1275
- - **Масштаб worktree:** жорсткий ліміт на MacBook обмежує реальну паралельність
1276
-
1277
- ### Можливості
1278
-
1279
- - `run_NNN.md` накопичує знання — можна дистилювати в кращі промпти майбутніх агентів
1280
- - Git history = безкоштовний time-travel debugging всього графу
1281
- - Файлова система може бути розподіленою (NFS, S3) — горизонтальне масштабування
1282
- - Природна інтеграція з CI/CD через git hooks
1283
-
1284
- ### Загрози
1285
-
1286
- - **Cascade при зміні кореня:** інженер інвалідує весь граф і не вкладається у бюджет — система стоїть
1287
- - **LLM недетермінізм:** той самий вузол розкладається по-різному при перезапуску — ускладнює debugging
1288
- - **TODO (distributed):** clock skew при multiple runners на різних хостах — `running_<pid>_until_<ts>` перевірки можуть бути некоректні. WON'T FIX до distributed setup; при потребі: `grace_period_sec` у `.n-cursor.json`.
1289
-
1290
- ---
1291
-
1292
- ## Зведена таблиця рішень
1293
-
1294
- | Аспект | Рішення |
1295
- |---|---|
1296
- | Структура | Рекурсивний складений ОАГ |
1297
- | Декомпозиція | Динамічна, тільки в Stage 1 (graph plan) |
1298
- | Хто вирішує структуру підграфу | Тільки агент (LLM) |
1299
- | Ребра | Потік даних (виходи → входи); топологія у `deps/` кожного вузла |
1300
- | Центральний файл графу | Відсутній — оркестратор сканує `task.md` + `a.md`/`h.md` + `deps/` |
1301
- | Формат файлів | Markdown + YAML-фронтматер |
1302
- | Атрибути фронтматеру | Англійські, snake_case |
1303
- | Імена файлів і директорій | Англійська (обробляються скриптами) |
1304
- | Всі файли | Immutable — новий факт = новий файл (виняток: `running_<pid>_until_<ts>` — git-ignored ephemeral; `audit-result_NNN.md` — 1 перезапис при clarification) |
1305
- | schema_version | Перше поле у всіх YAML-фронтматерах; поточна версія `1`; оркестратор відмовляє при невідомій версії |
1306
- | deps/ naming | Файли з розширенням `.md`; `deps/` може бути вкладеною (дзеркалює `tasks/`); dep-id = відносний шлях від `deps/` без `.md`; `ls -R deps/` → список dep-id |
1307
- | Часовий бюджет | `budget_sec` (м'який, агент перевіряє через ENV) + `budget_hard_sec` (hard kill; 0=вимкнено) |
1308
- | Бюджет уточнення | `plan_NNN.md` може перекрити `budget_sec`/`budget_hard_sec`/`progress_timeout_sec` |
1309
- | Budget priority | `plan_NNN.md` > `.n-cursor-override.json` > task file > `.n-cursor.json` |
1310
- | ENV для агента | `NCURSOR_BUDGET_SEC`, `NCURSOR_HARD_BUDGET_SEC`, `NCURSOR_STARTED_AT`, `NCURSOR_RUN_NNN` |
1311
- | Progress watchdog | `progress_timeout_sec`: kill якщо немає змін `mtime` у worktree N сек |
1312
- | Stall detection | `running_<pid>_until_<ts>`: `ts + grace_period ≤ now()` та `kill -0 <pid>` fails → stalled |
1313
- | Stall grace | `grace_period` у конфізі (дефолт: 60s); покриває clock skew на distributed FS; PID check захист від race condition |
1314
- | Agent concurrency | `agent_concurrency` — ліміт агентських worktrees (h.md не рахуються); watch: count(агентських running) < agent_concurrency → spawn |
1315
- | Auditor worktree | Новий worktree з main (агент мержить при `graph audit`) |
1316
- | Актори виконавців | `agent` \| `engineer` \| `human` — поле `actor` у `run_NNN.md` |
1317
- | Актор аудиту | `auditor` — пише виключно `audit-result_NNN.md` |
1318
- | Нумерація | `run_NNN.md` (корінь, кожна спроба); якщо success → `fact_NNN.md` (той самий NNN); якщо `graph audit` → `pending-audit_NNN.md` (NNN fact) → `audit-result_NNN.md` (NNN pending) |
1319
- | NNN source | wrapper рахує `count(run_*.md) + 1` до старту; агент отримує через ENV `NCURSOR_RUN_NNN` |
1320
- | Plan artifact | `plan_NNN.md` (numbered, immutable; без `mode:` поля) — вихід Stage 1; секції: Context, Approach, Risks |
1321
- | Plan review | Composite план → стан `plan-review`; `graph spawn --approve` або `--reject`; без approve — без дочірніх |
1322
- | Plan temperature | `plan_temperature: 0` у конфізі для детермінованого планування |
1323
- | Refs | `ref: path#section` відносно поточного файлу; без копій даних |
1324
- | Стан вузла | Derived — виключно через listing файлів/директорій, без читання вмісту; `a.md`/`h.md` = хто; стан = що потрібно зробити |
1325
- | Стани | `unassigned`, `pending`, `waiting`, `blocked`, `running`, `stalled`, `pending-audit`, `children-done`, `resolved`, `failed` |
1326
- | Протокол агента | System prompt оркестратора (не у файлах вузла) |
1327
- | Ізоляція агента | Може читати файли будь-яких вузлів без обмежень |
1328
- | Відновлення після збою | Сканування `run_*.md` — вузли без `fact_*.md` = failed |
1329
- | Паралельність | Git worktree, по одному на вузол |
1330
- | Атомарність | Worktree-межа: наступник стартує лише після merge |
1331
- | Злиття | Git merge; конфлікт → `result: failed (merge-conflict)` → EngineerAgent |
1332
- | Межа immutability | До worktree — вільно; після — тільки нові файли |
1333
- | Патч залежного вузла | Kill наступників (топологічний порядок), потім патч |
1334
- | Самовідновлення | EngineerAgent — мета-рівень, необмежений доступ |
1335
- | Інвалідація | `graph invalidate`: архівує fact_*/run_* → history/; cascade вниз по нащадках. `graph kill`: archive + git rm вузла; `--no-cascade` — escape hatch |
1336
- | Deps blocked-invalid | Якщо dep не має `fact_*.md` → стан `blocked-invalid-dep`; skip виконання |
1337
- | Збіжність | Часовий бюджет (не кількість спроб) |
1338
- | Ескалація | Кожен рівень — свіжий budget; листок → батько → ... → корінь → Telegram |
1339
- | Composite resolved | `fact_NNN.md` є (unified — той самий механізм що й атомарний); O(1) перевірка |
1340
- | Composite fact | `graph done <child>` → wrapper перевіряє чи всі siblings resolved → пише `fact_NNN.md` батька (NNN = count+1; Summary = агрегація дітей); рекурсивно вгору |
1341
- | Аудит | Async черга: `graph audit` → `pending-audit_NNN.md`; watch dispatches `graph run --actor auditor` |
1342
- | Audit clarification | `needs-clarification` → агент пише `amended_NNN.md`; повторний аудит; лише 1 раз |
1343
- | Audit run_summary | Після `run_summary_threshold` failed спроб → аудитор пише `run_summary.md` |
1344
- | Pending audit | NNN = NNN відповідного `fact_NNN.md`; оброблено якщо ∃ `audit-result_NNN.md` |
1345
- | Audit result | NNN = NNN відповідного `pending-audit`; окремий трек від `run_NNN.md` |
1346
- | Audit ліміт | 3 failed cycles → watch ескалює через Telegram |
1347
- | Merge після аудиту | `graph run --actor auditor` wrapper; success → git merge + delete worktree |
1348
- | Orphan worktree | Наступний тік: resolved + orphan → idempotent merge |
1349
- | Оркестратор | `graph run --auto` (one-shot, post-merge hook) + `n-cursor watch` (periodic rescan, 5 хв) |
1350
- | Race condition | `mkdir .worktrees/<node>-<deadline-unix>/` — атомарна FS; `EEXIST` → skip |
1351
- | Default mode | `default_mode` у конфізі (дефолт: `agent`); `graph init` без `--mode` → пише `a.md`/`h.md` автоматично |
1352
- | Actor/Mode | `a.md` = агент (model_tier, skills); `h.md` = людина (qualification); немає = `unassigned`. Стан `waiting` = a.md є, deps resolved (runner обирає plan або run); `pending` = h.md є, runner пропускає |
1353
- | Spawn --mode | `graph spawn --mode agent\|human` — обов'язковий; без нього → validation error |
1354
- | mode: human headless | `--auto` і watch пропускають `h.md` (`pending`); людина запускає вручну через `graph run` |
1355
- | Watch роль | Periodic rescan (5 хв): черга аудиту + попередження; TODO: daemon + Telegram |
1356
- | Executor assessment | `a.md` = агент (`model_tier`, `skills`); `h.md` = людина (`qualification` TODO); немає = `unassigned` |
1357
- | Model tiers | MIM→haiku, AVG→sonnet (default), MAX→opus; задається через `model_map` у конфізі |
1358
- | Disk check | `min_free_disk_gb` у конфізі; watch alertує і skip при нестачі місця |
1359
- | plan_NNN.md нумерація | merged/active → продовжується; graph kill → видаляє plan_*.md → reset до 001 |
1360
- | Ліміти worktree | `agent_concurrency` — ліміт агентських worktrees; людські — без ліміту; `warn_worktrees_above` — поріг попередження; `min_free_disk_gb` для disk check |
1361
- | history/ структура | `tasks/<node>/history/<ts>-invalidate/` — архів після invalidate; `tasks/<node>/history/<ts>-audit-retry/` — архів audit-result; `<tasks-root>/.history/<ts>-kill-<path>/` — архів kill |
1362
- | graph migrate | Upgrade команда: читає `graph_schema` з конфігу → applies migration chain; обов'язковий при релізі нової версії |
1363
- | Монорепо | Кожен workspace має свій `tasks/`; `NCURSOR_TASKS_DIR` env вказує поточний; один watch на tasks/ root; worktrees спільні (git root) |
1364
- | Моніторинг | `graph scan` — відновлює граф зі стану файлів в будь-який момент |