doctor · git:20260907.ad5db37 · 2026-09-07 · sha256 da0d04d3cb9695d7

doctor git:20260907.ad5db37A

Immutable. This exact content is served forever at /api/v1/blob/da0d04d3cb9695d7.

---
name: doctor
description: Diagnose Polisade Orchestrator project health
---

# /polisade:doctor — Project Health Diagnostics

Read-only диагностика здоровья Polisade Orchestrator-проекта. Проверяет структуру, файлы состояния, инструменты и консистентность.

**Status vocabulary check** (`artifact_statuses`, issue #151): статус артефакта
вне закрытого словаря (`scripts/_polisade_state_model.py`) — почти всегда опечатка,
и она молча выкидывает артефакт из ВСЕХ производных списков `PROJECT_STATE.json`
(в `artifactIndex` он при этом остаётся). Проверка даёт WARN и перечисляет
нарушителей; переходы статусов нигде не enforce'ятся — это тонкий stdlib-клиент.

**Pre-push hook check** (`prepush_hook`, issue #159): установлен ли git-хук
`/polisade:init`, запрещающий push в `main`/`master` без
`POLISADE_ALLOW_MAIN_PUSH=1` и push ветки, отличной от `POLISADE_EXPECTED_BRANCH`
(когда та задана). Каталог хуков резолвится через `git rev-parse --git-path hooks`
— `core.hooksPath` и worktree учитываются. WARN, а не FAIL: хуки не клонируются,
не версионируются и обходятся `--no-verify`, так что это ремень безопасности перед
серверной защитой веток, а не она сама. В сообщении WARN — готовая команда
установки; чужой существующий `pre-push` doctor не трогает и не предлагает
перезаписать.

**Vendored scripts check** (`scripts_vendor`, issue #127): в сборках, где
Python-скрипты плагина исполняются из копии в проекте (`.polisade/bin`, потому
что каталог установки закрыт Filesystem Guard'ом), сверяет копию с
`.polisade/bin/MANIFEST.sha256` — версия плагина плюс sha256 каждого файла.
Отсутствие копии под такой сборкой — FAIL с командой установки; нехватка файлов
или несовпавший хэш — FAIL «копия устарела или искажена»; версия манифеста
старше версии проекта — WARN. Отличие ТОЛЬКО в переводе строки (CRLF) —
отдельный WARN с причиной (`git core.autocrlf`), а не «искажена»: содержимое то
же. Права на файлы не проверяются — хэш считается по байтам. Если `.polisade/bin`
нет и сборка не вендорит скрипты, проверка PASS'ит как неприменимая. Сборка
определяется тремя ИЛИ-сигналами, ни один из которых не читает каталог
установки: `POLISADE_PLUGIN_ROOT` с `.gigacode/`, заголовок `# target: gigacode`
в манифесте, либо `GIGACODE.md` как ЕДИНСТВЕННЫЙ контекстный файл проекта.

**VCS provider check** (встроенный в дефолтный отчёт + отдельный режим `--vcs`):
под сборкой GigaCode `vcsProvider: github` даёт WARN (issue #120): GitHub
недоступен по сети этой инсталляции, дефолт там — `bitbucket-server`. Если `settings.vcsProvider == "bitbucket-server"` — проверяет наличие `.env`, что хотя бы один `BITBUCKET_DOMAIN{1,2}_URL` и `_TOKEN` заполнены (не stub-значения), что хост `git remote origin` совпадает с одним из заполненных доменов, и что `whoami` через `polisade_vcs.py` к матчнувшемуся инстансу возвращает 200 (через аутентифицированный endpoint — невалидный токен даст 401).

## Использование

```
/polisade:doctor                  # Диагностика текущего проекта (включая vcs_provider)
/polisade:doctor --traceability   # Traceability matrix report (text)
/polisade:doctor --traceability --format=md    # Markdown table
/polisade:doctor --traceability --format=json  # JSON для CI
/polisade:doctor --questions       # Open questions across all artifacts
/polisade:doctor --questions --format=json  # JSON для автоматизации
/polisade:doctor --vcs             # Только VCS-провайдер (быстрая диагностика токена/хоста)
/polisade:doctor --vcs --format=json  # JSON для автоматизации
```

## Алгоритм

1. Определить корень проекта (текущая рабочая директория). Preflight:
   `${POLISADE_PYTHON:-python3} --version` — не стартовал, значит **STOP**:
   интерпретатор по машине не ищем, просим PM выставить `POLISADE_PYTHON`
   (см. капсулу в `/polisade:migrate`, issue #169).
2. Запустить скрипт диагностики:

<!-- polisade:exec-denied CAPSULE BEGIN -->
> ⛔ **Вызов скрипта отклонён или не запустился** (`Command references protected path` / `Install directory is read-protected` / `Filesystem Guard` / `the tool's default permission is 'deny'`, отказ песочницы, ненулевой exit без вывода) — **STOP**.
> Процитируй отказ дословно. НЕ пересказывай по исходнику, что скрипт «сделал бы»; НЕ собирай dry-run вручную; НЕ переходи к apply/push/pr-create.
> НЕ транскрибируй скрипт (прочитать → записать копию в `/tmp` или в проект → запустить копию): копия не байт-идентична — уезжают классы символов в regex, форма возврата функций, пропадают целые функции — и молча меняется набор применённых изменений.
> Отказ инструмента — это отказ, а не результат. Доложи PM дословный текст отказа и сошлись на #127 (доставка скриптов в проект).
<!-- polisade:exec-denied CAPSULE END -->

```bash
${POLISADE_PYTHON:-python3} {plugin_root}/scripts/polisade_doctor.py {project_root}
```

Где `{plugin_root}` — корень Polisade Orchestrator плагина (директория, содержащая `scripts/`).
Команду выше бери как есть: в сборках, где скрипты вендорятся в проект
(`.polisade/bin`, issue #127), путь к скрипту уже подставлен конвертером — не
подменяй его на корень плагина.

3. Распарсить JSON-ответ скрипта.
4. Вывести результат в box-формате.

## Формат вывода

```
═══════════════════════════════════════════
Polisade Orchestrator DOCTOR
═══════════════════════════════════════════

[PASS] project_state — .state/PROJECT_STATE.json
[PASS] counters — .state/counters.json
[PASS] knowledge — .state/knowledge.json
[PASS] templates — 9 templates found
[PASS] backlog_dir — backlog/
[PASS] tasks_dir — tasks/
[PASS] architecture_dir — docs/architecture/
[PASS] gh_auth — Logged in as user
[FAIL] codex_cli — Command not found: codex
[PASS] state_schema — v2.8.1, schema 2
[WARN] artifact_sync — Orphan files: TASK-005
[PASS] design_packages — 2 design packages, all files present
[PASS] artifact_statuses — all artifact statuses in the known vocabulary
[WARN] prepush_hook — .git/hooks/pre-push not found. install: ...
[PASS] scripts_vendor — .polisade/bin: 22 файл(ов) совпали с MANIFEST.sha256 (версия 3.7.6)

───────────────────────────────────────────
Summary: 8 pass, 1 warn, 1 fail
═══════════════════════════════════════════
```

## Traceability Matrix

<!-- polisade:silo-legacy POINTER — канон «Силос → корпус» живёт в /polisade:design -->
> **Силос ≠ корпус.** Источник правды по архитектуре — живой корпус
> `docs/architecture/`; пакет `DESIGN-NNN-<slug>/` — legacy-силос. Прочитал
> файл из силоса — скажи об этом вслух (переходное чтение). Полный канон —
> `/polisade:design`, блок «Силос → корпус»; перевод силоса на корпус —
> `${POLISADE_PYTHON:-python3} scripts/polisade_migrate_silo.py <пакет>` (dry-run по умолчанию).

Режим `--traceability` строит матрицу прослеживаемости требований:

```
PRD/SPEC/FEAT FR/NFR → DESIGN sub-artifacts (realizes_requirements) → TASK (requirements:)
```

Парсит:
- `docs/prd/PRD-*.md`, `docs/specs/SPEC-*.md`, `backlog/features/FEAT-*.md` — FR-NNN (### headings) и NFR-NNN (table rows)

- `docs/architecture/DESIGN-*/manifest.yaml` — `realizes_requirements` и ADR `addresses`
- `tasks/TASK-*.md` — `requirements:` frontmatter + `status:`

IDs в матрице приводятся к composite формату `{DOC}.FR-NNN` — `PRD-001.FR-007` и `FEAT-002.FR-007` показываются в отдельных секциях и никогда не сливаются, даже если совпадает номер.

Пример вывода:

```
════════════════════════════════════════════════════════════
TRACEABILITY MATRIX
════════════════════════════════════════════════════════════

⚠️  AMBIGUOUS REFERENCES DETECTED
────────────────────────────────────────────────────────────
FR-007: defined in PRD-001, FEAT-002
    bare ref at tasks/TASK-003-foo.md (as `FR-07`)
Run /polisade:migrate --apply to attach scope prefixes automatically.
────────────────────────────────────────────────────────────

SPEC-001 → DESIGN-001

Requirement          Realized in DESIGN           Tasks                 Status
──────────────────── ──────────────────────────── ──────────────────── ────────────────
SPEC-001.FR-001      api.md, c4-container.md      TASK-001, TASK-005   done
SPEC-001.FR-002      api.md                       TASK-002             review
SPEC-001.FR-003      (none)                       (none)               ❌ NOT COVERED
SPEC-001.NFR-001     quality-scenarios.md         TASK-008             done
SPEC-001.NFR-002     ADR-001                      (none)               ⚠️ NO TASK

────────────────────────────────────────────────────────────
Total: 5 (3 FR + 2 NFR)
Coverage: 4/5 (80%)
  Realized in design: 4/5
  Has tasks: 3/5
  Done: 2/5
  Not covered: SPEC-001.FR-003
════════════════════════════════════════════════════════════
```

Секция «AMBIGUOUS REFERENCES» — **non-blocking warning**: она появляется, когда один и тот же FR/NFR объявлен в >1 top-level документе И хотя бы одна cross-doc ссылка сделана bare. Сама по себе она не меняет exit code — блокировка ambiguous refs это работа `polisade_lint_artifacts.py`.

Exit code: 0 если все требования покрыты (design или tasks), 1 если есть uncovered. Breaking change в v2.22.0: JSON root — теперь объект `{"matrix": [...], "ambiguous_refs": [...]}` вместо массива (читай `result.matrix[]` вместо `result[]`).

## Важно

- **Read-only** — ничего не модифицирует
- Для исправления drift используй `/polisade:sync`
- Для исправления schema warnings используй `/polisade:migrate`
- Когда `/polisade:doctor` советует `/polisade:sync` или `/polisade:migrate` — после `--apply` смотри раздел «После применения — закоммить и открыть PR» в этих скиллах (issue #108): canonical 7-шаговый рецепт довоза diff'а до PR через `polisade_vcs.py git-push` + `polisade_vcs.py pr-create --body-file`.
- Для установки Codex CLI: `npm install -g @openai/codex` ИЛИ `brew install openai-codex` (документация: https://github.com/openai/codex)