work_item: ORCH-117 stage: analysis author_agent: analyst status: ready-for-review created_at: 2026-06-15 model_used: claude-opus-4-8 title: "Sandbox-only fail-closed изоляция записи в Plane (регресс ORCH-114)" framework: pytest scope: > Покрывает fail-closed гард записи в Plane: блок прод-записи из тест-процесса даже при живом боевом токене (обязательный регресс ORCH-114), sandbox-only-разрешение под явным opt-in, default-deny по умолчанию, отличие тест-процесса от боевого/staging рантайма, перехват на момент вызова (устойчивость к захвату токена на импорте), аудит. ВНЕ покрытия: реальные сетевые вызовы к боевому Plane (запрещены самим фиксом) — всё через мок httpx; выбор механизма детекта — зона ADR (тесты проверяют поведение, не реализацию). notes: > TC-01 — ОБЯЗАТЕЛЬНЫЙ регресс инцидента ORCH-114: красный до фикса, зелёный после. Все три примитива записи (update_issue_state / add_comment / _set_issue_state_direct) проверяются на блок/разрешение через мок httpx.patch/httpx.post (никаких реальных сетевых вызовов). Полный регресс tests/ должен оставаться зелёным (autouse fail-closed-фикстура не ломает существующие тесты, большинство из которых уже мокируют plane_*/add_comment). Боевой ID проекта в тестах — 7a79f0a9-5278-49cd-9007-9a338f238f9c; sandbox — 8c5a3025-4f9d-4190-b79f-fa06276bb27e. tests: - id: TC-01 type: integration description: "РЕГРЕСС ORCH-114: pytest-env + живой прод-токен → notify_stage_change('ORCH-114','deploy','done') на боевой проект НЕ делает ни одного httpx.patch/post (мок httpx не вызван для prod-URL / гард блокирует). Красный до фикса, зелёный после." module: tests/test_orch117_plane_write_isolation.py expected: PASS - id: TC-02 type: unit description: "update_issue_state в тест-процессе с целевым боевым проектом 7a79f0a9-… → блок (httpx.patch не вызван); аудит-причина prod-project-in-test." module: tests/test_orch117_plane_write_isolation.py expected: PASS - id: TC-03 type: unit description: "add_comment в тест-процессе с боевым проектом → блок (httpx.post не вызван)." module: tests/test_orch117_plane_write_isolation.py expected: PASS - id: TC-04 type: unit description: "_set_issue_state_direct в тест-процессе с боевым проектом → блок (httpx.patch не вызван). Покрывает все set_issue_* (Done/In Review/Blocked/…), сводящиеся к этому примитиву." module: tests/test_orch117_plane_write_isolation.py expected: PASS - id: TC-05 type: unit description: "Default-deny: без явного opt-in запись в тест-процессе блокируется для ЛЮБОГО целевого проекта (в т.ч. sandbox)." module: tests/test_orch117_plane_write_isolation.py expected: PASS - id: TC-06 type: unit description: "Sandbox-разрешение: opt-in включён + целевой проект SANDBOX 8c5a3025-… → реальный httpx-вызов разрешён и адресован sandbox-URL (мок подтверждает вызов)." module: tests/test_orch117_plane_write_isolation.py expected: PASS - id: TC-07 type: unit description: "Sandbox-only даже с opt-in: opt-in включён, но целевой проект боевой → блок (allowlist sandbox-only), независимо от opt-in." module: tests/test_orch117_plane_write_isolation.py expected: PASS - id: TC-08 type: unit description: "Fail-closed при неопределённости: целевой project_id неразрешим/пуст в тест-процессе → блок (NFR-1 'не знаю ⇒ не пишу')." module: tests/test_orch117_plane_write_isolation.py expected: PASS - id: TC-09 type: unit description: "Устойчивость к захвату на импорте: PLANE_HEADERS содержит реальный токен, env/settings не подменяются постфактум → гард всё равно блокирует прод-запись на момент вызова (не зависит от os.environ.setdefault / подмены plane_api_token)." module: tests/test_orch117_plane_write_isolation.py expected: PASS - id: TC-10 type: unit description: "Нулевая регрессия боевого рантайма: при имитации НЕ-pytest процесса гард = no-op, httpx.patch/post вызывается с прежним URL/headers/payload (запись в Plane как до ORCH-117)." module: tests/test_orch117_plane_write_isolation.py expected: PASS - id: TC-11 type: unit description: "Staging != pytest: имитация staging-рантайма (sandbox-проект, не тест-процесс) → запись в SANDBOX проходит (детект тест-процесса не срабатывает ложно)." module: tests/test_orch117_plane_write_isolation.py expected: PASS - id: TC-12 type: unit description: "Аудит: на блокировку эмитится структурный WARNING/ERROR с project_id/work_item/операцией/причиной (caplog); на разрешённую sandbox-запись — audit-INFO." module: tests/test_orch117_plane_write_isolation.py expected: PASS - id: TC-13 type: integration description: "Дефолтная autouse-страховка conftest: репрезентативный advance стадии в обычном тесте не делает реальной записи в боевой Plane (страховка активна по умолчанию для всего сьюта)." module: tests/test_orch117_plane_write_isolation.py expected: PASS - id: TC-14 type: unit description: "Kill-switch без чёрного хода: при выключенном kill-switch гарда запись в БОЕВОЙ проект из pytest всё равно не разрешается молча (реальная sandbox-запись управляется только opt-in+allowlist)." module: tests/test_orch117_plane_write_isolation.py expected: PASS - id: TC-15 type: integration description: "Полный регресс tests/ зелёный — внедрённая autouse fail-closed-фикстура не ломает существующие тесты (smoke: pytest tests/ -q)." module: tests/ expected: PASS