@nitra/cursor 4.1.2 → 5.0.1
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/CHANGELOG.md +22 -0
- package/bin/docs/n-cursor.md +1 -9
- package/bin/n-cursor.js +3 -25
- package/package.json +1 -1
- package/rules/docker/lib/docs/docker-mirror.md +1 -1
- package/rules/docker/lib/docs/docker-native-addon.md +1 -1
- package/rules/npm-module/npm-module.mdc +1 -1
- package/rules/npm-module/policy/npm_publish_yml/template/npm-publish.yml.snippet.yml +1 -1
- package/rules/test/coverage/coverage.mjs +9 -19
- package/rules/test/test.mdc +1 -1
- package/scripts/dispatcher/trace.mjs +4 -16
- package/scripts/docs/build-agents-commands.md +1 -1
- package/scripts/docs/worktree-cli.md +1 -1
- package/scripts/lib/changed-files.mjs +19 -3
- package/scripts/lib/sync-gitignore-worktree.mjs +4 -5
- package/scripts/worktree-cli.mjs +1 -2
- package/skills/docgen/js/docgen-gen.mjs +7 -7
- package/docs/flow.MD +0 -1364
- package/scripts/dispatcher/docs/graph.md +0 -346
- package/scripts/dispatcher/docs/index.md +0 -236
- package/scripts/dispatcher/docs/trace.md +0 -296
- package/scripts/dispatcher/graph/lib/cmd-init.mjs +0 -112
- package/scripts/dispatcher/graph/lib/cmd-invalidate.mjs +0 -96
- package/scripts/dispatcher/graph/lib/cmd-kill.mjs +0 -141
- package/scripts/dispatcher/graph/lib/cmd-plan.mjs +0 -142
- package/scripts/dispatcher/graph/lib/cmd-run.mjs +0 -328
- package/scripts/dispatcher/graph/lib/cmd-scan.mjs +0 -115
- package/scripts/dispatcher/graph/lib/cmd-setup.mjs +0 -111
- package/scripts/dispatcher/graph/lib/cmd-signals.mjs +0 -328
- package/scripts/dispatcher/graph/lib/cmd-status.mjs +0 -131
- package/scripts/dispatcher/graph/lib/cmd-verify.mjs +0 -100
- package/scripts/dispatcher/graph/lib/cmd-watch.mjs +0 -128
- package/scripts/dispatcher/graph/lib/config.mjs +0 -103
- package/scripts/dispatcher/graph/lib/frontmatter.mjs +0 -224
- package/scripts/dispatcher/graph/lib/nnn.mjs +0 -127
- package/scripts/dispatcher/graph/lib/node-state.mjs +0 -157
- package/scripts/dispatcher/graph/lib/scanner.mjs +0 -235
- package/scripts/dispatcher/graph/lib/worktree-ops.mjs +0 -193
- package/scripts/dispatcher/graph-tasks.mjs +0 -92
- package/scripts/dispatcher/graph.mjs +0 -212
- package/scripts/dispatcher/index.mjs +0 -45
- package/scripts/dispatcher/lib/docs/active.md +0 -348
- package/scripts/dispatcher/lib/docs/artifact.md +0 -232
- package/scripts/dispatcher/lib/docs/budget.md +0 -167
- package/scripts/dispatcher/lib/docs/capability.md +0 -196
- package/scripts/dispatcher/lib/docs/commands.md +0 -210
- package/scripts/dispatcher/lib/docs/events.md +0 -183
- package/scripts/dispatcher/lib/docs/executor.md +0 -190
- package/scripts/dispatcher/lib/docs/gate.md +0 -231
- package/scripts/dispatcher/lib/docs/level.md +0 -335
- package/scripts/dispatcher/lib/docs/plan-panel.md +0 -181
- package/scripts/dispatcher/lib/docs/plan.md +0 -200
- package/scripts/dispatcher/lib/docs/planner.md +0 -269
- package/scripts/dispatcher/lib/docs/review.md +0 -255
- package/scripts/dispatcher/lib/docs/reviewer.md +0 -240
- package/scripts/dispatcher/lib/docs/snapshot.md +0 -247
- package/scripts/dispatcher/lib/docs/spec.md +0 -203
- package/scripts/dispatcher/lib/docs/state-store.md +0 -303
- package/scripts/dispatcher/lib/docs/subagent-runner.md +0 -173
- package/scripts/dispatcher/lib/events.mjs +0 -67
- package/scripts/dispatcher/lib/executor.mjs +0 -107
- package/scripts/dispatcher/lib/plan-panel.mjs +0 -76
- package/scripts/dispatcher/lib/state-store.mjs +0 -173
- package/scripts/dispatcher/lib/subagent-runner.mjs +0 -53
- package/scripts/graph/index.mjs +0 -115
- package/scripts/graph/lib/config.mjs +0 -62
- package/scripts/graph/lib/dag.mjs +0 -161
- package/scripts/graph/lib/frontmatter.mjs +0 -70
- package/scripts/graph/lib/nnn.mjs +0 -77
- package/scripts/graph/lib/state.mjs +0 -110
- package/scripts/graph/scan.mjs +0 -64
- 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` — відновлює граф зі стану файлів в будь-який момент |
|