# 08 — CONTOUR / AGENT-OPS GUIDE > snapshot: 2026-07-02, git 5302aacb2 — how the autonomous swarm ("contour") builds/ships note-clone Everything below lives in a **separate repo** `/Users/poolpooly/Projects/nc-agent-ops` (not note-clone). The board drives coding agents that branch, build, deploy note-clone to the Pi. Runbook of record: `board/CONTOUR-RUNBOOK.md`. Onboarding: `board/README.md`. --- ## 1. The board (single source of truth) | item | path | notes | |---|---|---| | tickets DB (authoritative) | `board/issues.json` | **NEVER hand-edit** — 1380 issues, atomic-write + lock only | | tickets DB (hot mirror) | `board/board.sqlite` | SQLite (node:sqlite, WAL) mirror since 2026-07-03 cutover; render reads it, writers dual-write it | | event log | `board/events.jsonl` | append-only audit (~34MB); every update logged | | rendered board | `board/board.html` (via `render-board.mjs`) | human view | | **guarded updater** | `board/update-issue.mjs` | the ONLY sanctioned way to mutate a ticket | | lock helper | `board/board-lock.mjs` | `withBoardLockSync` wraps every write | | status ranks / guard | `board/status-guard.mjs` | defines valid statuses + regression guard | ### Board storage — SQLite backend (cutover 2026-07-03) The board is now **SQLite-backed** while JSON stays authoritative during a >=2-week transition: - `board/board.sqlite` (node:sqlite, WAL mode) is a hot mirror of the board. - **Reads:** `render-board.mjs` reads from SQLite with a try/catch fallback to `issues.json` (board header shows `источник: SQLite (+JSON fallback)`). - **Writes:** sanctioned writers (`board/update-issue.mjs` canonical + `board/server-board.mjs` drag API) write JSON (authoritative) **and** mirror to SQLite (dual-write, best-effort — a SQLite failure never breaks the JSON write). Both stay under the atomic `board/board-lock.mjs` lock. - **Catch-all sync:** `board/watch-board.mjs` (launchd `com.poolpooly.nc-board-watch`, RunAtLoad + KeepAlive → survives reboot) re-syncs SQLite on any `issues.json` change, so even out-of-band JSON edits reach SQLite. - **JSON stays authoritative** + backup + fallback for >=2 weeks, then a later flip makes SQLite authoritative. `issues.json` is NOT deleted. - **Tools:** `board/board-db.mjs` (DB dependency) · `board/json2sqlite.mjs` (re-import JSON→SQLite) · `board/parity-check.mjs` (JSON↔SQLite audit). - **Agents: nothing changes.** Read the board via render/API as before; write via `board/update-issue.mjs --id X --status Y` as before. SQLite is transparent — the writer mirrors it for you. Never write `issues.json` directly (that was never the path — only `update-issue.mjs` under the lock). ### update-issue.mjs flags (`node board/update-issue.mjs …`) `--id TICKET` · `--create` (new ticket, needs `--id`, default status Intake) · `--status "QA WIP"` · `--set key=value` (typed: null/bool/number/JSON auto-parsed; repeatable) · `--label X` / `--remove-label X` (repeatable) · `--actor NAME` · `--summary TEXT` · `--expected-updated-at ISO` (optimistic-lock) · `--force` (override regression/branch guards) · `--require-verify` (Deployed needs `.agent_runs//verify.json` verdict=PASS; default OFF). Built-in guards (update-issue.mjs:150-230): **status_regression_guard** (`isUnsafeStatusRegression`, override w/ `--force`); **build_candidate_needs_branch** (can't enter Build Candidate without a real `branch`); **verify-gate** + locale sub-gate when `--require-verify`. --- ## 2. Ticket lifecycle (statuses) Authoritative set = `board/status-guard.mjs` (rank in parens; higher = further right): `Intake`(0) · `Legacy Triage`(0) · `Parked`(5) · `Dev WIP`(10) · `Needs Proof`(15) · `Blocked`(15) · `Dev Green`(20) · `Rejected by QA`(25) · `QA WIP`(30) · `QA Verified`(40) · `Rejected by Lead`(45) · `Lead Review`(50) · `Build Candidate`(60) · `Rollback Required`(65) · `Deployed`(70) · `Owner Accepted`(80). Also seen live: `Parked`, `Owner Rejected`. Regression to a lower rank is blocked unless from `Parked/Blocked/Needs Proof/Rejected by *` or to `Rollback Required` (status-guard.mjs:44-50). **Happy path** (CONTOUR-RUNBOOK.md §2): `Dev WIP → Build Candidate → (build-steward: provider artifact + M1 Pi deploy) → Deployed → (verify-gate PASS) → Owner Accepted`. FAIL→Dev WIP · INCONCLUSIVE→Needs Proof. `Artifact Built` ≠ `Deployed`. Goal: work EXITS (Accepted), doesn't loop. **Current distribution (1380):** Deployed 825, Owner Accepted 428, Parked 45, Needs Proof 40, Rejected by Lead 17, Dev WIP 14, Blocked 4, Lead Review 3, others ≤1. --- ## 3. Dispatch pipeline (launchd `com.poolpooly.*`) ``` auto-groom → auto-dispatch → route-issue (classifier) → queue → model → worker (branch off DEPLOYED base) ↓ Build Candidate build-steward → M4/GitHub/Google artifact → M1 pi-deploy → verify-gate → Deployed postqa-supervisor → post-deploy headless QA ``` | daemon | script | role | |---|---|---| | `auto-groom` | `board/auto-groom.sh` | promotes Intake→`Dev WIP`+label `auto-dispatch` so pool never starves (TARGET=4). Skips telephony/FC/deep-research, epic PARENTs (`ЭПИК:`), already-groomed. **≤1 ORB in flight** (shared EditorModal/orbAgentLoop) | | `auto-dispatch` | `note-clone/scripts/auto-dispatch.ts` (bootstrap `board/auto-dispatch-bootstrap.sh`) | spawns workers, cap **AUTO_DISPATCH_MAX_ACTIVE=4**. GATED statuses never dispatched: Parked/Blocked/Build Candidate/Deployed/Owner Accepted/Rejected/Rollback (auto-dispatch.ts:111-118,150). Workers branch from DEPLOYED base worktree | | release/build steward | `board/build-steward.sh` is the Google/deploy fallback | Build Candidate → operator selects M4→GitHub→Google8→Google32 → M1 `scripts/pi-deploy.sh` → verify-gate → Deployed. One lifecycle lease; no parallel builds/deploys | | `postqa-supervisor` | `board/postqa-pi.sh` / `postqa-dispatch.sh` | post-deploy headless QA on Needs Proof/Deployed. variant-B: CAP=1 + Pi-load>6 guard | | `deploy-candidate-guard` (3min) | `board/deploy-candidate-guard.sh` | releases owner-OK / `deploy-as-candidate` tickets out of Lead's build-gate → Build Candidate + kick steward. Excludes VOICE/codex + incomplete. Log `/tmp/deploy-candidate-guard.log` | | `churn-breaker` (10min) | `board/churn-breaker.sh` | ticket with **≥5 spawns** and no live worker → `Needs Proof` + labels `needs-live-verify-human`+`do-not-redispatch`+`held-off-rework`, clears tmuxSession. Skips live-worker/`engineer-handling`/terminal. Log `/tmp/churn-breaker.log` | | `contour-resurrector` / `contour-boot-restore` | `~/bin/contour-resurrector.cjs` | revive lead+workers after death/reboot; respects held-off-rework patterns | | `engineer-ownership-guard` | `board/engineer-ownership-guard.sh` | stops dispatcher grabbing engineer-owned tickets | | `mem-guard` / `session-reaper` / `steward-guard` / `contour-watchdog` | — | RAM defense, zombie-session reaping, steward protection, rekick | --- ## 4. Queue routing (classifier) — `.agent/route-issue.mjs` + `.agent/policy.yaml` **Rule-based, NO LLM.** Queue depends on **cost-of-error** (sensitive paths + blast-radius), NOT priority. Shadow-by-default unless `HARNESS_ENABLED=1`. Writes `.agent_runs//route.json`, appends `board/harness-shadow.jsonl`. QUEUE_RANK (route-issue.mjs:239): `cheap_autopilot < standard_agent < premium_plan_review < human_gate`. | queue | model (route-issue.mjs:816-820) | when | |---|---|---| | `premium_plan_review` | **Opus** (native, burst only) | auth-adjacent premium, migration/schema/prisma/db, middleware, race/deadlock/concurrency, data-loss/corruption, board/build-steward/auto-dispatch (ops area), areas build/infra/ops/security/auth/release, blast-radius > threshold | | `standard_agent` | **DeepSeek-pro** (`deepseek-v4-pro`) | default; feature areas (chat/editor/studio/viz/focus/notebook/api…), i18n runtime logic, owner-facing UX (studio/orb/radar/show/viz pinned even if title has cheap keywords, route-issue.mjs:531-548) | | `cheap_autopilot` | **DeepSeek-flash** (`deepseek-v4-flash`) | copy/text/typo/css/style/color/cosmetic, translation-only i18n, areas ui/uiux/ux/art, P3 no-sensitive-hits | | `human_gate` | **SKIP (never spawned)** | auth/oauth/jwt/payment/billing/csrf/xss/injection/privilege keywords; `no-auto-dispatch` label; codex-owned | | `codex_zone_untouchable` | **SKIP** (→human_gate effective) | VOICE/telephony paths (policy.yaml sensitive_paths) | **Model routing via labels:** `force-opus`/`needs-opus` escalate a specific ticket to Opus (route-issue.mjs:537-542) — set only on genuinely hard/shared-layer work. `deepseek-ok` = fine for DeepSeek. `no-auto-dispatch` → human_gate (route-issue.mjs:606). Codex zone (`api-codex-owned`/`codex-zone`) → human_gate. Bootstrap really-applies (not log-only) when `HARNESS_ENABLED=1`: opus→native binary, pro/flash→`claude-deepseek.sh`, router error/timeout→flash fallback (auto-dispatch-bootstrap.sh:23-30). Per-queue limits (policy.yaml `limits_by_queue`): cheap {2 attempts/3 files/250 diff/20min}, standard {3/10/800/30min}, premium {2/20/1500/45min}, human_gate {0}. **DeepSeek workers isolated by `CLAUDE_CONFIG_DIR`** — else they clobber Opus login → 401 (RUNBOOK; MEMORY). ### policy.yaml sensitive_paths (deterministic, pre-classifier) `telephony|sip|dialer|barge-in|call-manager|*9091*|*9092*|voice/` → **codex_zone_untouchable**. `.env*` → **block**. `auth/`,`payments/` → human_gate+deny_cheap. `migrations/` → premium+deny_cheap. `board/`,`build-steward*`,`auto-dispatch*`,`middleware*` → premium. `locales/`,`translations/` → cheap-if-translation-only. `i18n/`,`intl/`,`*locale*` → standard, deny-cheap-if-runtime-logic. ### i18n / locale guard (read 15 first) The app has ~100+ locales and a large generated i18n corpus. Locale work is cheap only when it is **translation-only and preserves the existing lazy-loading shape**. Hard rule for every worker: **never import all locale JSON/modules into a top-level client/server entry, provider, layout, route, or shared barrel**. That silently turns a translation task into a build-size/runtime task and can push Cloud Build past the normal 7-10 minute range or crash locale boot for some languages. Routing rule: translation-key fill / copy-only may go cheap after an i18n plan; lazy loader, locale registry, fallback policy, generated-key tooling, bundle shape, or build-time changes go `standard_agent` or `premium_plan_review` if the blast radius is global. For all-locale expansion, first create/accept a plan ticket with key inventory, lazy-path proof, selected locale smoke (`en`, `uk`, `ru`, `pt-BR` or another historically risky locale), and a bundle/build-time guard. Details: `docs/PASSPORT/15-I18N-LOCALE-GUARD.md`. --- ## 5. Model roster & cap-saving **Lead = Opus** (`opus-lead-live` tmux, orchestrator). **Workers = DeepSeek** (cheap); Opus only on hardcore. Reserve: `codex-spark` for big batches (e.g. i18n 100+ langs). Reviewer burst: Sonnet. Purpose: conserve the weekly Opus cap. Budget governor `.agent/budget-governor.mjs` (daily_usd 0; 80%→no premium-for-standard; 100%→stop new runs). --- ## 6. Build & deploy to Pi — release steward + `board/build-steward.sh` - **Provider ladder:** M4 Lima `noteclone-build` default → GitHub Actions `build-arm64.yml` artifact-only fallback → paid Google `E2_HIGHCPU_8` → emergency one-run `E2_HIGHCPU_32` with explicit owner OK. M4 VM is on-demand and stops after artifact verification; idle VM would reserve 8 GiB needed by M4 workers. - **Paid notice:** every Google submission sends Telegram first with tickets, exact head, fallback reason and CPU profile. Google 8 app-only uses standing owner OK and does not wait for a reply; Google 32 always waits for explicit owner OK and is reset to 8 after the run. - **Boundary:** M4/GitHub never receive Pi secrets and never deploy. M1 release steward validates manifest/SHA256/native ARM64 deps/source-index worker/smoke, then runs the canonical cumulative-box deploy. - **State names:** record `Artifact Built`, `Deployed`, and `PostQA` separately. A green artifact build never proves product code is live. - **Cumulative rule:** `pi-deploy` swaps the WHOLE app, so the box = prod + ALL Build Candidate branches together, else a deploy silently rolls back prior fixes. - **GREEN rule:** never trust gcloud exit code — require literal `STATUS … SUCCESS`; else poll `gcloud builds describe ` to terminal. - **STALE-BASE GUARD** (build-steward.sh:56-65): sync PROD to the REAL sha the Pi serves (`curl nb.wool2.online/sw.js`) before assembling → prevents cumulative rollback (RUNBOOK §G). - **HOLD regex** (build-steward.sh:96): `do-not-build|do-not-deploy|hold-*|owner-ok-required|needs-owner-ok|epic-incomplete|code66-risky|…` → steward will NOT build/deploy even if dropped into Build Candidate. - Deploy: `scripts/pi-deploy.sh ` ships prebuilt artifact to `pi@mailhub.duckdns.org -p 2046`. Does NOT run `prisma migrate deploy` (migrations gated separately, owner-OK only). Prod URL **nb.wool2.online**. - Deploy sha of record: `bash board/latest-deploy-sha.sh`. **Branch ONLY from DEPLOYED base**, never `origin/main` (main is ~4774 commits behind live — contour has no push creds; RUNBOOK §I). Base worktree `autodispatch-base-351c` is kept = live by the stale-base guard. ### Deploy-candidate model (RUNBOOK §3, KEY) Where headless Playwright CAN prove pre-deploy → prove pre-deploy. Where a LIVE browser is needed (Orb behind `?companion.v2.enabled=1`, visual tickets) → prove ONLY post-deploy. Don't hold a build-gate on live-browser tickets (deadlock). **Never run M1 dev-server for Orb (OOM) → Orb tested only on prod nb.wool2.online.** --- ## 7. Comms — inbox + Monitor - Letters: `board/inbox/LETTER-NN-to--.md`; register a row in `board/inbox/INDEX.md` (owner relays the number to the addressee). - Roster: `opus-lead-live` (Opus lead, tmux) · `backup-opus` (contour surgeon) · `opus-engineer` · `API-Codex` / `codex-lead-live` (VOICE + Meeting Room owner) · `fable` · owner. - Monitor watches `to-` addressing. Drive/state docs: `board/BACKUP-OPUS-DRIVE-STATE.md`, `board/CODE94-DRIVE-STATE.md`, `board/lead-duty-state.json`, `board/OPUS-LOOP-HEARTBEAT.txt`. - Owner-facing charges: `CHARGE.md`, `QA_LEAD_CHARGE.md`, `board/postqa-charge.md`. --- ## 8. Areas, priorities, zones **ID prefixes** (top by volume): ORB 165 · FOCUS 94 · VIZ 80 · STUDIO 76 · SEC 70 · VOICE 67 · UIUX 58 · SWOT 54 · OPS 41 · AGENT 41 · CHAT 36 · BUILD 32 · LIGHTDB 27 · PODCAST 24 · SKREPKA 22 · SHOW 22 · VIZMODE 21 · AUTH 19 · GRAPH 19 · TASK 18 · PROFILE 18 · SCEN 18 · DEPLOY 17 · I18N 13 · DICTATION 13 · MEETINGROOM 12 · DREAM 11 · TEAM/ARCADE 10 · then PLAYER/COMMS/EDITOR/CODE**/WEB/FC/VIRAL/GARDENER… **Priorities:** P0–P3. P0 ≠ premium — a P0 copy-fix routes cheap; cost-of-error decides the queue. ### Zones — do NOT touch - **VOICE / telephony / SIP (ports 9091/9092)** = **Codex zone**. Contour ONLY tracks, never codes/deploys. VOICE-* ship via native release + owner OK, never the box-deploy. - **Meeting Room (MEETINGROOM-*)** = Codex-owned (5 guards protect it, see LETTER-05/06/10). - **FC / deep-research (factcheck)** = Parked / owner zone (auto-groom skips). - QA NEVER under `_@wool2.online` (owner acct) — only `qa-postqa-001@wool2.online` / `qa-team-01..05`. - Prod DB/migrations (Pi localhost:55432 via SSH tunnel) — explicit owner-OK only. - **"Deployed" ≠ "works"** — needs a browser/audio proof before Owner Accepted. --- ## 9. Troubleshooting quick-ref (CONTOUR-RUNBOOK.md §5) | symptom | fix | |---|---| | paid work stalls at release | `deploy-candidate-guard` auto-releases owner-OK; manual: `update-issue --id X --status "Build Candidate" --label owner-night-mandate-prod-deploy-ok` + kick steward | | noop-churn (ticket loops) | `churn-breaker` (≥5 spawns→Needs Proof). Count: `grep -oE 'spawning [A-Z0-9-]+' /tmp/auto-dispatch.out.log \| sort \| uniq -c \| sort -rn` | | postqa-supervisor crash (exit 144) | `launchctl bootout gui/$(id -u)/com.poolpooly.postqa-supervisor` during focus-gate; bootstrap back after | | headless drops on Orb (no verdict) | expected — use backup-opus live Chrome proof; don't headless the Orb | | M1 swap<600M / blackscreen risk | `pkill playwright`; shed non-critical workers; mem-guard backstops | | cumulative rollback | stale-base guard (steward syncs to Pi sw.js); check `git -C autodispatch-base-351c rev-parse HEAD` vs `curl -s nb.wool2.online/sw.js` | | held state re-churns | keep held ticket at `Needs Proof` + label `do-not-redispatch`/`held-off-rework`; never leave held in QA/Dev WIP (resurrector grabs it) | | build time jumps after locale/i18n work | suspect eager locale import or generated corpus bundled into main chunk; hold build, inspect diff for top-level `locales`/`i18n/generated` imports, require 15-I18N proof before retry | | lead tmux won't submit | paste with timestamp → Enter → wait 2s → guarded second Enter only if same input remains and no `Working`; verify output/child process | **Model check for a ticket:** `grep "harness-route " /tmp/auto-dispatch.out.log`. --- # КОНТУР-ОВЕРХОЛ 2026-07-03 (Fable-5) — стабилизация до само-достаточности Крупная перестройка контура. Ниже — актуальная архитектура (замещает разрозненные заметки выше по многим пунктам). ## Единая правда лидов — LEAD-CANON `board/LEAD-CANON.md` (v5) — единый источник правды, который читают ОБА лида (opus-lead-live `/lead` SIP9091 + codex-lead-live `/lead2` SIP9092) при каждом воскрешении И на каждом self-tick (§8 версия-чек). Правишь файл → оба сходятся за тик, переживает ребут. - codex-lead читает канон через симлинк `codex-lead2-channel-20260531/docs/operator/CODEX_LEAD_LIVE_CONTEXT.md → board/LEAD-CANON.md` (был заморожен на May-31 = корень войны двух лидов, вылечено). - opus-lead читает через boot-hook `~/bin/opus-lead-canon-inject.sh` (send-keys перечитать) + resume-память. - Разделы: §1 один активный директор (маркер `~/.contour-active-director`), §2 deploy-candidate, §3 held-паттерны, §9 DoD-приёмка (valid-shape 401/403), §9.1 anti-footgun, §10 проактивный blocked-tail-sweep. - Комм-мост Fable↔opus-lead идёт через backup-opus (opus-lead инбокс не читает). ## Управление моделями — Owner-Монитор (bugs.wool2.online/monitor) Живой пульт (авто-30с): директор+капы, лиды(heartbeat), стражи, DoD-фейки, требует-внимания, воркеры, доска, в-полёте, сигналы. Кнопки БЕЗ правки кода: - **Директор:** Opus / Codex / Авто (`~/.director-manual-override` + `~/.contour-active-director`). - **Провайдер-капы:** `~/.{opus,codex,deepseek}-cap-low`. - **Модели поштучно (вкл/выкл):** `~/.model-disabled-` — opus/sonnet/deepseek-pro/deepseek-flash действенны на воркерах (codex-* пока для будущего). Выключенная модель выпадает из scoring-лестницы → выбор падает на следующую доступную. - Файлы: `board/monitor.html` + `board/contour-state.mjs` (коллектор) + server-board.mjs роуты `/monitor`, `/contour-state.json`, POST `/contour-action`. ## Модель-роутинг (реальность, см. DOC-REALITY-RECON.md) Живой диспетчер = `worktrees/autodispatch-base-351c/scripts/auto-dispatch.ts`, HARNESS_ENABLED=1 (ENFORCE). `.agent/route-issue.mjs` (детерминированный) → queue+executor → resolveHarnessRoute → спавн: premium→Opus native; standard→DeepSeek-pro (claude-deepseek.sh); cheap→DeepSeek-flash; voice/human→skip. **`applyFleetAvailability`** honours per-model disable/cap флаги → ремап на доступную (fleet last-mile). Codex gpt-5.5 — НЕ через auto-dispatch (ручной codex-lead / легаси). **Provider-neutral rule:** deterministic contour automation (`contour-janitor`, `watchdog`, `reconcile-finished`, `auto-groom`, guard/rearm code) is not a Codex-bound or Opus-bound service. It must behave identically when `/monitor` switches active director/provider, and it must read board state plus monitor/director/model availability state. If a future deterministic path needs AI escalation, it must discover active director and available models from monitor state, not hardcode Codex/Opus. ## Cap-баланс — cap-director-guard launchd `com.poolpooly.cap-director-guard` (5-10мин, RunAtLoad). Ставит `~/.contour-active-director` по cap-сигналам (owner-флаги + реактивный-429) + lead-liveness (флип только на ЖИВОГО лида; оба недоступны→Telegram-алерт). Owner-override уважается. Прямого cap-% нет — на owner-глаза+429. ## Само-исцеление — contour-janitor launchd `com.poolpooly.contour-janitor` (--execute, 5мин, RunAtLoad). Правила §1-9: dead-worker reclaim, do-not-redispatch expiry (§2, скип register-серии), human_gate un-stick (§3), stale-build-stop ФЛАГ лиду (§4, не авто-снимает), dispatcher-fill (§5), auto-clean-replay при CONFLICT (§6, скип deploy-candidate), stuck-QA-WIP промоут (§8), dead-guard reload (§9), **lead-liveness** (§9x: сессия-есть-CLI-мёртв→kill-session+kickstart, т.к. kickstart один труп не пересоздаёт). HELD-whitelist (owner-hold/live-gap-verified/register-серия) не трогает. ## DoD-gate — убийца фейк-деплоев launchd `com.poolpooly.dod-verify-guard` (--execute, 15мин). Live-curl valid-shape unauth по `board/dod-endpoint-map.json` → ложно-закрытые (Deployed/Accepted но live 200/500) → авто-reopen Needs Proof (§3: held не трогает). Пишет `dod-status.json` для Монитора. ## Стражи (launchd com.poolpooly.*) + Guard-health auto-dispatch, build-steward (transient-retry на gcloud INTERNAL_ERROR + priority-ordering security-перед-фичей + single-flight-lock), deploy-candidate-guard (авто-релиз deploy-as-candidate из лидова гейта), churn-breaker, cap-director-guard, dod-verify-guard, contour-janitor, contour-resurrector, opus/codex-lead-live-tmux, nc-agent-board, lead-duty-watch, mem-guard. Монитор показывает жив/МЁРТВ + hero-счётчик. Janitor §9 авто-reload мёртвых. ## Каталог затыков `board/CONTOUR-JAM-CATALOG.md` (ведёт backup-opus) — сценарии затыков (JAM-NNN): симптом/корень/детекция-пробел/резолв/фикс. JAM-001 = пассивный-лид+build-монополия. ## Само-достаточность — ДОКАЗАНА (2026-07-03) Обе фазы PASS: opus-active (SEC-0309 полный цикл автономно) + codex-active (2 тика чисто, 0 футбола). Война двух лидов вылечена каноном (не отключением — единой правдой). Контур сам: gate-хэндофы, self-verify §9, blocked-tail-sweep §10.