opencode-orchestrator-uni 0.3.1 → 0.5.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.
- package/README.md +239 -10
- package/README.ru.md +240 -10
- package/agents/en/context-lint.md +90 -0
- package/agents/en/orchestrator.md +10 -0
- package/agents/ru/context-lint.md +90 -0
- package/agents/ru/orchestrator.md +10 -0
- package/docs/en/context-efficiency.md +196 -0
- package/docs/en/root-runbook.md +2 -0
- package/docs/en/session-discipline.md +230 -0
- package/docs/ru/context-efficiency.md +196 -0
- package/docs/ru/root-runbook.md +2 -0
- package/docs/ru/session-discipline.md +225 -0
- package/index.ts +45 -7
- package/lib/commands.ts +204 -0
- package/lib/context-lint.ts +2482 -0
- package/lib/report-schema.ts +36 -0
- package/lib/session-budget.ts +1236 -0
- package/lib/tools.ts +276 -9
- package/package.json +1 -1
- package/schemas/context-lint-failure.v1.json +29 -0
- package/schemas/context-lint.v1.json +58 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# Session discipline: how to run development
|
|
2
|
+
|
|
3
|
+
This document defines the session protocol: where a session starts and ends, its
|
|
4
|
+
context budget, when to delegate, how to treat tool output, and what survives the
|
|
5
|
+
session. It complements `context-efficiency.md` (the instruction-file budget) and
|
|
6
|
+
`root-runbook.md` (the audit loop): those cover artifacts and verdicts, this one
|
|
7
|
+
covers keeping context small and decisions pinned.
|
|
8
|
+
|
|
9
|
+
## Why it matters
|
|
10
|
+
|
|
11
|
+
- Context is a consumable resource, not the project's memory. As input grows,
|
|
12
|
+
quality degrades even on simple tasks (context rot); "it fits the window" does
|
|
13
|
+
not mean "the model uses it".
|
|
14
|
+
- Compaction is a lossy summary. After it the agent remembers a paraphrase of
|
|
15
|
+
decisions, not the decisions; a second compaction paraphrases a paraphrase.
|
|
16
|
+
- A measured long session: 13 253 turns, 19 compactions, the median prompt grew
|
|
17
|
+
from 314k to 780k tokens. Almost all of that is re-sending history, not work.
|
|
18
|
+
This is an internal measurement of a consumer project: the artifact is not
|
|
19
|
+
attached to the repository, and the figures are not reproducible from it.
|
|
20
|
+
|
|
21
|
+
The protocol keeps the working state small and the decisions in files that
|
|
22
|
+
outlive the session.
|
|
23
|
+
|
|
24
|
+
## 1. A session is one slice
|
|
25
|
+
|
|
26
|
+
A session is one slice with a measurable DoD, not the whole project.
|
|
27
|
+
|
|
28
|
+
| Rule | Value |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| Estimated prompt budget | ≤ 150k tokens |
|
|
31
|
+
| Compactions per slice | ≤ 1 |
|
|
32
|
+
| Stop signal | the first compaction |
|
|
33
|
+
| Required output | handoff + commit |
|
|
34
|
+
|
|
35
|
+
- At the first sign of approaching the budget, close the slice instead of pushing
|
|
36
|
+
on: handoff, commit, new session.
|
|
37
|
+
- After the first compaction the slice always closes: the summary has already lost
|
|
38
|
+
details and further work runs on a paraphrase.
|
|
39
|
+
- Cut work by waves, gates or fix batches; a phase change (plan → implementation →
|
|
40
|
+
acceptance) always starts a new session.
|
|
41
|
+
|
|
42
|
+
## 2. Role separation
|
|
43
|
+
|
|
44
|
+
- Investigation and verification ("find", "understand", "reproduce") go to
|
|
45
|
+
subagents: they get a clean context and return 1–2k tokens of findings instead
|
|
46
|
+
of a dump.
|
|
47
|
+
- Implementation goes to one writer per workspace: related decisions must be made
|
|
48
|
+
in one context (otherwise parallel agents diverge in style and contracts).
|
|
49
|
+
- Decisions, consolidation and acceptance belong to the root orchestrator.
|
|
50
|
+
- The audit loop is unchanged: re-audit resumes the same sessions; short audit
|
|
51
|
+
sessions do not threaten the budget.
|
|
52
|
+
|
|
53
|
+
Rule of thumb: delegate **investigation** broadly, **implementation** narrowly,
|
|
54
|
+
keep **integration** centralized.
|
|
55
|
+
|
|
56
|
+
## 3. Tool batching
|
|
57
|
+
|
|
58
|
+
Every turn re-sends the whole prompt. One tool per turn multiplies the budget by
|
|
59
|
+
the number of small steps.
|
|
60
|
+
|
|
61
|
+
- ≥ 3 independent commands per turn, or one script instead of five to ten calls.
|
|
62
|
+
- Move long WSL commands into a wrapper script: a 250-character line in every turn
|
|
63
|
+
costs more than a file read once.
|
|
64
|
+
- For file reads use `read` with `limit`/`offset`, not a full `cat`.
|
|
65
|
+
- Target metric: ≥ 2 tools per turn.
|
|
66
|
+
|
|
67
|
+
## 4. Tool output
|
|
68
|
+
|
|
69
|
+
- Anything over 20k chars goes to a file; context gets the head (4k) and the path.
|
|
70
|
+
The guard does this automatically for `shell`, `bash`, `webfetch`.
|
|
71
|
+
- Screenshots and binary attachments are files plus a path, never base64 in
|
|
72
|
+
context.
|
|
73
|
+
- `execute` (Code Mode) is not capped by the guard: limit the result in the script
|
|
74
|
+
itself — return a summary and a path, not hundreds of kilobytes of raw JSON.
|
|
75
|
+
- `read`, `write`, `edit` and the kit's own tools are not capped: they have their
|
|
76
|
+
own contract, and losing content breaks the work.
|
|
77
|
+
|
|
78
|
+
## 5. State in files
|
|
79
|
+
|
|
80
|
+
- `handoff` — the required output of a slice (template below); default path
|
|
81
|
+
`docs/planning/handoff-<YYYY-MM-DD>.md`, the project root when the directory
|
|
82
|
+
does not exist.
|
|
83
|
+
- `decisions` — decisions and reasons; one decision per entry; default path
|
|
84
|
+
`docs/planning/decisions-<YYYY-MM-DD>.md`.
|
|
85
|
+
- `state` — branch, gates, open risks, the next three steps; refreshed before a
|
|
86
|
+
new session; default path `docs/planning/state.md`.
|
|
87
|
+
- A new session starts from `state` and the target SPEC section, not from a
|
|
88
|
+
retelling of the previous session.
|
|
89
|
+
|
|
90
|
+
## 6. Models
|
|
91
|
+
|
|
92
|
+
- Default to the cheap model; the expensive one only for architecture decisions,
|
|
93
|
+
reviews and dispute resolution.
|
|
94
|
+
- Document edits, reading and mechanical checks do not need an expensive model.
|
|
95
|
+
- Measurement: 223 turns of the expensive model cost $63, 104 turns of the cheap
|
|
96
|
+
one cost $0.09. One model switch saves more than any prompt tweak. This is an
|
|
97
|
+
internal measurement of a consumer project: the artifact is not attached to the
|
|
98
|
+
repository, and the figures are not reproducible from it.
|
|
99
|
+
|
|
100
|
+
## 7. Metrics
|
|
101
|
+
|
|
102
|
+
| Metric | Target | Where |
|
|
103
|
+
| --- | --- | --- |
|
|
104
|
+
| Estimated prompt (median) | ≤ 150k | `orch_uni_session_stats` |
|
|
105
|
+
| Compactions per slice | ≤ 1 | `orch_uni_session_stats` |
|
|
106
|
+
| Tools per turn (`toolCalls/requests`, reported as `toolsPerTurn`) | ≥ 2 | `orch_uni_session_stats` |
|
|
107
|
+
| Share of capped outputs among the tool's calls (`toolOutputsCappedByTool.shell / toolCallsByTool.shell`; likewise `bash`, `webfetch`) | < 5% | `orch_uni_session_stats` |
|
|
108
|
+
| Handoff at slice end | present | working tree |
|
|
109
|
+
|
|
110
|
+
`orch_uni_session_stats` reports the counters of the current plugin instance and
|
|
111
|
+
the configured limits; historical runs are counted from the session database on
|
|
112
|
+
the project side.
|
|
113
|
+
|
|
114
|
+
## 8. Guard
|
|
115
|
+
|
|
116
|
+
The guard is a set of plugin hooks plus a compaction-event subscription, not a
|
|
117
|
+
set of bans:
|
|
118
|
+
|
|
119
|
+
- `session.context` — estimates the prompt and adds one short reminder when it
|
|
120
|
+
crosses a budget level. The reminder is injected once at a level transition,
|
|
121
|
+
not on every request; within a level the text is stable, so the prefix cache is
|
|
122
|
+
not broken.
|
|
123
|
+
- compaction events — the `session.compaction` hook is registered on every host:
|
|
124
|
+
it counts attempts (`compactionAttempts`) and sends a tentative note to the
|
|
125
|
+
nearest request when the terminal event is delayed; the note names no
|
|
126
|
+
compaction number and claims no limit. Only `session.compaction.ended`
|
|
127
|
+
confirms a compaction (deduplicated by `event.id` or the durable position);
|
|
128
|
+
only it moves `compactions` and the limit. `session.compaction.failed`
|
|
129
|
+
increments `compactionFailures`, drops the tentative note and keeps a
|
|
130
|
+
confirmed note that is still waiting for delivery. When the event stream ends
|
|
131
|
+
or throws, the guard reports `eventLive: false`, `compactionSource: "hook"` and
|
|
132
|
+
`streamFailures: 1` (the per-session `errors` stays `0`); the hook is already
|
|
133
|
+
registered, so attempts keep counting, while no confirmed compaction or stop
|
|
134
|
+
signal is invented — no false "limit reached" on that path. `guard` exposes
|
|
135
|
+
the source, liveness and stream failures.
|
|
136
|
+
- `tool.execute.after` — caps the output of the configured tools and stores the
|
|
137
|
+
full text in a private per-process root
|
|
138
|
+
`os.tmpdir()/opencode-tool-output-<random>/` (POSIX 0700 for the root and the
|
|
139
|
+
session directory, 0600 for the file). Windows ignores mode (measured: 0666 for
|
|
140
|
+
the root, the session directory and the file): privacy there rests on the ACL of
|
|
141
|
+
`os.tmpdir()` and is not checked by code. The root is created
|
|
142
|
+
with `mkdtemp` at setup, so a pre-created directory or junction named
|
|
143
|
+
`opencode-tool-output` is never used; on POSIX the root must belong to the
|
|
144
|
+
current uid with no group/other access, otherwise the guard disables itself with
|
|
145
|
+
a warning (fail-open: the result is untouched). Session directories (`s-<uuid>`)
|
|
146
|
+
and files (`c-<uuid>.txt`, written with `wx`) are random, and each `realpath` is
|
|
147
|
+
proven inside the root; the root is removed when the plugin unloads (best
|
|
148
|
+
effort). A string with a lone surrogate is normalized once (U+FFFD), so the
|
|
149
|
+
file, the head and the counters describe the same string. Residual risk
|
|
150
|
+
accepted: a same-user attacker can still swap a session
|
|
151
|
+
directory between `realpath` and `writeFile` (Node has no `openat`). If `TMPDIR`
|
|
152
|
+
points inside the workspace, the files land there.
|
|
153
|
+
|
|
154
|
+
Configuration (defaults):
|
|
155
|
+
|
|
156
|
+
```jsonc
|
|
157
|
+
{
|
|
158
|
+
"sessionGuard": {
|
|
159
|
+
"enabled": true,
|
|
160
|
+
"promptTokensSoft": 150000,
|
|
161
|
+
"maxCompactions": 1
|
|
162
|
+
},
|
|
163
|
+
"toolOutputGuard": {
|
|
164
|
+
"enabled": true,
|
|
165
|
+
"maxChars": 20000,
|
|
166
|
+
"headChars": 4000,
|
|
167
|
+
"tools": ["shell", "bash", "webfetch"]
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`maxCompactions: 0` disables the compaction limit. `guard.toolOutputEnabled` is
|
|
173
|
+
`true` only when the private root exists **and** the effective tool list is not
|
|
174
|
+
empty; `guard.toolOutputTools` is that list (normalized, protected and kit names
|
|
175
|
+
removed) and `guard.unkeyedCalls` counts hook calls without a non-empty
|
|
176
|
+
`sessionID` (such input is dropped, not bucketed under `""`). An empty `tools`
|
|
177
|
+
list — explicit or after every entry was rejected — disables capping with a
|
|
178
|
+
warning. Accepted limits: a session directory `s-<uuid>` lives until the plugin
|
|
179
|
+
unloads — it is removed neither on `session.deleted` nor after a refused cap
|
|
180
|
+
(a readonly result, for example) and is released with the root; lone-surrogate
|
|
181
|
+
normalization relies on ES2024 `String.prototype.isWellFormed`/`toWellFormed`
|
|
182
|
+
(the minimum runtime version is not pinned in `engines`, so on an old runtime the
|
|
183
|
+
cap degrades fail-open: `errors` +1, the result is untouched).
|
|
184
|
+
In the counters, `toolCalls`
|
|
185
|
+
and the per-tool counters include nested Code Mode calls, and `outputCharsSaved`
|
|
186
|
+
is an upper estimate in characters (the injected stub is subtracted).
|
|
187
|
+
`toolCallsByTool` and `toolOutputsCappedByTool` split calls and capped outputs by
|
|
188
|
+
the normalized tool name (namespace stripped, lower-case). Duplicate compaction
|
|
189
|
+
events are counted once only when the host supplies `event.id` or a durable
|
|
190
|
+
position; without either identity a re-delivered event may be counted again
|
|
191
|
+
(fail-open in the direction of counting).
|
|
192
|
+
|
|
193
|
+
The guard is fail-open: a hook error neither breaks the request nor corrupts the
|
|
194
|
+
tool result. It writes nothing to the working tree (except when `TMPDIR` points
|
|
195
|
+
inside the workspace — see above) and blocks nothing — it only reminds and caps
|
|
196
|
+
output. Either part can be disabled with its `enabled` flag.
|
|
197
|
+
|
|
198
|
+
## Handoff template
|
|
199
|
+
|
|
200
|
+
```md
|
|
201
|
+
# Handoff: <slice> — <YYYY-MM-DD>
|
|
202
|
+
|
|
203
|
+
<!-- File: docs/planning/handoff-<YYYY-MM-DD>.md; use the project root when the directory is absent. -->
|
|
204
|
+
|
|
205
|
+
## Goal and DoD
|
|
206
|
+
One sentence of the goal and a verifiable definition of done.
|
|
207
|
+
|
|
208
|
+
## Done
|
|
209
|
+
- What changed; what proves it (command, test, commit).
|
|
210
|
+
|
|
211
|
+
## Not done
|
|
212
|
+
- What remains and why (blocker, owner decision, next wave).
|
|
213
|
+
|
|
214
|
+
## Decisions
|
|
215
|
+
- Decision — reason; link to the document or commit.
|
|
216
|
+
|
|
217
|
+
## Risks and limits
|
|
218
|
+
- Accepted risk — by whom and until when.
|
|
219
|
+
|
|
220
|
+
## Next step
|
|
221
|
+
One concrete action for the next session.
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Slice checklist
|
|
225
|
+
|
|
226
|
+
- [ ] The slice is closed by DoD, not by "ran out of time".
|
|
227
|
+
- [ ] The handoff is written and references evidence.
|
|
228
|
+
- [ ] Decisions and risks are in files, not only in chat.
|
|
229
|
+
- [ ] The working tree is clean or explicitly described in the handoff.
|
|
230
|
+
- [ ] The next session starts from `state` and SPEC, not from a retelling.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Token-efficient AGENTS.md и SPEC.md
|
|
2
|
+
|
|
3
|
+
Файл инструкций — это бюджет внимания, а не документация. Оплачивается каждая строка: она участвует в контексте каждой сессии и конкурирует с задачей. Цель — минимум проверяемых правил, которые нельзя вывести из кода и которые нужны в большинстве сессий.
|
|
4
|
+
|
|
5
|
+
## Бюджет
|
|
6
|
+
|
|
7
|
+
| Файл | Цель | Потолок |
|
|
8
|
+
|---|---:|---:|
|
|
9
|
+
| Глобальный `AGENTS.md` | 25–40 строк / <500 токенов | 60 строк |
|
|
10
|
+
| Корневой `AGENTS.md` | 100–200 строк / 1–2k токенов | 200 строк |
|
|
11
|
+
| Вложенный `AGENTS.md` | 20–60 строк / <700 токенов | 100 строк |
|
|
12
|
+
| `SPEC.md` | 80–150 строк / ≤1,5k слов | 2k слов |
|
|
13
|
+
| `ADR` | 30–60 строк / <700 токенов | 100 строк |
|
|
14
|
+
|
|
15
|
+
`orch_uni_context_lint` применяет один бюджет корня (`max_lines`, по умолчанию 200 строк) и бюджет слов SPEC (`max_words`, по умолчанию 1500 — цель из таблицы; потолок 2k включается через `max_words: 2000`). Цифры по уровням выше — ориентир для автора, а не отдельные лимиты инструмента. Инструмент сообщает о том, что не проверял (ссылки вне корня, каталоги глубже глубины 3, незакрытый фенс), INFO-находками, поэтому `pass` никогда не означает «непроверено».
|
|
16
|
+
|
|
17
|
+
Правило отбора: если строка не проверяема, выводима из кода/линтера или нужна реже половины сессий — удалить либо вынести в ссылку.
|
|
18
|
+
|
|
19
|
+
## AGENTS.md: 12 правил
|
|
20
|
+
|
|
21
|
+
1. Команды — в начало: install, build, test, one test, lint, run; с флагами и рабочей директорией.
|
|
22
|
+
2. Только невыводимое: флаги, ловушки, порядок слоёв, запреты, версии.
|
|
23
|
+
3. Одна инструкция — одна строка, императив, проверяемо.
|
|
24
|
+
4. Стиль — линтеру; не пересказывать README и стандарт языка.
|
|
25
|
+
5. Явные «Never» и «Ask first»: секреты, `dist/`, prod, миграции, зависимости.
|
|
26
|
+
6. Приоритеты — в тексте: что переопределяет что; при конфликте — остановись и спроси.
|
|
27
|
+
7. Один источник правды: `AGENTS.md` + симлинк/импорт для `CLAUDE.md`.
|
|
28
|
+
8. Ссылки вместо инлайна; инлайнить только то, что нужно всегда.
|
|
29
|
+
9. Правило добавлять после повторной ошибки агента, а не «на будущее».
|
|
30
|
+
10. Мёртвое правило удалять: это налог на каждый запрос.
|
|
31
|
+
11. Стабильное — вверх, даты/ветки/задачи — вниз (prefix cache).
|
|
32
|
+
12. Ревьюить файл как код; менять при смене тулинга/CI.
|
|
33
|
+
|
|
34
|
+
## Шаблон AGENTS.md
|
|
35
|
+
|
|
36
|
+
```md
|
|
37
|
+
# AGENTS.md
|
|
38
|
+
|
|
39
|
+
## Приоритеты
|
|
40
|
+
1. Явное указание пользователя в задаче.
|
|
41
|
+
2. SPEC.md активной фичи.
|
|
42
|
+
3. Ближайший AGENTS.md.
|
|
43
|
+
4. Этот файл.
|
|
44
|
+
5. Глобальный AGENTS.md.
|
|
45
|
+
При конфликте — остановись и спроси.
|
|
46
|
+
|
|
47
|
+
## Команды
|
|
48
|
+
| Задача | Команда | Примечание |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| Установка | `<...>` | `<версия>` |
|
|
51
|
+
| Сборка | `<...>` | `<флаг>` |
|
|
52
|
+
| Тесты | `<...>` | `<лимит>` |
|
|
53
|
+
| Один тест | `<...>` | `<имя>` |
|
|
54
|
+
| Линт/типы | `<...>` | |
|
|
55
|
+
| Запуск | `<...>` | `<порт>` |
|
|
56
|
+
|
|
57
|
+
## Границы
|
|
58
|
+
- `<path>/` — сгенерировано, не редактировать.
|
|
59
|
+
- `<path>/` — вендорное, не редактировать.
|
|
60
|
+
- Новые файлы — в `<path>`.
|
|
61
|
+
|
|
62
|
+
## Конвенции
|
|
63
|
+
- <Одно проверяемое правило на строку.>
|
|
64
|
+
|
|
65
|
+
## Проверка
|
|
66
|
+
`<analyze>` → `<lint>` → `<test>`.
|
|
67
|
+
Готово = все команды зелёные + `<DoD>`.
|
|
68
|
+
|
|
69
|
+
## Никогда / сначала спроси
|
|
70
|
+
- Никогда: секреты, `dist/`, prod, применённые миграции.
|
|
71
|
+
- Сначала спроси: схема БД, публичный API, новые зависимости, CI.
|
|
72
|
+
|
|
73
|
+
## Указатели
|
|
74
|
+
- `SPEC.md` — контракт фичи.
|
|
75
|
+
- `docs/adr/` — решения.
|
|
76
|
+
- `docs/pitfalls.md` — ловушки.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## SPEC.md: 12 правил
|
|
80
|
+
|
|
81
|
+
1. Одна фича — один файл, 1–3 страницы; больше — split через roadmap.
|
|
82
|
+
2. Стабильные ID (`R1`, `AC1.1`), не перенумеровывать.
|
|
83
|
+
3. Одно требование — одно наблюдаемое поведение.
|
|
84
|
+
4. EARS или Given/When/Then — единообразно; MUST/SHOULD/MAY — редко.
|
|
85
|
+
5. Никакого HOW: стек, файлы, классы — в plan/design/ADR.
|
|
86
|
+
6. Контракты — ссылками, не копиями.
|
|
87
|
+
7. Non-goals — 3–7 пунктов.
|
|
88
|
+
8. Инварианты — отдельно от acceptance criteria.
|
|
89
|
+
9. Таблицы — для AC и трассировки; проза — для контекста.
|
|
90
|
+
10. Frontmatter: `status`, `owner`, `version`, `supersedes`; без changelog в теле.
|
|
91
|
+
11. Не более трёх открытых вопросов, у каждого owner и дата; `TBD` в других местах запрещён.
|
|
92
|
+
12. DoD и метрики — измеримые; «быстрее» без числа не считается.
|
|
93
|
+
|
|
94
|
+
## Шаблон SPEC.md
|
|
95
|
+
|
|
96
|
+
```md
|
|
97
|
+
---
|
|
98
|
+
id: SPEC-000
|
|
99
|
+
title: <название>
|
|
100
|
+
status: draft
|
|
101
|
+
owner: "@team"
|
|
102
|
+
version: 1.0.0
|
|
103
|
+
supersedes: —
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
# SPEC-000: <название>
|
|
107
|
+
|
|
108
|
+
## 1. Контекст и проблема
|
|
109
|
+
<3–5 строк: кто страдает, в чём боль, почему сейчас.>
|
|
110
|
+
|
|
111
|
+
## 2. Цель и non-goals
|
|
112
|
+
**Цель:** <одно предложение>.
|
|
113
|
+
**Non-goals:** <3–7 пунктов «сознательно не делаем»>.
|
|
114
|
+
<!-- один пункт на строку с `-` и отступом 0–4; перенесённый абзац — один пункт;
|
|
115
|
+
фенс, `<pre>` и bullet с отступом 5+ — код, не пункт. -->
|
|
116
|
+
|
|
117
|
+
## 3. Сценарии
|
|
118
|
+
| ID | Роль | Цель | Ценность | Независимый тест |
|
|
119
|
+
|---|---|---|---|---|
|
|
120
|
+
| S1 | <...> | <...> | <...> | <...> |
|
|
121
|
+
|
|
122
|
+
## 4. Требования (EARS)
|
|
123
|
+
- **R1** — WHEN <триггер>, THE SYSTEM SHALL <результат>.
|
|
124
|
+
- **R2** — IF <условие>, THEN THE SYSTEM SHALL <...>.
|
|
125
|
+
|
|
126
|
+
## 5. Acceptance criteria
|
|
127
|
+
| AC | R | Given | When | Then | Test |
|
|
128
|
+
|---|---|---|---|---|---|
|
|
129
|
+
| AC1.1 | R1 | <...> | <...> | <...> | `test_...` |
|
|
130
|
+
|
|
131
|
+
## 6. Инварианты и краевые случаи
|
|
132
|
+
- Инвариант: <всегда истинно>.
|
|
133
|
+
- Пусто/лимит/конкурентность/ошибка: <...>.
|
|
134
|
+
|
|
135
|
+
## 7. Контракты
|
|
136
|
+
Ссылки: `<openapi.yaml>`, `<schema.sql>`, `<types.ts>`.
|
|
137
|
+
|
|
138
|
+
## 8. Нефункциональные требования
|
|
139
|
+
| Метрика | Цель | Как проверяем |
|
|
140
|
+
|---|---|---|
|
|
141
|
+
| Latency p95 | <...> | <...> |
|
|
142
|
+
|
|
143
|
+
## 9. Метрики успеха
|
|
144
|
+
- **SC-1** — <число и способ проверки>.
|
|
145
|
+
|
|
146
|
+
## 10. Открытые вопросы
|
|
147
|
+
- **Q1** — <...>; owner: <...>; до <дата>.
|
|
148
|
+
|
|
149
|
+
## 11. Трассировка
|
|
150
|
+
| R | AC | Task | Test |
|
|
151
|
+
|---|---|---|---|
|
|
152
|
+
| R1 | AC1.1 | T1 | `test_...` |
|
|
153
|
+
|
|
154
|
+
## 12. Definition of Done
|
|
155
|
+
- [ ] AC закрыты.
|
|
156
|
+
- [ ] Тесты зелёные.
|
|
157
|
+
- [ ] Контракты и docs/ADR обновлены.
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Команда /agents-md
|
|
161
|
+
|
|
162
|
+
1. Определи режим: аудит существующего файла или черновик с нуля.
|
|
163
|
+
2. Для аудита: `orch_uni_context_lint` → `context-lint`; покажи SGR и `closing_list`.
|
|
164
|
+
3. Для черновика: собери факты и задай не более 8 вопросов; не выдумывай команды и пути.
|
|
165
|
+
4. Заполни шаблон; уложись в 150–200 строк.
|
|
166
|
+
5. Прогони линтер, покажи diff и открытые вопросы.
|
|
167
|
+
6. Канонический файл меняй только после подтверждения; правки — через `fix-runner`.
|
|
168
|
+
|
|
169
|
+
## Команда /spec
|
|
170
|
+
|
|
171
|
+
1. Зафиксируй бриф и задай не более 5 уточняющих вопросов.
|
|
172
|
+
2. Non-goals, приоритеты и метрики утверждает владелец; не придумывай их.
|
|
173
|
+
3. Заполни шаблон: EARS, AC с тестами, инварианты, трассировка, DoD.
|
|
174
|
+
4. Прогони `orch_uni_context_lint` и `context-lint`; покажи diff и вопросы.
|
|
175
|
+
5. Канонический файл меняй только после подтверждения.
|
|
176
|
+
|
|
177
|
+
## Ревью
|
|
178
|
+
|
|
179
|
+
- [ ] `AGENTS.md` ≤200 строк, `SPEC.md` ≤1,5k слов.
|
|
180
|
+
- [ ] Каждая строка проверяема.
|
|
181
|
+
- [ ] Нет дублей с README/линтером/CI.
|
|
182
|
+
- [ ] Приоритеты и границы прописаны.
|
|
183
|
+
- [ ] Все относительные ссылки существуют.
|
|
184
|
+
- [ ] У каждого R есть AC, у каждого AC есть тест.
|
|
185
|
+
- [ ] Non-goals и DoD на месте.
|
|
186
|
+
- [ ] Есть baseline на 10–20 задачах.
|
|
187
|
+
|
|
188
|
+
## Анти-паттерны
|
|
189
|
+
|
|
190
|
+
| Анти-паттерн | Почему плохо | Что делать |
|
|
191
|
+
|---|---|---|
|
|
192
|
+
| Автогенерация «на всякий случай» | −0,5…−2% успеха, +20–23% косты | Писать вручную, только высокосигнальное |
|
|
193
|
+
| Пересказ README/архитектуры | Дублирование, нулевой эффект | Ссылка на docs |
|
|
194
|
+
| Дублирование линтера | Токены на то, что и так проверяется | Линтер/CI |
|
|
195
|
+
| Общие фразы («пиши чисто») | Непроверяемо | Императив + пример |
|
|
196
|
+
| Противоречия между уровнями | Модель выбирает произвольно | Явные приоритеты |
|
package/docs/ru/root-runbook.md
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
- **Root (главный агент)** — не аудирует: формирует бриф, запускает субагентов, сводит отчёты; по «недоказуемым» — решение владельца: root оформляет вопрос, фиксирует риск и эскалирует. Ведёт реестр принятых рисков, вносит правки отдельными коммитами, решает «ship».
|
|
6
6
|
- **Субагенты** (read-only): `factcheck`, `empiric`, `invariant`, `arbiter`, (на воротах) `redteam`, `meta`.
|
|
7
7
|
|
|
8
|
+
Сессионная гигиена среза — границы, бюджет контекста (≤150k токенов, ≤1 компакция) и handoff — в `session-discipline.md`.
|
|
9
|
+
|
|
8
10
|
## Шаблон брифа `case-<id>.md` (общий для всех шагов)
|
|
9
11
|
|
|
10
12
|
```
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Гигиена сессии: как вести разработку
|
|
2
|
+
|
|
3
|
+
Документ задаёт протокол работы сессии: где её границы, какой бюджет контекста,
|
|
4
|
+
когда делегировать, как обращаться с выводом инструментов и что остаётся после
|
|
5
|
+
сессии. Он дополняет `context-efficiency.md` (бюджет файлов инструкций) и
|
|
6
|
+
`root-runbook.md` (аудиторский цикл): там — про артефакты и вердикты, здесь —
|
|
7
|
+
про то, как не сжечь контекст и не потерять решения по дороге.
|
|
8
|
+
|
|
9
|
+
## Зачем это нужно
|
|
10
|
+
|
|
11
|
+
- Контекст — расходуемый ресурс, а не память проекта. С ростом входа качество
|
|
12
|
+
падает даже на простых задачах (context rot); «влезло в окно» не значит
|
|
13
|
+
«модель это использует».
|
|
14
|
+
- Компакция — lossy-пересказ. После неё агент помнит не решения, а их краткое
|
|
15
|
+
изложение; повторная компакция пересказывает пересказ.
|
|
16
|
+
- Замер длинной сессии: 13 253 хода, 19 компакций, медиана промпта выросла с
|
|
17
|
+
314k до 780k токенов. Почти всё это — повторная пересылка истории, а не работа.
|
|
18
|
+
Это внутренний замер проекта-потребителя: артефакт в репозиторий не приложен,
|
|
19
|
+
из этого репозитория цифры не воспроизводятся.
|
|
20
|
+
|
|
21
|
+
Цель протокола — держать рабочее состояние маленьким, а решения — в файлах,
|
|
22
|
+
которые переживают сессию.
|
|
23
|
+
|
|
24
|
+
## 1. Сессия = срез
|
|
25
|
+
|
|
26
|
+
Сессия — это один срез с измеримым DoD, а не весь проект.
|
|
27
|
+
|
|
28
|
+
| Правило | Значение |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| Бюджет оценки промпта | ≤ 150k токенов |
|
|
31
|
+
| Компакций на срез | ≤ 1 |
|
|
32
|
+
| Стоп-сигнал | первая компакция |
|
|
33
|
+
| Обязательный выход | handoff + коммит |
|
|
34
|
+
|
|
35
|
+
- При первом признаке приближения к бюджету — не «дожимать», а закрывать срез:
|
|
36
|
+
handoff, коммит, новая сессия.
|
|
37
|
+
- После первой компакции срез закрывается в любом случае: суммаризация уже
|
|
38
|
+
потеряла детали, и дальнейшая работа идёт по пересказу.
|
|
39
|
+
- Резать работу по волнам, гейтам или пакетам правок; смена фазы (план →
|
|
40
|
+
реализация → приёмка) — всегда новая сессия.
|
|
41
|
+
|
|
42
|
+
## 2. Разделение ролей
|
|
43
|
+
|
|
44
|
+
- Разведка и проверка («найти», «понять», «воспроизвести») — субагентам: у них
|
|
45
|
+
чистый контекст, а в ответ возвращается 1–2k токенов выводов, а не дамп.
|
|
46
|
+
- Реализация — одному писателю на рабочее пространство: связанные решения должны
|
|
47
|
+
приниматься в одном контексте (иначе параллельные агенты расходятся в стиле и
|
|
48
|
+
контрактах).
|
|
49
|
+
- Решения, сведение и приёмка — root-оркестратору.
|
|
50
|
+
- Аудиторский цикл не меняется: re-audit — resume тех же сессий; короткие
|
|
51
|
+
аудиторские сессии бюджету не угрожают.
|
|
52
|
+
|
|
53
|
+
Практическое правило: делегируй **исследование** широко, **реализацию** — узко,
|
|
54
|
+
**интеграцию** держи централизованной.
|
|
55
|
+
|
|
56
|
+
## 3. Батчинг инструментов
|
|
57
|
+
|
|
58
|
+
Каждый ход — это полная пересылка промпта. Один инструмент за ход умножает
|
|
59
|
+
бюджет на число мелочей.
|
|
60
|
+
|
|
61
|
+
- ≥ 3 независимые команды за ход или один скрипт вместо пяти-десяти вызовов.
|
|
62
|
+
- Длинные команды WSL выноси в скрипт-обёртку: строка на 250 символов в каждом
|
|
63
|
+
ходе дороже, чем файл, который читается один раз.
|
|
64
|
+
- Для чтения файлов — `read` с `limit`/`offset`, а не `cat` целиком.
|
|
65
|
+
- Цель по метрике: ≥ 2 инструментов на ход.
|
|
66
|
+
|
|
67
|
+
## 4. Вывод инструментов
|
|
68
|
+
|
|
69
|
+
- Всё, что больше 20k символов, — в файл; в контекст идёт голова (4k) и путь.
|
|
70
|
+
Это делает guard автоматически для `shell`, `bash`, `webfetch`.
|
|
71
|
+
- Скриншоты и бинарные вложения — файлом и путём, не base64 в контексте.
|
|
72
|
+
- `execute` (Code Mode) guard не режет: ограничивай результат в самом скрипте —
|
|
73
|
+
возвращай сводку и путь, а не сырой JSON на сотни килобайт.
|
|
74
|
+
- `read`, `write`, `edit` и инструменты комплекта не урезаются: у них свой
|
|
75
|
+
контракт, и потеря содержимого ломает работу.
|
|
76
|
+
|
|
77
|
+
## 5. Состояние в файлах
|
|
78
|
+
|
|
79
|
+
- `handoff` — обязательный выход среза (шаблон ниже); путь по умолчанию —
|
|
80
|
+
`docs/planning/handoff-<YYYY-MM-DD>.md`, если каталога нет — корень проекта.
|
|
81
|
+
- `decisions` — решения и причины; одно решение — одна запись; путь по умолчанию —
|
|
82
|
+
`docs/planning/decisions-<YYYY-MM-DD>.md`.
|
|
83
|
+
- `state` — ветка, гейты, открытые риски, следующие три шага; обновляется перед
|
|
84
|
+
новой сессией; путь по умолчанию — `docs/planning/state.md`.
|
|
85
|
+
- Новая сессия начинается с `state` и целевого раздела SPEC, а не с пересказа
|
|
86
|
+
прошлой сессии.
|
|
87
|
+
|
|
88
|
+
## 6. Модели
|
|
89
|
+
|
|
90
|
+
- По умолчанию — дешёвая модель; дорогая — только на архитектурные решения,
|
|
91
|
+
ревью и разбор спорного.
|
|
92
|
+
- Правки документов, чтение и механические проверки не требуют дорогой модели.
|
|
93
|
+
- Замер: 223 хода дорогой модели — $63, 104 хода дешёвой — $0.09. Один
|
|
94
|
+
переключатель модели экономит больше, чем любая оптимизация промпта. Это
|
|
95
|
+
внутренний замер проекта-потребителя: артефакт в репозиторий не приложен, из
|
|
96
|
+
этого репозитория цифры не воспроизводятся.
|
|
97
|
+
|
|
98
|
+
## 7. Метрики
|
|
99
|
+
|
|
100
|
+
| Метрика | Цель | Где смотреть |
|
|
101
|
+
| --- | --- | --- |
|
|
102
|
+
| Оценка промпта (медиана) | ≤ 150k | `orch_uni_session_stats` |
|
|
103
|
+
| Компакций на срез | ≤ 1 | `orch_uni_session_stats` |
|
|
104
|
+
| Инструментов на ход (`toolCalls/requests`, он же `toolsPerTurn`) | ≥ 2 | `orch_uni_session_stats` |
|
|
105
|
+
| Доля урезанных выводов среди вызовов инструмента (`toolOutputsCappedByTool.shell / toolCallsByTool.shell`; так же `bash`, `webfetch`) | < 5% | `orch_uni_session_stats` |
|
|
106
|
+
| Handoff в конце среза | есть | рабочее дерево |
|
|
107
|
+
|
|
108
|
+
`orch_uni_session_stats` отдаёт счётчики текущего инстанса плагина и настроенные
|
|
109
|
+
лимиты; исторические прогоны считаются по базе сессий на стороне проекта.
|
|
110
|
+
|
|
111
|
+
## 8. Guard
|
|
112
|
+
|
|
113
|
+
Guard — это хуки плагина и подписка на события компакции, а не запреты:
|
|
114
|
+
|
|
115
|
+
- `session.context` — оценивает промпт и добавляет одно короткое напоминание при
|
|
116
|
+
переходе на новый уровень бюджета. Напоминание инжектится один раз при
|
|
117
|
+
переходе уровня, а не в каждом запросе; внутри уровня текст стабилен, поэтому
|
|
118
|
+
префикс-кэш не рвётся.
|
|
119
|
+
- события компакции — хук `session.compaction` регистрируется всегда: он считает
|
|
120
|
+
попытки (`compactionAttempts`) и отдаёт tentative-напоминание ближайшему
|
|
121
|
+
запросу при задержке терминального события; напоминание не называет номер
|
|
122
|
+
компакции и не заявляет лимит. Подтверждает компакцию только
|
|
123
|
+
`session.compaction.ended` (дедуп по `event.id` или durable-позиции); он же
|
|
124
|
+
двигает `compactions` и лимит. `session.compaction.failed` увеличивает
|
|
125
|
+
`compactionFailures`, снимает tentative-note и сохраняет confirmed-note, ещё
|
|
126
|
+
ждущий доставки. Если поток событий завершился или упал, guard выставляет
|
|
127
|
+
`eventLive: false`, `compactionSource: "hook"` и `streamFailures: 1`
|
|
128
|
+
(per-session `errors` остаётся `0`), хук уже зарегистрирован и попытки
|
|
129
|
+
продолжают считаться; подтверждённых компакций и стопа на этом пути нет —
|
|
130
|
+
ложного «лимит исчерпан» не бывает. Источник, liveness и сбои потока видны в
|
|
131
|
+
`guard`.
|
|
132
|
+
- `tool.execute.after` — урезает вывод настроенных инструментов и складывает
|
|
133
|
+
полный текст в приватный корень на инстанс плагина
|
|
134
|
+
`os.tmpdir()/opencode-tool-output-<random>/` (POSIX: 0700 на корень и каталог
|
|
135
|
+
сессии, 0600 на файл). На Windows mode не действует (замер: 0666 у корня,
|
|
136
|
+
каталога сессии и файла): приватность там держится только на ACL каталога
|
|
137
|
+
`os.tmpdir()` и кодом не проверяется. Корень создаётся `mkdtemp` при
|
|
138
|
+
setup, поэтому заранее созданный каталог или junction `opencode-tool-output` не
|
|
139
|
+
используется; на POSIX корень обязан принадлежать текущему uid и не давать
|
|
140
|
+
доступа group/other, иначе guard отключается с warning (fail-open: результат не
|
|
141
|
+
трогается). Каталог сессии (`s-<uuid>`) и файл (`c-<uuid>.txt`, запись `wx`) —
|
|
142
|
+
случайные, их `realpath` проверяется внутри корня; при выгрузке плагина корень
|
|
143
|
+
удаляется best effort. Строка с lone surrogate нормализуется один раз (U+FFFD),
|
|
144
|
+
и файл, голова и счётчики описывают уже одну и ту же строку. Остаточный риск
|
|
145
|
+
принят: same-user атакующий всё ещё может
|
|
146
|
+
подменить каталог сессии между `realpath` и `writeFile` (в Node нет `openat`).
|
|
147
|
+
Если `TMPDIR` указывает внутрь воркспейса, файлы попадут туда.
|
|
148
|
+
|
|
149
|
+
Конфигурация (значения по умолчанию):
|
|
150
|
+
|
|
151
|
+
```jsonc
|
|
152
|
+
{
|
|
153
|
+
"sessionGuard": {
|
|
154
|
+
"enabled": true,
|
|
155
|
+
"promptTokensSoft": 150000,
|
|
156
|
+
"maxCompactions": 1
|
|
157
|
+
},
|
|
158
|
+
"toolOutputGuard": {
|
|
159
|
+
"enabled": true,
|
|
160
|
+
"maxChars": 20000,
|
|
161
|
+
"headChars": 4000,
|
|
162
|
+
"tools": ["shell", "bash", "webfetch"]
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`maxCompactions: 0` отключает лимит компакций. `guard.toolOutputEnabled` равен
|
|
168
|
+
`true` только когда приватный корень создан **и** эффективный список инструментов
|
|
169
|
+
непуст; `guard.toolOutputTools` — этот список (после нормализации и отбрасывания
|
|
170
|
+
защищённых и kit-имён), `guard.unkeyedCalls` — число вызовов хуков без непустого
|
|
171
|
+
`sessionID` (такой ввод отбрасывается, а не сливается в бакет `""`). Пустой
|
|
172
|
+
`tools` — явный или после отбрасывания всех имён — выключает кап с warning.
|
|
173
|
+
Принятые ограничения: каталог сессии `s-<uuid>` живёт до выгрузки плагина — он не
|
|
174
|
+
удаляется ни при `session.deleted`, ни после отброшенного капа (например, при
|
|
175
|
+
неизменяемом результате), а освобождается вместе с корнем; нормализация lone
|
|
176
|
+
surrogate опирается на ES2024 `String.prototype.isWellFormed`/`toWellFormed`
|
|
177
|
+
(минимальная версия рантайма в `engines` не зафиксирована — на старом рантайме кап
|
|
178
|
+
деградирует fail-open: `errors` +1, результат не тронут).
|
|
179
|
+
В счётчиках `toolCalls` и
|
|
180
|
+
per-tool счётчики включают вложенные вызовы Code Mode, а `outputCharsSaved` —
|
|
181
|
+
верхняя оценка в символах (внедрённая заглушка вычитается). `toolCallsByTool` и
|
|
182
|
+
`toolOutputsCappedByTool` разносят вызовы и урезанные выводы по нормализованному
|
|
183
|
+
имени инструмента (namespace снят, регистр нижний). Дубликаты событий компакции
|
|
184
|
+
считаются один раз только при наличии `event.id` или durable-позиции; без
|
|
185
|
+
identity повторная доставка может пересчитать событие (fail-open в сторону
|
|
186
|
+
счёта).
|
|
187
|
+
|
|
188
|
+
Guard fail-open: ошибка хука не ломает запрос и не портит результат инструмента.
|
|
189
|
+
Он не пишет в рабочее дерево (кроме случая `TMPDIR` внутри воркспейса — см.
|
|
190
|
+
выше) и не блокирует действия — только напоминает и урезает вывод. Отключить
|
|
191
|
+
можно любым из флагов `enabled`.
|
|
192
|
+
|
|
193
|
+
## Шаблон handoff
|
|
194
|
+
|
|
195
|
+
```md
|
|
196
|
+
# Handoff: <срез> — <YYYY-MM-DD>
|
|
197
|
+
|
|
198
|
+
<!-- Файл: docs/planning/handoff-<YYYY-MM-DD>.md; нет каталога — корень проекта. -->
|
|
199
|
+
|
|
200
|
+
## Цель и DoD
|
|
201
|
+
Одно предложение цели и проверяемый критерий готовности.
|
|
202
|
+
|
|
203
|
+
## Сделано
|
|
204
|
+
- Что изменено; чем подтверждено (команда, тест, коммит).
|
|
205
|
+
|
|
206
|
+
## Не сделано
|
|
207
|
+
- Что осталось и почему (блокер, решение владельца, следующая волна).
|
|
208
|
+
|
|
209
|
+
## Решения
|
|
210
|
+
- Решение — причина; ссылка на документ или коммит.
|
|
211
|
+
|
|
212
|
+
## Риски и ограничения
|
|
213
|
+
- Принятый риск — кем и до какого момента.
|
|
214
|
+
|
|
215
|
+
## Следующий шаг
|
|
216
|
+
Одно конкретное действие для следующей сессии.
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Чек-лист среза
|
|
220
|
+
|
|
221
|
+
- [ ] Срез закрыт по DoD, а не по «кончилось время».
|
|
222
|
+
- [ ] Handoff записан и ссылается на доказательства.
|
|
223
|
+
- [ ] Решения и риски зафиксированы в файлах, а не только в переписке.
|
|
224
|
+
- [ ] Рабочее дерево чистое или явно описано в handoff.
|
|
225
|
+
- [ ] Следующая сессия начинается с `state` и SPEC, а не с пересказа.
|