architect(ET): auto-commit from architect run_id=158
All checks were successful
CI / test (push) Successful in 13s
All checks were successful
CI / test (push) Successful in 13s
This commit is contained in:
@@ -9,8 +9,9 @@
|
||||
- **Stage Engine** (`src/stage_engine.py`) — исполнение переходов, диспетчеризация QG (`_run_qg`), откаты, синхронизация с Plane.
|
||||
- **Review/Test Parsers** (`src/review_parse.py`, ORCH-046) — defensive-извлечение дословного must-fix текста из артефактов для встраивания в `task_desc` заворота: `extract_review_findings` (P0/P1 из `12-review.md`), `extract_test_failures` (фрагмент тела `13-test-report.md`). Контракт «never raise»: любая ошибка → `""`.
|
||||
- **Quality Gates** (`src/qg/checks.py`) — проверки выхода со стадии, реестр `QG_CHECKS`.
|
||||
- **Agent Launcher** (`src/agents/launcher.py`) — запуск Claude CLI агентов в изолированном git worktree, мониторинг, auto-advance.
|
||||
- **Queue** (`src/queue_worker.py`, ORCH-1) — персистентная очередь задач (SQLite `jobs`), atomic claim, max_concurrency, ретраи, restart-safe.
|
||||
- **Agent Launcher** (`src/agents/launcher.py`) — запуск Claude CLI агентов в изолированном git worktree, мониторинг, auto-advance. **Валидация результата (ORCH-044):** `exit_code==0` считается успехом только если run-лог непустой и содержит валидный result-JSON; пустой/невалидный результат ⇒ job `failed`/retry + алерт, без авто-advance и «успешного» коммента.
|
||||
- **Preflight** (`src/preflight.py`, ORCH-1/ORCH-044) — дешёвый token-free гейт клейма: `os.path.exists(bin)` + `claude --version` + **проверка авторизации** (чтение `<AGENT_HOME>/.claude/.credentials.json` и валидности `claudeAiOauth.expiresAt`; постфактум-маркер `Not logged in`). Кешируется на `preflight_cache_ttl`. Подробнее: [ADR work-item ORCH-044](../work-items/ORCH-044/06-adr/ADR-001-preflight-auth-and-empty-result-failure.md).
|
||||
- **Queue** (`src/queue_worker.py`, ORCH-1) — персистентная очередь задач (SQLite `jobs`), atomic claim, max_concurrency, ретраи, restart-safe. Не клеймит job при `preflight=fail` (в т.ч. auth-fail).
|
||||
- **Project Registry** (`src/projects.py`, ORCH-6) — Plane project id → repo + prefix; фильтрация вебхуков по проекту.
|
||||
- **Plane Sync** (`src/plane_sync.py`) — синхронизация статусов/комментариев в Plane.
|
||||
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
# ADR-001: Token-free auth-preflight + «пустой результат = провал» в запуске агента
|
||||
|
||||
**Work Item:** ORCH-044
|
||||
**Статус:** Accepted
|
||||
**Дата:** 2026-06-06
|
||||
**Автор:** Architect
|
||||
|
||||
> ⛔ **Scope (коррекция владельца, 06.06):** `--effort` (P2) **исключён** из ORCH-044 и
|
||||
> вынесен в **ORCH-50**. Этот ADR покрывает только **P1** (preflight ловит авторизацию)
|
||||
> и **P3** (пустой лог / нет result-JSON ⇒ job `failed`). Любые решения по effort,
|
||||
> дефолтам `agent_effort_*` и ORCH-41 effort-докам — **вне этого ADR**.
|
||||
|
||||
---
|
||||
|
||||
## Контекст
|
||||
|
||||
Инцидент 05.06 (ORCH-17): аналитик-агент стартовал и мгновенно «умирал» — run-лог пустой
|
||||
(0 байт), job в очереди завис в `running`. Две наложившиеся причины: (1) `claude Not logged
|
||||
in` после ребилда контейнера; (2) `--effort` гасил stdout. **Системная проблема:**
|
||||
preflight пропустил заведомо нерабочую задачу в работу, а пустой результат был неотличим
|
||||
от успеха. Поскольку инстанс общий для всех проектов (self-hosting, общая очередь/БД),
|
||||
тихое зависание блокирует конвейер **всех** проектов.
|
||||
|
||||
Текущее состояние слоя запуска:
|
||||
- `src/preflight.py` проверяет только `os.path.exists(bin)` и `claude --version`. `--version`
|
||||
отвечает успешно **даже когда claude не залогинен** (версия — локальная информация) ⇒
|
||||
preflight слеп к авторизации.
|
||||
- `src/agents/launcher.py::_monitor_agent` трактует `exit_code == 0` как успех **независимо
|
||||
от формы stdout** (комментарий в `_spawn`, стр. 302) ⇒ пустой лог + exit 0 → `done` +
|
||||
авто-advance стадии.
|
||||
|
||||
Ограничения (BR-1): preflight обязан быть **локальным и token-free** — никакого prompt-ping
|
||||
и сетевых вызовов к API модели.
|
||||
|
||||
## Решение
|
||||
|
||||
### P1 — Preflight ловит авторизацию (комбинация проактивной и постфактум-проверок)
|
||||
|
||||
Реализуем **оба** подхода из TR-1.2 (a + b), проактивный — основной гейт, постфактум —
|
||||
защитная сетка.
|
||||
|
||||
**(a) Проактивно — чтение файла учётных данных (основной гейт).**
|
||||
`preflight._compute()` после успешного `--version` выполняет `_check_auth()`:
|
||||
1. Резолвит путь к credentials **согласованно с HOME, под которым launcher реально спавнит
|
||||
claude** (`/home/slin`), а НЕ из окружения процесса оркестратора. Реализуется зеркально
|
||||
`_claude_bin()`: новый `_agent_home()` читает `AgentLauncher.AGENT_HOME` (новая константа,
|
||||
значение `/home/slin`), путь = `settings.claude_credentials_path` если задан, иначе
|
||||
`<AGENT_HOME>/.claude/.credentials.json`.
|
||||
2. Файла нет / нечитаем / невалидный JSON ⇒ `(False, "claude not logged in: credentials …")`.
|
||||
3. Нет блока `claudeAiOauth` / accessToken ⇒ `(False, "not logged in: no oauth token")`.
|
||||
4. `claudeAiOauth.expiresAt` (epoch **ms**) `<= now_ms (+ skew)` ⇒
|
||||
`(False, "OAuth token expired at <iso>")`.
|
||||
5. accessToken есть, но `expiresAt` отсутствует/не число ⇒ **OK** (нельзя доказать истечение;
|
||||
не плодим ложные срабатывания — см. Риски).
|
||||
6. Иначе ⇒ `(True, "auth ok")`.
|
||||
|
||||
`_check_auth()` **никогда не бросает**: любое исключение → `(False, "auth check error: …")`
|
||||
(fail-safe в сторону «не клеймить», BR-2 / TR-3.5).
|
||||
|
||||
Кеширование (TR-1.4 / AC-6): чтение файла встроено в `_compute()`, который уже кешируется
|
||||
`check()` на `preflight_cache_ttl`. **Отдельный кеш не вводится** — auth-чтение происходит
|
||||
только на cache-miss, как и `--version`.
|
||||
|
||||
Гейтинг клейма (TR-1.5 / AC-4 / BR-2): **изменений в `queue_worker._drain_once` не требуется**
|
||||
— он уже не клеймит job при `ok=False`. Информативный auth-reason автоматически попадает в
|
||||
`worker.last_preflight_reason` и `/queue` (без изменения схемы ответа).
|
||||
|
||||
**(b) Постфактум — маркер `Not logged in` в run-логе (защитная сетка).**
|
||||
Если агент всё-таки стартовал при протухшей сессии (гонка: токен истёк между preflight и
|
||||
спавном), `launcher` при финализации детектит auth-маркер в логе
|
||||
(`preflight.is_auth_failure_text(text)`: «not logged in», «please run /login»,
|
||||
«unauthorized», «401») и:
|
||||
- включает маркер в `error` job;
|
||||
- вызывает `preflight.reset_cache()`, чтобы **следующий тик воркера переоценил auth
|
||||
проактивно** (быстрый подхват re-login ИЛИ дальнейшее гейтирование, если всё ещё битый).
|
||||
|
||||
Auth-провал **не** маршрутизируется как transient (это не 429) и **не** крутит брейкер —
|
||||
правильный механизм гейтирования здесь preflight, а не circuit breaker.
|
||||
|
||||
### P3 — Пустой лог / нет result-JSON ⇒ провал job
|
||||
|
||||
В `_monitor_agent` для ветки `exit_code == 0` вводим **валидацию результата** перед тем как
|
||||
считать job успешным. Новый защитный хелпер `_validate_result(output_path) -> (ok, reason)`:
|
||||
- лог отсутствует / пустой (size 0 или только whitespace) ⇒ невалиден;
|
||||
- иначе извлекаем result-JSON **тем же контрактом**, что usage-учёт
|
||||
(`usage._extract_last_json_object` / `parse_usage_from_text`); нет валидного объекта ⇒
|
||||
невалиден;
|
||||
- хелпер обёрнут try/except и **не роняет монитор**; при собственной ошибке —
|
||||
fail-safe в сторону провала (TR-3.5).
|
||||
|
||||
`success = (exit_code == 0 and result_ok)`. Побочные эффекты успеха выполняются **только при
|
||||
`success`**:
|
||||
- `_post_usage_comments(...)` (успешный status-коммент) — **не** постится при невалидном
|
||||
результате (AC-12);
|
||||
- `_try_advance_stage(...)` — **не** вызывается при невалидном результате (AC-12);
|
||||
- при `exit_code == 0 and not result_ok` шлётся Telegram-алерт о «пустом/невалидном
|
||||
результате».
|
||||
|
||||
Финализация job (`_finalize_job` получает новый флаг `result_ok`):
|
||||
- `exit_code == 0 and result_ok` ⇒ `done` (как раньше, AC-13 — без регрессии);
|
||||
- `exit_code != 0` **ИЛИ** `result_ok == False` ⇒ путь провала:
|
||||
- классификация лога `error_classifier.classify_log_file` (по умолчанию **permanent**;
|
||||
transient-маркер уводит в transient-путь — TR-3.3);
|
||||
- permanent: `attempts < max_attempts` ⇒ requeue (`queued`), иначе `failed` + алерт;
|
||||
- `error` информативен: `empty run log / no result JSON (run_id=…)` для случая пустого
|
||||
результата.
|
||||
|
||||
Реальный `exit_code` по-прежнему пишется в `agent_runs` без искажения; на решение
|
||||
done/fail влияет отдельный флаг `result_ok`, а не подменённый код выхода.
|
||||
|
||||
`exit_code == 0` теперь **всегда** завершается терминально/ретраябельно (`done` |
|
||||
`failed` | `queued`) — путь «быстрая смерть с exit 0 → вечный running» закрыт (AC-14, BR-3).
|
||||
Watchdog ORCH-7 продолжает закрывать таймауты.
|
||||
|
||||
### Конфигурация (config.py)
|
||||
|
||||
| Настройка | Env | Default | Назначение |
|
||||
|-----------|-----|---------|------------|
|
||||
| `preflight_check_auth` | `ORCH_PREFLIGHT_CHECK_AUTH` | `True` | Вкл/выкл auth-проверку (аварийный тумблер) |
|
||||
| `claude_credentials_path` | `ORCH_CLAUDE_CREDENTIALS_PATH` | `""` | Явный путь; пусто ⇒ `<AGENT_HOME>/.claude/.credentials.json` |
|
||||
| `auth_expiry_skew_seconds` | `ORCH_AUTH_EXPIRY_SKEW_SECONDS` | `0` | Запас на рассинхрон часов при сравнении `expiresAt` |
|
||||
|
||||
`agent_effort_*` дефолты и `--effort` в `_spawn` — **не трогаем** (scope, ORCH-50).
|
||||
|
||||
## Альтернативы
|
||||
|
||||
- **A1. Prompt-ping (ping→pong) для проверки auth.** ❌ Запрещено BR-1 (жжёт rate limit,
|
||||
латентность). Отвергнуто.
|
||||
- **A2. Только постфактум-маркер (чистый вариант b).** Ловит auth лишь ПОСЛЕ спавна и траты
|
||||
цикла; не гейтирует клейм. Оставлен как защитная сетка, но не как основной механизм.
|
||||
- **A3. Сетевая валидация токена у провайдера.** Нарушает «preflight локальный» (TR-1.6),
|
||||
добавляет сетевую зависимость в горячий путь воркера. Отвергнуто.
|
||||
- **A4. Подменять exit_code на ненулевой при пустом результате.** Исказило бы
|
||||
`agent_runs.exit_code` и классификацию. Выбрали отдельный флаг `result_ok`.
|
||||
- **A5. Отдельный кеш для auth.** Избыточно — `_compute()` уже под общим TTL.
|
||||
|
||||
## Последствия
|
||||
|
||||
**Плюсы.**
|
||||
- Заведомо нерабочая (не залогинен / протухший токен) задача **не клеймится** — экономия
|
||||
цикла и отсутствие тихого зависания.
|
||||
- Пустая «быстрая смерть» агента теперь видима: `failed`/retry + алерт вместо ложного `done`
|
||||
и движения стадии вперёд по пустому результату.
|
||||
- Без изменения схемы БД, без новых QG/стадий, без новых HTTP-endpoint'ов.
|
||||
- Auth-reason виден в `/queue` для диагностики.
|
||||
|
||||
**Минусы / ограничения.**
|
||||
- **Риск ложноположительного auth-fail** (см. `10-tech-risks.md` R-1): неверно
|
||||
резолвленный путь к credentials заблокирует клейм **всех** проектов (общая очередь).
|
||||
Митигируется: единый источник HOME (`AGENT_HOME`), тумблер `ORCH_PREFLIGHT_CHECK_AUTH`,
|
||||
обязательная проверка на staging (8501) перед прод-деплоем.
|
||||
- Проверка `expiresAt` — локальная; реально отозванный, но ещё не истёкший токен ловится
|
||||
только постфактум-маркером (b).
|
||||
- `expiresAt`-отсутствие трактуется как OK (компромисс против ложных срабатываний).
|
||||
|
||||
**Self-hosting.** Изменения только в слое preflight/launch; **не** требуют рестарта/падения
|
||||
прод-контейнера `orchestrator` в рамках задачи. Выкатка — через staging-гейт (AC-17).
|
||||
|
||||
## Связи
|
||||
|
||||
- BRD `01-brd.md` (P1, P3), ТЗ `02-trz.md` (TR-1.x, TR-3.x; scope-коррекция),
|
||||
Acceptance `03-acceptance-criteria.md` (AC-1…AC-6, AC-10…AC-17).
|
||||
- Риски: `10-tech-risks.md`. Инфра: `07-infra-requirements.md`. БД: `08-data-requirements.md`.
|
||||
- Код: `src/preflight.py`, `src/agents/launcher.py` (`_monitor_agent`, `_finalize_job`),
|
||||
`src/config.py`, `src/usage.py` (`_extract_last_json_object`),
|
||||
`src/error_classifier.py` (`classify_log_file`), `src/queue_worker.py` (без изменений).
|
||||
- ORCH-1 (очередь/resilience), ORCH-7 (watchdog), ORCH-41 (resolver — **не трогаем effort**).
|
||||
- **ORCH-50** — полноценный возврат `--effort` (вынесен из этой задачи).
|
||||
46
docs/work-items/ORCH-044/07-infra-requirements.md
Normal file
46
docs/work-items/ORCH-044/07-infra-requirements.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# 07 — Требования к инфраструктуре
|
||||
|
||||
**Work Item:** ORCH-044
|
||||
**Основано на:** ADR-001, ТЗ `02-trz.md`
|
||||
|
||||
## Топология
|
||||
**Без изменений.** Новых контейнеров, портов, сервисов, очередей не вводится. Прод
|
||||
`orchestrator` (8500) и staging `orchestrator-staging` (8501) остаются как есть
|
||||
(`docs/operations/INFRA.md`).
|
||||
|
||||
## Учётные данные claude (P1)
|
||||
- Launcher спавнит claude с `HOME=/home/slin` (`src/agents/launcher.py`). Preflight ДОЛЖЕН
|
||||
резолвить путь к credentials от **этого же** HOME, а не от окружения процесса оркестратора.
|
||||
- Ожидаемое расположение файла OAuth-токена: **`/home/slin/.claude/.credentials.json`**
|
||||
(структура: `claudeAiOauth.expiresAt` — epoch **ms**).
|
||||
- Файл — секрет; в гит НЕ коммитится (правило агентов №8). На хосте монтируется в контейнер
|
||||
как раньше; задача его расположение **не меняет**, только начинает читать.
|
||||
- ⚠️ **Проверить на staging:** реальный путь файла внутри контейнера совпадает с
|
||||
резолвленным preflight. Несовпадение ⇒ ложный auth-fail и блок очереди (R-1).
|
||||
|
||||
## Новые переменные окружения (env-карта)
|
||||
Документировать в `docs/operations/INFRA.md` и docstring `src/config.py`:
|
||||
|
||||
| Env | Default | Назначение |
|
||||
|-----|---------|------------|
|
||||
| `ORCH_PREFLIGHT_CHECK_AUTH` | `true` | Включение token-free auth-проверки в preflight. Аварийный тумблер: `false` возвращает старое поведение (только bin + `--version`). |
|
||||
| `ORCH_CLAUDE_CREDENTIALS_PATH` | `""` | Явный путь к `.credentials.json`. Пусто ⇒ `<AGENT_HOME>/.claude/.credentials.json`. |
|
||||
| `ORCH_AUTH_EXPIRY_SKEW_SECONDS` | `0` | Запас на рассинхрон часов при сравнении `expiresAt`. |
|
||||
|
||||
`--effort` env (`ORCH_AGENT_EFFORT_*`) — **вне scope**; прод-хотфикс `ORCH_AGENT_EFFORT_*=""`
|
||||
в `.env` **оставить как есть** (ORCH-50).
|
||||
|
||||
## Эксплуатационные процедуры
|
||||
- **Аварийный откат auth-гейта без редеплоя кода:** выставить `ORCH_PREFLIGHT_CHECK_AUTH=false`
|
||||
в `.env` и перезапустить воркер обычной процедурой выката (НЕ в рамках этой задачи).
|
||||
- **Диагностика:** auth-причина видна в `GET /queue` (`preflight_reason`) и в warning-логе
|
||||
`orchestrator.preflight`.
|
||||
- **Re-login:** при детекте auth-маркера в логе launcher сбрасывает preflight-кеш, поэтому
|
||||
после ручного `claude /login` следующий тик воркера (≤ `preflight_cache_ttl`) подхватит
|
||||
валидную сессию автоматически.
|
||||
|
||||
## Self-hosting / деплой (AC-17)
|
||||
- Изменения только в слое preflight/launch — **не** требуют рестарта/падения прод-контейнера
|
||||
в рамках задачи.
|
||||
- Выкатка self-доработки ORCH — **через staging-гейт (8501)** перед прод-деплоем
|
||||
(CLAUDE.md, `docs/operations/INFRA.md`, ADR-0003).
|
||||
23
docs/work-items/ORCH-044/08-data-requirements.md
Normal file
23
docs/work-items/ORCH-044/08-data-requirements.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# 08 — Требования к схеме БД
|
||||
|
||||
**Work Item:** ORCH-044
|
||||
**Основано на:** ADR-001, ТЗ `02-trz.md` §4
|
||||
|
||||
## Вердикт: изменений схемы НЕ требуется
|
||||
|
||||
Новых таблиц, колонок, индексов, миграций — **нет**.
|
||||
|
||||
P1 (auth-preflight) и P3 (пустой результат ⇒ провал) работают на **существующих** структурах:
|
||||
|
||||
- **`jobs`** — повторно используются существующие колонки для пути провала:
|
||||
`status` (`queued`/`running`/`done`/`failed`), `error`, `attempts`, `max_attempts`,
|
||||
`transient_attempts`, `available_at`, `run_id`. Пустой/невалидный результат идёт тем же
|
||||
путём, что и обычный permanent/transient провал (`mark_job` / `mark_job_transient`).
|
||||
- **`agent_runs`** — `exit_code` пишется без искажения (реальный код выхода процесса).
|
||||
Решение done/fail принимается по отдельному in-memory флагу `result_ok` в `_monitor_agent`,
|
||||
а не по колонке.
|
||||
|
||||
## Состояние данных
|
||||
- Никаких бэкофиллов / data-migration.
|
||||
- Auth-проверка читает **файл** `.credentials.json` (вне БД), результат кешируется in-memory
|
||||
(`preflight._cache`), не персистится.
|
||||
20
docs/work-items/ORCH-044/10-tech-risks.md
Normal file
20
docs/work-items/ORCH-044/10-tech-risks.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# 10 — Технические риски
|
||||
|
||||
**Work Item:** ORCH-044
|
||||
**Основано на:** ADR-001
|
||||
|
||||
| ID | Риск | Вероятн. | Влияние | Митигация |
|
||||
|----|------|----------|---------|-----------|
|
||||
| R-1 | **Ложноположительный auth-fail.** Неверно резолвленный путь к `.credentials.json` (иной HOME/маунт) ⇒ preflight всегда FAIL ⇒ **не клеймится ни одна job всех проектов** (общая очередь, self-hosting). | Средняя | **Высокое** | Единый источник HOME (`AgentLauncher.AGENT_HOME`, зеркально `_claude_bin()`); тумблер `ORCH_PREFLIGHT_CHECK_AUTH=false`; **обязательная проверка на staging** (реальный путь == резолвленный) перед прод-деплоем; информативный reason в `/queue` + warning-лог. |
|
||||
| R-2 | **Fail-safe-провал на легитимном пустом выводе.** Агент легитимно завершился `exit 0` с непустым логом, но `_validate_result` ошибочно счёл результат невалидным ⇒ ложный `failed`/requeue (регрессия AC-13). | Низкая | Среднее | Контракт извлечения JSON — тот же, что у работающего usage-учёта (`_extract_last_json_object`); регресс-тест TC-15 (валидный лог ⇒ `done`); валидатор не трогает успешный путь, кроме булева флага. |
|
||||
| R-3 | **`expiresAt` без сетевой валидации.** Реально отозванный, но ещё не истёкший по времени токен пройдёт проактивную проверку (a). | Средняя | Среднее | Защитная сетка постфактум (b): маркер `Not logged in` в логе ⇒ `error` + `preflight.reset_cache()` ⇒ следующий тик переоценивает auth; полная сетевая валидация — вне scope (BR-1). |
|
||||
| R-4 | **`expiresAt` отсутствует/нечисловой** в файле (иная версия CLI / иной формат) ⇒ проверка трактует как OK и пропускает. | Низкая | Низкое | Осознанный компромисс против ложных срабатываний (см. ADR §P1.5); отсутствие токена/accessToken по-прежнему ⇒ FAIL; постфактум-маркер ловит реальный «не залогинен». |
|
||||
| R-5 | **Часовой рассинхрон** контейнер vs токен ⇒ валидный токен сочтён истёкшим. | Низкая | Среднее | `ORCH_AUTH_EXPIRY_SKEW_SECONDS` (default 0) для запаса; контейнеры на одном хосте (mva154) — рассинхрон маловероятен. |
|
||||
| R-6 | **Транзиентный auth (битый JSON в момент записи re-login).** Чтение файла во время атомарной перезаписи ⇒ временный FAIL. | Низкая | Низкое | Кеш TTL сглаживает; следующий тик перечитает; fail-safe в сторону «подождать» (job остаётся `queued`, не теряется). |
|
||||
| R-7 | **Конфликт test-plan с коррекцией scope.** `04-test-plan.yaml` TC-09/TC-10/TC-11 проверяют `--effort` (variant B: «`--effort` не формируется»), но владелец **исключил** effort из ORCH-044 и оставил дефолты `agent_effort_*` = `high`. При дефолтной тест-конфигурации `_spawn` сформирует `--effort high` ⇒ TC-09 (ожидающий отсутствие флага) **упадёт**. | **Высокая** | Среднее | Developer/Tester: **адаптировать TC-09/10/11** под «effort не трогаем» (assert успешной сборки cmd без требования удаления флага, либо пометить как deferred→ORCH-50). Артефакт `04-test-plan.yaml` — чужой этап (правило №3), архитектор его НЕ редактирует, только фиксирует расхождение здесь. AC-7/AC-8/AC-9 не применяются (см. `03-acceptance-criteria.md` §P2). |
|
||||
| R-8 | **Постфактум auth-сброс кеша зацикливает.** Повторные auth-провалы ⇒ повторные `reset_cache()`. | Низкая | Низкое | `reset_cache()` лишь форсирует один пересчёт; следующий `check()` снова закеширует на TTL; цикла «горячего» чтения нет; job не клеймится при FAIL. |
|
||||
|
||||
## Сводно
|
||||
Доминирующий риск — **R-1** (блок очереди ложным auth-fail при неверном пути) и
|
||||
организационный **R-7** (test-plan vs scope). Оба закрываются: R-1 — staging-проверкой +
|
||||
тумблером, R-7 — правкой effort-тестов разработчиком/тестером согласно коррекции владельца.
|
||||
Reference in New Issue
Block a user