diff --git a/openspec/changes/hotfolder-direct-write/.openspec.yaml b/openspec/changes/hotfolder-direct-write/.openspec.yaml
new file mode 100644
index 0000000..8126432
--- /dev/null
+++ b/openspec/changes/hotfolder-direct-write/.openspec.yaml
@@ -0,0 +1,2 @@
+schema: spec-driven
+created: 2026-10-09
diff --git a/openspec/changes/hotfolder-direct-write/design.md b/openspec/changes/hotfolder-direct-write/design.md
new file mode 100644
index 0000000..4d7e198
--- /dev/null
+++ b/openspec/changes/hotfolder-direct-write/design.md
@@ -0,0 +1,58 @@
+## Context
+
+Запись входящего файла сейчас — двухфазная: `Tmp/.<имя>.part`, затем rename (SMB) / жёсткая ссылка (local) в `Input`
+без замены (решения №17, №26, №32, №35 Среза 3a). Старый АРМ писал файл прямо в `Input` и в контуре работал. Владелец
+09.10.2026 выбрал повторить старый АРМ и перезаписывать существующий файл.
+
+## Goals / Non-Goals
+
+**Goals:** прямая запись в `Input` в обоих режимах; меньше требований к правам на шаре; срок ожидания ответа 20 мин.
+
+**Non-Goals:** см. proposal.
+
+## Decisions
+
+### D-1. Прямая запись с усечением (решение владельца 09.10.2026)
+
+`SmbHotFolder.put`: `smbclient.open_file(\<имя>, mode="wb", **credentials)` и `write`. `LocalHotFolder.put`:
+`open(/<имя>, "wb")` (`Path.write_bytes`). Существующий файл перезаписывается. Повторная запись в рамках одного
+процесса по-прежнему исключена дедупликацией по `job_id` в воркере (требование «Подтверждение запроса до обработки»);
+перезапись возможна только при повторе задания ядром в другом процессе.
+
+### D-2. Каталог `Input` создаётся, как раньше
+
+Перед записью `Input` создаётся без родителей, «уже есть» и отказ в правах при существующем каталоге — не ошибка
+(`_ensure_dir` в `smb`, `mkdir(exist_ok=True)` в `local`); корень не создаётся. Старый АРМ каталог не создавал, но
+терпимое создание поведения в контуре не меняет, а dev-режим и тесты на нём держатся.
+
+### D-3. `Tmp` остаётся только у эмулятора
+
+`SCAN_TMP_DIR` удаляется из настроек шлюза (`Settings`, `extra="ignore"` — старое значение в `.env` не ломает старт).
+Эмулятор читает свою переменную `SCAN_TMP_DIR` (`EmulatorSettings`) и собирает набор ответа через
+`LocalHotFolder.tmp_dir`, поэтому параметр `tmp_dir` у `LocalHotFolder` остаётся, но `put` его не использует.
+
+### D-4. Срок ожидания 20 мин
+
+`SCAN_TIMEOUT_SECONDS` по умолчанию 1200. Эффективный дедлайн — `min(reply_deadline, старт + SCAN_TIMEOUT_SECONDS)`,
+поэтому arm-core поднимает срок ответа OCR до 20 мин отдельным PR (RSHB/arm-core#87).
+
+### D-5. Ошибка записи
+
+При исключении во время открытия/записи `put` снимает недописанный `/<имя>` только при включённой очистке
+(ошибка снятия подавляется, исходное исключение поднимается) — R-3.
+
+## Risks / Trade-offs
+
+- ИС может увидеть недописанный файл (OQ-79) → смягчение: монопольное открытие `smbclient` на время записи; так же
+ работал старый АРМ. Откат — вернуть двухфазную запись из истории ветки.
+- Ошибка посреди записи оставляет обрезанный файл в `Input` → удаляется только при `SCAN_CLEANUP=true` (как `.part`
+ раньше, D-5); иначе остаётся, конверт `RECOGNITION_FAILED`.
+
+## Decisions made without discussion
+
+| # | Дата | Решение | Обоснование | Цена ошибки |
+|---|---|---|---|---|
+| R-1 | 2026-10-09 | Режим `local` тоже пишет прямо в `Input` (не только `smb`) | Одинаковая семантика режимов, один набор тестов; эмулятор пишет/читает локально мгновенно | Эмулятор может прочитать недописанный файл — на локальной ФС практически невозможно; откат — вернуть `.part` в `local` |
+| R-2 | 2026-10-09 | `Input` по-прежнему создаётся перед записью (D-2), хотя старый АРМ этого не делал | Отказ в правах на `mkdir` при существующем каталоге уже не ошибка; dev и тесты на этом держатся | Лишний вызов `mkdir` на шаре на каждое задание |
+| R-3 | 2026-10-09 | Ошибка записи: недописанный файл в `Input` удаляется только при `SCAN_CLEANUP=true` (D-5) | Требование «при `SCAN_CLEANUP=false` ничего не удалять» (OQ-144) сохраняется | Обрезанный файл остаётся в `Input`, ИС может его распознать с ошибкой; ядро получит `RECOGNITION_FAILED` от шлюза раньше |
+| R-4 | 2026-10-09 | `SCAN_TMP_DIR` удаляется из шлюза, у эмулятора остаётся (D-3) | Шлюзу каталог больше не нужен; эмулятору нужен для атомарной сборки ответа | Нет: `extra="ignore"` — старые `.env` стартуют |
diff --git a/openspec/changes/hotfolder-direct-write/proposal.md b/openspec/changes/hotfolder-direct-write/proposal.md
new file mode 100644
index 0000000..c805bf5
--- /dev/null
+++ b/openspec/changes/hotfolder-direct-write/proposal.md
@@ -0,0 +1,43 @@
+## Why
+
+Решение владельца 09.10.2026: запись входящего файла в горячую папку ИС Сканирование — «как в старом АРМ», где она
+работала в контуре: `smbclient.open_file('/Input/<имя>', 'wb')` прямо во входящий каталог
+(`reference-old-arm/api/src/core/clients.py:111`). Нынешняя схема (временный `Tmp/.<имя>.part` + rename без замены,
+OQ-79, OQ-137) требует от учётной записи шары каталога `Tmp`, прав на его создание и на переименование и в контуре не
+проверялась. Там же владелец решил: срок ожидания ответа OCR — 20 минут.
+Issue — RSHB/arm-scan-gateway#26. Вне среза `09-delivery-plan.md` (доработка Среза 3a по эксплуатации).
+
+## What Changes
+
+- `put` (режимы `smb` и `local`): файл `.` пишется сразу во входящий каталог, открытием на запись с
+ усечением (`wb`); существующий файл **перезаписывается** (как в старом АРМ). Временного файла, каталога `Tmp` и
+ переименования больше нет.
+- Настройка шлюза `SCAN_TMP_DIR` удаляется; параметр `tmp_dir` у `SmbHotFolder` удаляется. У `LocalHotFolder` он остаётся
+ только для эмулятора (сборка набора ответа, переменная эмулятора `SCAN_TMP_DIR` не меняется).
+- Очистка (`SCAN_CLEANUP=true`) больше не удаляет `Tmp/...part`.
+- `SCAN_TIMEOUT_SECONDS` по умолчанию 300 → 1200 (20 мин) — в паре с `reply_deadline` OCR 20 мин в arm-core
+ (RSHB/arm-core#87).
+- README, `CLAUDE.md`, `api/.env.example`, тесты — под новую семантику.
+
+## Non-goals
+
+- Протокол ответа (`Output//*.json`, стабилизация набора, OQ-78) не меняется.
+- Эмулятор ИС не меняется: он по-прежнему пропускает `*.part`/`*.tmp`/dot-файлы и собирает ответ через свой `Tmp`.
+- Аутентификация SMB не меняется: `OPENBAO__SMB__SMB_LOGIN`/`SMB_PASSWORD` уже передаются в сессию и в каждый вызов.
+- Таймаут вызовов `smbclient` (соединение ~60 с по умолчанию библиотеки) не трогаем.
+- Таблицы DBML не затрагиваются.
+
+## Capabilities
+
+### New Capabilities
+
+### Modified Capabilities
+- `image-recognition-gateway`: запись входящего файла без временного файла и с перезаписью; очистка без `Tmp`.
+
+## Impact
+
+- ASSUMPTION(OQ-79): ИС Сканирование не берёт файл, пока шлюз держит его открытым на запись (`smbclient.open_file`
+ по умолчанию открывает монопольно), — так работал старый АРМ. Если подтвердится обратное, вернуть запись через
+ временный файл.
+- Учётной записи шары больше не нужны права на `Tmp` и на переименование; нужна запись в `Input`.
+- Остатки `Tmp/.*.part` от прежних версий шлюз больше не трогает — убрать вручную.
diff --git a/openspec/changes/hotfolder-direct-write/specs/image-recognition-gateway/spec.md b/openspec/changes/hotfolder-direct-write/specs/image-recognition-gateway/spec.md
new file mode 100644
index 0000000..62d5643
--- /dev/null
+++ b/openspec/changes/hotfolder-direct-write/specs/image-recognition-gateway/spec.md
@@ -0,0 +1,67 @@
+## MODIFIED Requirements
+
+### Requirement: Передача файла в ИС Сканирование через горячие папки
+После подтверждения сервис SHALL прочитать исходник из S3 (`s3.bucket/s3.key`) и записать его во входящий каталог как
+`.`, где `` — расширение `original_name` запроса в нижнем регистре; запрос без расширения SHALL
+завершаться конвертом `RECOGNITION_FAILED` без записи в горячую папку. Файл SHALL записываться сразу во входящий каталог
+открытием на запись с усечением, как в старом АРМ (решение владельца 09.10.2026): временных файлов, служебного каталога
+и переименования нет; если `/.` уже существует, он SHALL быть перезаписан содержимым объекта S3.
+Затем сервис SHALL опрашивать исходящий каталог `//` каждые `SCAN_POLL_SECONDS` до
+`min(reply_deadline, старт + SCAN_TIMEOUT_SECONDS)` (`SCAN_TIMEOUT_SECONDS` по умолчанию 1200). ИС кладёт в этот каталог
+все распознанные документы задания — по одному `*.json` на документ, набор появляется целиком и больше не меняется;
+пустой или отсутствующий каталог означает ожидание. Ответ SHALL считаться готовым, когда в каталоге есть хотя бы один
+`*.json`, а набор имён и размеров `*.json` не менялся `SCAN_SETTLE_SECONDS` (по умолчанию 2); готовый набор SHALL
+включать **все** `*.json` каталога по возрастанию имени, остальные файлы игнорируются.
+
+#### Scenario: Файл положен во входящий каталог
+- **WHEN** получен запрос для `aep_5.PDF` с `job_id = 3f1c…`
+- **THEN** во входящем каталоге появляется `3f1c….pdf`, байт в байт равный объекту S3; каталог `Tmp/` не создаётся, временных файлов нет
+
+#### Scenario: Назначение уже существует
+- **WHEN** запрос для `aep_5.pdf` с `job_id = 3f1c…` обрабатывается, а `Input/3f1c….pdf` уже лежит с другим содержимым (задание повторено ядром после потери ack в другом процессе)
+- **THEN** `Input/3f1c….pdf` перезаписан и байт в байт равен объекту S3, задание продолжается опросом `Output/3f1c…/`, конверт `RECOGNITION_FAILED` не публикуется
+
+#### Scenario: Запрос без расширения
+- **WHEN** получен запрос с `original_name = "scan"` без точки
+- **THEN** опубликован конверт `error`, `error.code = RECOGNITION_FAILED`, сообщение о расширении; во входящем каталоге файла нет
+
+#### Scenario: Ответ из одного файла
+- **WHEN** в `Output/3f1c…/` появляется один `*.json` и набор не меняется `SCAN_SETTLE_SECONDS`
+- **THEN** опрос прекращается не позднее чем через `SCAN_SETTLE_SECONDS` + `SCAN_POLL_SECONDS` после первого опроса, увидевшего окончательный набор (отсчёт стабилизации — от этого опроса, а не от времени изменения файла), ответ из одного файла передаётся дальше по конвейеру
+
+#### Scenario: Ответ из нескольких файлов
+- **WHEN** в `Output/3f1c…/` лежат `3f1c….json` и `3f1c…_ofr.json`, посторонний файл `readme.txt`, и набор не меняется `SCAN_SETTLE_SECONDS`
+- **THEN** дальше по конвейеру передаются оба `*.json` по возрастанию имени, `readme.txt` игнорируется
+
+#### Scenario: Набор ещё пишется
+- **WHEN** размер файла или число файлов в `Output/3f1c…/` менялось менее `SCAN_SETTLE_SECONDS` назад
+- **THEN** ответ не считается готовым, опрос продолжается до дедлайна
+
+#### Scenario: Ошибка посреди записи
+- **WHEN** запись `Input/3f1c….pdf` прервалась ошибкой ввода-вывода
+- **THEN** опубликован конверт `RECOGNITION_FAILED`; недописанный файл удалён только при `SCAN_CLEANUP=true`, при `false` остаётся на месте
+
+### Requirement: Очистка горячей папки под флагом
+При `SCAN_CLEANUP=true` после того, как готовый набор ответа прочитан и склеен в `raw/{job_id}.json` (независимо от
+исхода нормализации), сервис SHALL удалить всё содержимое `//` (json-файлы ответа и любые
+прочие) и сам каталог ``, входящий файл `/.` и его вариант `/..done`,
+если они существуют. При `SCAN_CLEANUP=false` (по умолчанию) сервис MUST NOT удалять ничего, включая недописанный при
+ошибке записи входящий файл. При `TIMEOUT`, `FILE_READ_ERROR`, `RECOGNITION_FAILED` записи очистка MUST NOT выполняться
+(при ошибке записи и `SCAN_CLEANUP=true` снимается только недописанный входящий файл). Ошибка удаления SHALL быть
+записана в лог одной строкой на задание и MUST NOT менять конверт, `metrics.warnings` и `/healthcheck`.
+
+#### Scenario: Очистка включена
+- **WHEN** `SCAN_CLEANUP=true`, ответ на `3f1c….pdf` прочитан из `Output/3f1c…/`
+- **THEN** после публикации конверта в горячей папке нет `Output/3f1c…/`, `Input/3f1c….pdf` и `Input/3f1c….pdf.done`
+
+#### Scenario: Очистка выключена
+- **WHEN** `SCAN_CLEANUP=false`, ответ прочитан
+- **THEN** все файлы остаются на месте, сервис ничего не удаляет и не пишет в лог о очистке (решение №27)
+
+#### Scenario: Нет прав на удаление
+- **WHEN** `SCAN_CLEANUP=true`, удаление `Output/3f1c…/3f1c….json` завершается `PermissionError`
+- **THEN** конверт опубликован с тем же статусом, что и без очистки; в логе одна строка об ошибке удаления; `/healthcheck.hotfolder = ok`
+
+#### Scenario: Таймаут не чистит
+- **WHEN** `SCAN_CLEANUP=true`, ответ не появился до дедлайна
+- **THEN** опубликован `TIMEOUT`, входящий файл и каталог `Output//` (если есть) не тронуты
diff --git a/openspec/changes/hotfolder-direct-write/tasks.md b/openspec/changes/hotfolder-direct-write/tasks.md
new file mode 100644
index 0000000..9e4b52e
--- /dev/null
+++ b/openspec/changes/hotfolder-direct-write/tasks.md
@@ -0,0 +1,18 @@
+## 1. Тесты (scan-gateway, `api/tests/`)
+
+- [ ] 1.1 `unit/test_smb_hotfolder.py`: `put` пишет `open_file(\<имя>, "wb")` без `Tmp`/`rename`; существующий
+ файл перезаписывается; ошибка записи — снятие файла только при `cleanup=True`; `cleanup` не трогает `.part`
+- [ ] 1.2 `unit/test_local_hotfolder.py`: то же для `LocalHotFolder`; `Tmp` не создаётся `put`
+- [ ] 1.3 `unit/test_config.py`, `unit/test_hotfolder_factory.py`: нет `SCAN_TMP_DIR` у шлюза, `SCAN_TIMEOUT_SECONDS`
+ по умолчанию 1200; интеграционные `test_roundtrip.py` — без ожиданий про `Tmp/.part`
+
+## 2. Реализация (scan-gateway, `api/src/`)
+
+- [ ] 2.1 `gateway/smb.py`: `put` — прямая запись (D-1, D-2, D-5), убрать `tmp_dir`, `.part` из `cleanup`
+- [ ] 2.2 `gateway/hotfolder.py`: `LocalHotFolder.put` — прямая запись; `tmp_dir` только для эмулятора (D-3)
+- [ ] 2.3 `core/config.py`, `gateway/__init__.py`: убрать `SCAN_TMP_DIR` у шлюза; `SCAN_TIMEOUT_SECONDS = 1200`
+
+## 3. Документация
+
+- [ ] 3.1 `README.md`, `CLAUDE.md`, `api/.env.example` — прямая запись, `SCAN_TMP_DIR` только у эмулятора, таймаут 1200
+- [ ] 3.2 `make check-full`