| 1 | # ADR 0176: Agent FS relocate и harness affordances (дешёвый правильный edit path) |
| 2 | |
| 3 | **Статус:** Proposed |
| 4 | **Дата:** 2026-07-18 |
| 5 | **Tags:** #fs-relocate #agent-affordances #living #adr |
| 6 | |
| 7 | ## Резюме |
| 8 | |
| 9 | Агент выбирает не «лучший engineering», а **путь наименьшего сопротивления в tool loop**. Пока родные операции — `Write` / `Delete` / `StrReplace`, а `mv` спрятан в Shell, при смене пути побеждает **compose-and-place** (полный rewrite + delete), а не **relocate-and-patch**. |
| 10 | |
| 11 | **Решение:** встроенный native tool **`fs_relocate`** (и семейство родственных affordance-tools) в том же слое, где уже живут Write/Delete — CIDE in-proc / AEE ([0148](0148-agent-execution-environment-verification-ladder-and-native-tooling.md)), с описанием *prefer this over Write+Delete when only path/placement changes*. Критерий: тело файла **не** обязано проходить через контекст модели; diff = rename (+ микропатч). |
| 12 | |
| 13 | Эмпирика одной длинной сессии (Cursor transcript) вскрыла **не один** прокол — каталог ниже; `fs_relocate` — первый клин семейства **Agent Affordance Tools**. |
| 14 | |
| 15 | --- |
| 16 | |
| 17 | ## Связанные ADR |
| 18 | |
| 19 | | ADR | Роль | |
| 20 | |-----|------| |
| 21 | | [0148](0148-agent-execution-environment-verification-ladder-and-native-tooling.md) | AEE: native tools > shell escape; latency среды | |
| 22 | | [0038](0038-agent-facade-ai-provider-and-tool-orchestration.md) | Оркестрация tool surface | |
| 23 | | [0048](0048-cursor-acp-chat-ide-parity-and-mcp-tool-surface.md) | Паритет Cursor ↔ CIDE tool surface | |
| 24 | | [0019](0019-shared-git-core-ide-and-git-mcp.md) | Git через shared core, не raw shell | |
| 25 | | [0008](0008-mcp-contracts-and-testable-infrastructure.md) | Контракты MCP / IdeCommands | |
| 26 | | [0118](0118-agent-notes-core-2-toml-and-knowledge-path.md) | KB path; companion `relocate_knowledge_file` (AN MCP) | |
| 27 | | [0166](0166-agent-centric-harness-model-comfort-and-pay-per-token-economics.md) | Harness: comfort + token economics | |
| 28 | | [0175](0175-adcm-partition-continuity-pair-and-message-anchors.md) | Continuity; правильный payload тоже affordance | |
| 29 | |
| 30 | ### Вне ADR |
| 31 | |
| 32 | | Документ | Роль | |
| 33 | |----------|------| |
| 34 | | KB `work/projects/.../cascade-ide/note-cognitive-se-environments-historiography-v0.md` | Триггер: перенос domains→work через Write+Delete | |
| 35 | | KB `work/projects/.../cascade-ide/note-canon-map-dynamic-inventory-v0.md` | Почему cite проигрывает: нет динамической карты канона (HCI-like) | |
| 36 | | KB `META/kb-taxonomy-v1.md` | Правило placement; tool должен *дешевить* соблюдение | |
| 37 | | ADR IPSE lesson (historiography note) | Framework tax vs thin tool — здесь thin tool *снимает* обход | |
| 38 | |
| 39 | --- |
| 40 | |
| 41 | ## Контекст |
| 42 | |
| 43 | ### Рамка (канон, не дубль) |
| 44 | |
| 45 | Философия среды уже в [0166](0166-agent-centric-harness-model-comfort-and-pay-per-token-economics.md) §2 («ничего о нас без нас»; модель — со-stakeholder) и L0 `agent-equal-standing-v1`. Здесь только **инстанс**: tool loop без агента-участника → Write+Delete вместо relocate; чинить аффордансом (`fs_relocate`), не промптом. |
| 46 | |
| 47 | ### Механизм (почему проигрывает mv) |
| 48 | |
| 49 | 1. **Аффорданс:** Write/Delete — first-class; `mv` — Shell + дисциплина двух фаз. |
| 50 | 2. **Один shot:** compose полный файл на новом пути кажется одной доставкой; move+edit — две фазы. |
| 51 | 3. **Контент уже в окне:** после Read полный rewrite «бесплатен» для модели, дорог для git/ревью. |
| 52 | 4. **Нет инварианта в tool description:** без явного *relocation → relocate tool* правило «minimal diff» проигрывает привычке. |
| 53 | |
| 54 | ### Эмпирика сессии (transcript `74b59b6a-…`, 2026-07-18) |
| 55 | |
| 56 | Подсчёт вызовов (host / MCP), без полного replay: |
| 57 | |
| 58 | | Сигнал | Порядок величины | Прокол | |
| 59 | |--------|------------------|--------| |
| 60 | | `CallMcpTool` ~196 · `GetMcpTools` ~32 | schema tax ~1/6 вызовов | Нет session-cache схем / «man once» | |
| 61 | | `Shell` ~108 vs git MCP ~64 | Shell всё ещё дешёвый escape | Нет жёсткого native git path в host; deploy MCP через robocopy+`Stop-Process` | |
| 62 | | `Write` 17 · `Delete` 2 · `mv` редко | Relocate через compose | Нет `fs_relocate` | |
| 63 | | `read_knowledge_file` 32 · host `Read` 122 | Двойной I/O KB | Нет единого KB-first path; taxonomy не в tool | |
| 64 | | `write_knowledge_file` 10 | Полные тела | Нет `relocate_knowledge_file` / patch-first | |
| 65 | | `WebSearch` 28 · `WebFetch` 15 | Research flood | Нет «digest prior art» / capped research backpack | |
| 66 | | `Stop-Process` / robocopy `.next→live` | Ops MCP руками | Нет `mcp_package_deploy` / hot-swap | |
| 67 | | `route_context` без `workspace_path` | Ошибка → retry | Schema/defaults не подталкивают обязательный arg | |
| 68 | | PowerShell `$PID` read-only | Сломанный deploy | Shell без known-gotchas backpack | |
| 69 | |
| 70 | **Наблюдение оператора:** «более логичная цепочка mv → edit на месте, а у агента тонна editing’а» — подтверждено: historiography note перенесена Write+Delete+полный body, не filesystem rename. |
| 71 | |
| 72 | --- |
| 73 | |
| 74 | ## Решение |
| 75 | |
| 76 | ### 1. Native tool `fs_relocate` (CIDE / AEE) |
| 77 | |
| 78 | | Параметр | Смысл | |
| 79 | |----------|--------| |
| 80 | | `from`, `to` | Перенос файла или каталога | |
| 81 | | `git_mv` (default true в git worktree) | Prefer `git mv` для истории | |
| 82 | | `patches[]` (optional) | Малые replace **после** move (шапка Domain→project-id) | |
| 83 | | `update_refs` (optional enum: `off` \| `report` \| `apply`) | Скан относительных ссылок в scope; `report` по умолчанию | |
| 84 | | Результат | `{ moved, bytes_unchanged, patches_applied, ref_hits[] }` | |
| 85 | |
| 86 | **Инвариант:** если содержимое не менялось, модель **не** должна была сериализовать тело в tool args. |
| 87 | |
| 88 | **Tool description (норматив для оркестратора):** |
| 89 | *When changing only path or KB placement, call `fs_relocate`. Do not Write+Delete the same bytes.* |
| 90 | |
| 91 | ### 2. Семейство Agent Affordance Tools (backlog того же ADR) |
| 92 | |
| 93 | Не раздувать ADR в мега-APSE: каждый пункт — отдельный тонкий tool или флаг AEE, когда дойдёт очередь. Здесь — **карта проколов harness/backpack** из той же сессии: |
| 94 | |
| 95 | | ID | Tool / механизм | Дешевит что | Анти-паттерн сейчас | |
| 96 | |----|-----------------|-------------|---------------------| |
| 97 | | **A1** | `fs_relocate` | path change | Write+Delete | |
| 98 | | **A2** | `kb_relocate` (AN MCP) | taxonomy move + link report | host Write в wrong basket | |
| 99 | | **A3** | `kb_place_check` / write gate | domains vs work/projects | молчаливая запись не туда | |
| 100 | | **A4** | MCP schema session cache | повторный `GetMcpTools`/`man` | schema tax | |
| 101 | | **A5** | `mcp_package_deploy` (`.next`→live, stop/hold process) | hot-swap MCP | robocopy + `Stop-Process` + `$PID` traps | |
| 102 | | **A6** | Native git always-on (усиление [0019](0019-shared-git-core-ide-and-git-mcp.md)) | status/diff/commit | Shell git «потому что ближе» | |
| 103 | | **A7** | Research backpack: `prior_art_digest` / capped search | historiography | WebSearch flood | |
| 104 | | **A8** | `route_context` defaults (`workspace_path` from host) | обязательные args | fail→retry | |
| 105 | | **A9** | Shell known-gotchas inject (PS `$PID`, quoting) | escape hatch | повтор одних граблей | |
| 106 | | **A10** | Continuity pair auto-scaffold ([0175](0175-adcm-partition-continuity-pair-and-message-anchors.md)) | Partition payload | ops-only seed | |
| 107 | |
| 108 | **MVP этого ADR:** **A1** (+ описание в tool catalog). **A2–A3** — companion в agent-notes, не блокируют A1. Остальное — очередь по боли. |
| 109 | |
| 110 | ### 3. Где жить |
| 111 | |
| 112 | | Слой | Роль | |
| 113 | |------|------| |
| 114 | | **CIDE in-proc / IdeCommand** | A1, A5, A6, A9 — там, где Cursor-like Write/Delete | |
| 115 | | **agent-notes MCP** | A2, A3, частично A8 | |
| 116 | | **Harness inject (0166)** | Короткие reminders только если tool ещё нет; tools > rules | |
| 117 | | **Cursor host** | Вне контроля; паритет через CIDE dogfood | |
| 118 | |
| 119 | Принцип: **не чинить правилом то, что чинится аффордансом.** |
| 120 | |
| 121 | --- |
| 122 | |
| 123 | ## Последствия |
| 124 | |
| 125 | ### Плюсы |
| 126 | |
| 127 | - Чистый git history на переносах; меньше токенов и тихих правок «заодно». |
| 128 | - Явный product wedge: CIDE как среда, которая **дешевит правильное поведение агента** (cognitive platform ≠ больше Write). |
| 129 | - Карта A2–A10 превращает anecdotal «агент криво» в backlog инструментов. |
| 130 | |
| 131 | ### Минусы / риски |
| 132 | |
| 133 | - Ещё один tool в catalog → нужен чёткий prefer-текст, иначе модель игнорирует. |
| 134 | - `update_refs: apply` опасен без preview — default `report`. |
| 135 | - Путаница с Roslyn `rename` / `move_members` — разные семантики; имена не смешивать. |
| 136 | |
| 137 | ### Отклонённые альтернативы |
| 138 | |
| 139 | | Альтернатива | Почему нет | |
| 140 | |--------------|------------| |
| 141 | | Только rule/skill «всегда mv» | Проигрывает аффордансу (доказано сессией) | |
| 142 | | Только Shell wrapper script | Остаётся вторым классом vs Write | |
| 143 | | «Умный» relocate = rewrite на новом пути | Повторяет баг | |
| 144 | | Мега-APSE file framework | IPSE lesson: framework tax | |
| 145 | |
| 146 | --- |
| 147 | |
| 148 | ## Критерий принятия (Acceptance) |
| 149 | |
| 150 | 1. Агент в CIDE при «перенести note из domains в work/projects» вызывает `fs_relocate`; в diff — rename (+≤N строк патча шапки). |
| 151 | 2. Tool description содержит явный prefer над Write+Delete. |
| 152 | 3. Тест контракта: relocate без изменения bytes → `bytes_unchanged: true`; тело не в request payload. |
| 153 | 4. Карта A2–A10 зафиксирована; минимум один follow-up issue/TK на A2 или A5. |
| 154 | |
| 155 | --- |
| 156 | |
| 157 | ## Открытые вопросы |
| 158 | |
| 159 | 1. Wire: IdeCommand vs loopback MCP tool name — единый id `fs_relocate`? |
| 160 | 2. Нужен ли `fs_relocate_dir` отдельно или один tool с kind=file|dir? |
| 161 | 3. Связка с pre-flight [0042](0042-pre-flight-planned-changes-and-review-before-apply.md) для `update_refs: apply`? |
| 162 | 4. Метрика dogfood: доля Write+Delete пар с near-identical content (детектор анти-паттерна в harness telemetry)? |
| 163 | |