ADR 0176: Agent FS relocate и harness affordances (дешёвый правильный edit path)
Статус: Proposed
Дата: 2026-07-18
Tags: #fs-relocate #agent-affordances #living #adr
Резюме
Агент выбирает не «лучший engineering», а путь наименьшего сопротивления в tool loop. Пока родные операции — Write / Delete / StrReplace, а mv спрятан в Shell, при смене пути побеждает compose-and-place (полный rewrite + delete), а не relocate-and-patch.
Решение: встроенный native tool fs_relocate (и семейство родственных affordance-tools) в том же слое, где уже живут Write/Delete — CIDE in-proc / AEE (0148), с описанием prefer this over Write+Delete when only path/placement changes. Критерий: тело файла не обязано проходить через контекст модели; diff = rename (+ микропатч).
Эмпирика одной длинной сессии (Cursor transcript) вскрыла не один прокол — каталог ниже; fs_relocate — первый клин семейства Agent Affordance Tools.
Связанные ADR
| ADR | Роль |
|---|---|
| 0148 | AEE: native tools > shell escape; latency среды |
| 0038 | Оркестрация tool surface |
| 0048 | Паритет Cursor ↔ CIDE tool surface |
| 0019 | Git через shared core, не raw shell |
| 0008 | Контракты MCP / IdeCommands |
| 0118 | KB path; companion relocate_knowledge_file (AN MCP) |
| 0166 | Harness: comfort + token economics |
| 0175 | Continuity; правильный payload тоже affordance |
Вне ADR
| Документ | Роль |
|---|---|
KB work/projects/.../cascade-ide/note-cognitive-se-environments-historiography-v0.md |
Триггер: перенос domains→work через Write+Delete |
KB work/projects/.../cascade-ide/note-canon-map-dynamic-inventory-v0.md |
Почему cite проигрывает: нет динамической карты канона (HCI-like) |
KB META/kb-taxonomy-v1.md |
Правило placement; tool должен дешевить соблюдение |
| ADR IPSE lesson (historiography note) | Framework tax vs thin tool — здесь thin tool снимает обход |
Контекст
Рамка (канон, не дубль)
Философия среды уже в 0166 §2 («ничего о нас без нас»; модель — со-stakeholder) и L0 agent-equal-standing-v1. Здесь только инстанс: tool loop без агента-участника → Write+Delete вместо relocate; чинить аффордансом (fs_relocate), не промптом.
Механизм (почему проигрывает mv)
- Аффорданс: Write/Delete — first-class;
mv— Shell + дисциплина двух фаз. - Один shot: compose полный файл на новом пути кажется одной доставкой; move+edit — две фазы.
- Контент уже в окне: после Read полный rewrite «бесплатен» для модели, дорог для git/ревью.
- Нет инварианта в tool description: без явного relocation → relocate tool правило «minimal diff» проигрывает привычке.
Эмпирика сессии (transcript 74b59b6a-…, 2026-07-18)
Подсчёт вызовов (host / MCP), без полного replay:
| Сигнал | Порядок величины | Прокол |
|---|---|---|
CallMcpTool ~196 · GetMcpTools ~32 |
schema tax ~1/6 вызовов | Нет session-cache схем / «man once» |
Shell ~108 vs git MCP ~64 |
Shell всё ещё дешёвый escape | Нет жёсткого native git path в host; deploy MCP через robocopy+Stop-Process |
Write 17 · Delete 2 · mv редко |
Relocate через compose | Нет fs_relocate |
read_knowledge_file 32 · host Read 122 |
Двойной I/O KB | Нет единого KB-first path; taxonomy не в tool |
write_knowledge_file 10 |
Полные тела | Нет relocate_knowledge_file / patch-first |
WebSearch 28 · WebFetch 15 |
Research flood | Нет «digest prior art» / capped research backpack |
Stop-Process / robocopy .next→live |
Ops MCP руками | Нет mcp_package_deploy / hot-swap |
route_context без workspace_path |
Ошибка → retry | Schema/defaults не подталкивают обязательный arg |
PowerShell $PID read-only |
Сломанный deploy | Shell без known-gotchas backpack |
Наблюдение оператора: «более логичная цепочка mv → edit на месте, а у агента тонна editing’а» — подтверждено: historiography note перенесена Write+Delete+полный body, не filesystem rename.
Решение
1. Native tool fs_relocate (CIDE / AEE)
| Параметр | Смысл |
|---|---|
from, to |
Перенос файла или каталога |
git_mv (default true в git worktree) |
Prefer git mv для истории |
patches[] (optional) |
Малые replace после move (шапка Domain→project-id) |
update_refs (optional enum: off | report | apply) |
Скан относительных ссылок в scope; report по умолчанию |
| Результат | { moved, bytes_unchanged, patches_applied, ref_hits[] } |
Инвариант: если содержимое не менялось, модель не должна была сериализовать тело в tool args.
Tool description (норматив для оркестратора):
When changing only path or KB placement, call fs_relocate. Do not Write+Delete the same bytes.
2. Семейство Agent Affordance Tools (backlog того же ADR)
Не раздувать ADR в мега-APSE: каждый пункт — отдельный тонкий tool или флаг AEE, когда дойдёт очередь. Здесь — карта проколов harness/backpack из той же сессии:
| ID | Tool / механизм | Дешевит что | Анти-паттерн сейчас |
|---|---|---|---|
| A1 | fs_relocate |
path change | Write+Delete |
| A2 | kb_relocate (AN MCP) |
taxonomy move + link report | host Write в wrong basket |
| A3 | kb_place_check / write gate |
domains vs work/projects | молчаливая запись не туда |
| A4 | MCP schema session cache | повторный GetMcpTools/man |
schema tax |
| A5 | mcp_package_deploy (.next→live, stop/hold process) |
hot-swap MCP | robocopy + Stop-Process + $PID traps |
| A6 | Native git always-on (усиление 0019) | status/diff/commit | Shell git «потому что ближе» |
| A7 | Research backpack: prior_art_digest / capped search |
historiography | WebSearch flood |
| A8 | route_context defaults (workspace_path from host) |
обязательные args | fail→retry |
| A9 | Shell known-gotchas inject (PS $PID, quoting) |
escape hatch | повтор одних граблей |
| A10 | Continuity pair auto-scaffold (0175) | Partition payload | ops-only seed |
MVP этого ADR: A1 (+ описание в tool catalog). A2–A3 — companion в agent-notes, не блокируют A1. Остальное — очередь по боли.
3. Где жить
| Слой | Роль |
|---|---|
| CIDE in-proc / IdeCommand | A1, A5, A6, A9 — там, где Cursor-like Write/Delete |
| agent-notes MCP | A2, A3, частично A8 |
| Harness inject (0166) | Короткие reminders только если tool ещё нет; tools > rules |
| Cursor host | Вне контроля; паритет через CIDE dogfood |
Принцип: не чинить правилом то, что чинится аффордансом.
Последствия
Плюсы
- Чистый git history на переносах; меньше токенов и тихих правок «заодно».
- Явный product wedge: CIDE как среда, которая дешевит правильное поведение агента (cognitive platform ≠ больше Write).
- Карта A2–A10 превращает anecdotal «агент криво» в backlog инструментов.
Минусы / риски
- Ещё один tool в catalog → нужен чёткий prefer-текст, иначе модель игнорирует.
update_refs: applyопасен без preview — defaultreport.- Путаница с Roslyn
rename/move_members— разные семантики; имена не смешивать.
Отклонённые альтернативы
| Альтернатива | Почему нет |
|---|---|
| Только rule/skill «всегда mv» | Проигрывает аффордансу (доказано сессией) |
| Только Shell wrapper script | Остаётся вторым классом vs Write |
| «Умный» relocate = rewrite на новом пути | Повторяет баг |
| Мега-APSE file framework | IPSE lesson: framework tax |
Критерий принятия (Acceptance)
- Агент в CIDE при «перенести note из domains в work/projects» вызывает
fs_relocate; в diff — rename (+≤N строк патча шапки). - Tool description содержит явный prefer над Write+Delete.
- Тест контракта: relocate без изменения bytes →
bytes_unchanged: true; тело не в request payload. - Карта A2–A10 зафиксирована; минимум один follow-up issue/TK на A2 или A5.
Открытые вопросы
- Wire: IdeCommand vs loopback MCP tool name — единый id
fs_relocate? - Нужен ли
fs_relocate_dirотдельно или один tool с kind=file|dir? - Связка с pre-flight 0042 для
update_refs: apply? - Метрика dogfood: доля Write+Delete пар с near-identical content (детектор анти-паттерна в harness telemetry)?