Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"globset",
"hexdigit",
"Homebrew",
"inlines",
"journaled",
"keypair",
"Kratos",
Expand Down Expand Up @@ -98,6 +99,7 @@
"toplevel",
"topo",
"tungstenite",
"uluru",
"unmatch",
"unrs",
"vitaliytv",
Expand Down Expand Up @@ -139,8 +141,10 @@
"бандлили",
"батчинг",
"Батчинг",
"батчингу",
"батчі",
"батчу",
"беклогу",
"биндинги",
"Биндінг",
"білдах",
Expand Down Expand Up @@ -255,6 +259,8 @@
"ін'єктуються",
"інвалідація",
"інвалідаціями",
"інвалідованих",
"інвалідували",
"інвалідована",
"інвалідований",
"інвалідованого",
Expand Down Expand Up @@ -571,6 +577,7 @@
"тенанта",
"тенанти",
"тенантів",
"термінації",
"термінацію",
"термінація",
"типо",
Expand All @@ -586,6 +593,8 @@
"фейл",
"фейлиться",
"фейляться",
"упоровся",
"фенсити",
"фенсить",
"фенситься",
"фетчиться",
Expand Down
2 changes: 2 additions & 0 deletions .v8rignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
.cursor/hooks.json
.marksman.toml
.claude/settings.local.json
crates/mt-core/tests/fixtures/name-vectors.json
layers/schemas/layers.schema.json
170 changes: 143 additions & 27 deletions bun.lock

Large diffs are not rendered by default.

40 changes: 13 additions & 27 deletions crates/agent-server/src/docs/graph.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ type: Rust Module
title: graph.rs
resource: crates/agent-server/src/graph.rs
docgen:
crc: b05874f3
crc: 4d27361c
model: openai-codex/gpt-5.4-mini
tier: cloud-min
score: 100
Expand All @@ -13,39 +13,25 @@ docgen:

## Огляд

Міст до графа для інтерактивного run вузла за контрактом `runtime.md` і `git.md`: сесія робить attach через CAS claim, працює в detached worktree, веде `session.jsonl` у run ref, підтримує handoff і завершується через fenced publish або переходить у release. Це не перевизначає граф: усі дії делегуються `mt-core` — тій самій реалізації, яку `@7n/mt` використовує через napi.

Публічний обсяг цього модуля — `GraphConfig`, `new`, `InteractiveRun`, `HandoffTicket`, `attach`, `attach_resume`, `generation`, `add_approval`, `commit_turn`, `renew`, `done`, `release`, `handoff`.

Виклики працюють fail-safe: помилки перехоплюються, назовні винятки не кидаються, а для окремих збоїв повертається порожнє значення замість exception.

`.nitra/` існує лише в run ref під час сесії, не потрапляє в `main` і прибирається перед publish.
Містить інтерактивний run вузла як lifecycle від `CAS claim` і `run ref` з detached worktree до `session.jsonl`, `attach`/`attach_resume`, `done` або `release`, а також `handoff`. `done` завершує сесію fenced publish, `release` ставить її на паузу без публікації. Усі операції виконуються через `mt-core` через `napi`; графовий контракт не реімплементується, а використовується та сама реалізація, що й у `@7n/mt`. `GraphConfig`, `new`, `InteractiveRun`, `HandoffTicket`, `attach`, `attach_resume`, `generation`, `add_approval`, `commit_turn`, `renew`, `done`, `release`, `handoff`. `.nitra/` живе лише в run ref і прибирається перед publish, тому в `main` не потрапляє. Локальні fail-safe гілки можуть повертати порожнє значення замість винятку; інші помилки можуть поширюватися назовні.

## Поведінка

GraphConfig задає спільні параметри життєвого циклу вузла: шлях до tasks-дерева, тривалість lease і початкового актора. new створює цей контрактний стан із дефолтним lease та actor для подальших операцій.

InteractiveRun описує живу сесію, у якій claim уже утримується, а worktree матеріалізовано. Усі наступні дії працюють у межах цього стану й або продовжують той самий run ref, або переводять його в нову фазу.

HandoffTicket переносить мінімум, потрібний для кооперативного переходу між хостами: старий run ref і покоління claim-а, від якого треба продовжити. attach_resume використовує цей тікет, щоб відновити worktree не з main, а зі стану попереднього run ref, зберігши журнал і проміжні зміни.

attach запускає сесію з CAS-claimом, створює detached worktree від базового стану й прив’язує його до run ref. Якщо вузол уже зайнято, відмова є явною, без переходу в напівживий стан.

generation повертає fencing token поточного claim-а, який використовується як межа для side effects і для перевірки, що сесія ще належить цьому хосту.
GraphConfig задає спільні межі життєвого циклу run: звідки беруться tasks і скільки живе claim до renewal або takeover. new створює початковий контекст вузла, з якого далі будуються attach, renew, done, release і handoff.

add_approval доповнює сесію вже верифікованим підтвердженням, яке потім потрапляє в синтезовані артефакти run-а.
InteractiveRun тримає активний стан сесії: claim лишається захопленим, а worktree — матеріалізованим, доки вузол не завершить хід або не передасть естафету. Усі подальші дії працюють поверх цього стану і повертають результат або fail-safe помилку без ламання зовнішнього потоку.

commit_turn зберігає хід у журнал сесії та фіксує зміни в run ref, щоб відновлення, handoff і подальший publish бачили повну послідовність дій. Порожній хід не створює зайвих артефактів.
attach запускає звичайний старт інтерактивної сесії: бере claim, створює detached worktree від базового стану й піднімає run ref як основу для журналу та наступних комітів. Якщо claim уже зайнято, потік зупиняється явною помилкою, щоб не змішувати два активні вузли в одному графі.

renew продовжує lease для того самого claim-а, зберігаючи ownership лише поки CAS ще не втрачено. Якщо claim уже перехоплено, сесію треба зупинити.
commit_turn є базовою одиницею прогресу: зміни з worktree та журнал сесії накопичуються в run ref, а порожні ходи не створюють зайвих артефактів. add_approval входить у цей самий потік як підтверджений сигнал, що потім потрапляє в синтезовані run-артефакти.

handoff переводить живу сесію в кооперативний режим передачі: зберігає повний журнал у run ref, знімає claim і повертає тікет для attach_resume на новому хості. Це зберігає безперервність серії й не викидає проміжний стан.
generation і renew разом утримують fencing token для side effects: generation відображає поточну версію claim, а renew продовжує її без зміни власника. Якщо claim уже втрачено, renew повертає ознаку провалу, щоб сесію можна було безпечно зупинити.

release завершує активне утримання вузла без publish: claim знімається, worktree прибирається, а run ref лишається як база для відновлення або подальшого handoff.
handoff переводить активну сесію на інший хост без втрати журналу: run ref зберігається як база відновлення, claim знімається, а результатом стає HandoffTicket. attach_resume споживає цей тікет, відновлює worktree зі стану старого run ref і продовжує generation з наступного значення; якщо старий run ref недоступний, це явна помилка, а не прихований збій.

done завершує сесію тільки після успішного Check-гейту. Перед publish він синтезує контрактні артефакти run і fact, прибирає `.nitra/` з індексу та виконує fenced publish у main; при невдачі сесія лишається живою.
done завершує хід через перевірку стану, синтез run- і fact-артефактів та fenced publish у main. Тут `.nitra/` має бути прибраний із публічного результату; якщо перевірка не проходить, вузол лишається живим і може бути виправлений перед повторною спробою.

`HandoffTicket` і `attach_resume` разом реалізують міграцію сесії між хостами без втрати журналу та мідфлайт-змін, а `done` завершує той самий ланцюг публікацією без розриву в нумерації серії.
release відпускає вузол без публікації: claim знімається, worktree прибирається, а run ref лишається як база для подальшого attach або відновлення.

## Публічний API

Expand All @@ -61,9 +47,9 @@ done завершує сесію тільки після успішного Chec
- done — `mt done`: гейт `## Check` (контракт graph.md — fail → відмова сигналу, run лишається живим) → синтез `run_NNN.md`/`fact_NNN.md` → стрип `.nitra/` з індексу (інваріант git.md) → fenced publish (rebase на origin/main + atomic push main / видалення claim+run ref). Успіх → worktree прибирається.
- release — Пауза/відпустити: CAS-delete claim + прибрати worktree; run ref лишається (журнал сесії — база відновлення наступного attach).
- handoff — Кооперативний handoff (git.md, claim-операція `handoff`; runtime.md, «Міграція сесії між хостами», крок 2): синтезує `run_NNN.md (result: handoff)` → коміт → push run ref БЕЗ стрипу `.nitra/` — повний журнал розмови їде разом (checkpoint-режим із дистильованим summary — окрема задача) → CAS-delete claim. Повертає тікет для `attach_resume` на новому хості.
- new — створює новий інстанс модуля для роботи з NAPI-інтеграцією.
- new — створює новий об’єкт для публікації, щоб зібрати дані допису перед відправкою

## Гарантії поведінки

- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
- За певних помилок повертає порожнє значення (напр. `null`) замість винятку.
- Містить локальні fail-safe гілки; інші помилки можуть поширюватися назовні.
- Деякі локальні fail-safe гілки повертають порожнє значення (напр. `null`) замість винятку.
16 changes: 8 additions & 8 deletions crates/agent-server/tests/docs/graph_wiring.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@ type: Rust Module
title: graph_wiring.rs
resource: crates/agent-server/tests/graph_wiring.rs
docgen:
crc: ec604fa5
crc: d9f8a134
model: openai-codex/gpt-5.4-mini
tier: cloud-min
score: 100
judgeModel: openai-codex/gpt-5.4-mini
---

## Огляд

Файл інтегрує WS-сесії з graph-мостом для перевірки контракту між `UserMessage`, `DoneSession` і `ReleaseSession`. Тестовий контур працює на bare-репо як origin, використовує MockProvider-агент і реальний WS, щоб зафіксувати поведінку без втрати run ref: перше `UserMessage` прив’язує вузол, `DoneSession` запускає fenced publish, а `ReleaseSession` ставить сесію на паузу й звільняє вузол.
Інтеграція WS-сесій із graph-мостом: attach на першому UserMessage, журнал у run ref, DoneSession → fenced publish, ReleaseSession → пауза. Все герметично: bare-репо як origin, скриптований runner, реальний WS.

## Поведінка

1. Піднімає ізольоване середовище з bare-origin, робочим репозиторієм, вузлом `mt/demo` і WS-сервером із graph-мостом та scripted MockProvider.
2. Приймає перше `UserMessage` як момент захоплення вузла: сесія прив’язується до вузла, а хід агента стартує тільки після успішного attach.
3. Фіксує сесію в журналі окремого run ref, щоб історія звернення зберігалася незалежно від подальшого завершення або паузи.
4. Після завершення ходу через `DoneSession` публікує результат у `main` у fenced-режимі: службові refs прибираються, а `.nitra/` не потрапляє в публікацію.
5. Після `ReleaseSession` знімає блокування з вузла без втрати журналу: claim звільняється, run ref залишається доступним, і вузол можна знову захопити новим `UserMessage`.
6. Якщо вузол уже зайнятий іншим тримачем, завершує спробу помилкою `claim-lost` без виконання ходу.
Під час першого `UserMessage` сесія може прив’язати вузол до поточного тримача; якщо вузол уже зайнятий іншим тримачем, для клієнта приходить `Error` із `claim-lost`, і хід не стартує.

Після завершення ходу публікація відбувається через fenced publish у `main`: `refs/mt/claims/` і `refs/mt/runs/` зникають, а службовий журнал не потрапляє в `.nitra/session.jsonl` на `main`.

`DoneSession` завершує сесію коміт-станом із повідомленням про done; окремі контрактні артефакти спроби лишаються в `main`. `ReleaseSession` лише знімає claim і зберігає run ref та журнал, тож той самий вузол можна прив’язати знову.

## Гарантії поведінки

Expand Down
18 changes: 4 additions & 14 deletions crates/agent-server/tests/docs/handoff_ws.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,15 @@ type: Rust Module
title: handoff_ws.rs
resource: crates/agent-server/tests/handoff_ws.rs
docgen:
crc: b5181e7d
crc: bf0ca276
model: openai-codex/gpt-5.4-mini
score: 100
judgeModel: openai-codex/gpt-5.4-mini
tier: cloud-min
score: 80
---

## Огляд

Цей файл описує контрольований сценарій handoff сесії для кроків 2–3 у `runtime.md` («Міграція сесії між хостами»): дві незалежні `AppState` з окремими `state_dir` симулюють два хости в одному й тому самому git-репозиторії. Після `handoff_node` на хості 1 `resume_node` на хості 2 з тим самим тікетом успадковує журнал і продовжує `seq` без розривів.

## Поведінка

1. Піднімає два незалежні хости з окремими `state_dir`, але з одним і тим самим локальним git-репозиторієм, щоб змоделювати міграцію сесії між різними вузлами без зміни робочого дерева.
2. На першому хості відкриває сесію, приймає хід користувача й фіксує завершення агента, після чого переводить вузол у стан handoff.
3. Формує ticket для handoff і перевіряє, що він створений як нова генерація для передачі сесії.
4. На другому хості відновлює той самий вузол за цим ticket і перевіряє, що журнал попереднього хоста доступний локально ще до нового ходу.
5. Запускає новий хід на другому хості та підтверджує, що sequence номер продовжується без розривів після resume.
6. Не перевіряє паралельні гонки між хостами; сценарій навмисно послідовний.
7. Не перевіряє віддалену мережеву міграцію між різними машинами; обидва хости працюють у межах одного локального репозиторію як контрольована симуляція.
Кооперативний handoff на рівні AppState/session (runtime.md, «Міграція сесії між хостами», кроки 2-3): дві незалежні `AppState` (окремі `state_dir` — симуляція двох хостів), той самий git-репозиторій. Хід на хості 1 → `handoff_node` → `resume_node` на хості 2 з тим самим тікетом → журнал успадкований, наступний хід продовжує seq без розривів.

## Гарантії поведінки

Expand Down
Loading
Loading