Use when working on the SSOT docs (docs/NN_NN_*.md) — editing or creating a canon doc, hunting or fixing SSOT drift, adding or hardening a docs linter / CI gate, checking where a fact canonically lives, or publishing canon to the GitHub wiki. Operational playbook for docs:check_refs / docs:toc / tracker:check / wiki:sync, plus §Guard-craft — the craft of building a gate that actually catches: the recipe for adding a gate, its thesis, design rules, mutation-verify pitfalls, hardening checklist...
Scanned 9/20/2026
Install to Claude Code
npx -y skills add Alexey-Lukin/silken_net --skill ssot-maintenance --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Ssot Maintenance?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/alexey-lukin-ssot-maintenance)More formats (shields.io, HTML) on the badges page.
---
name: ssot-maintenance
description: "Use when working on the SSOT docs (docs/NN_NN_*.md) — editing or creating a canon doc, hunting or fixing SSOT drift, adding or hardening a docs linter / CI gate, checking where a fact canonically lives, or publishing canon to the GitHub wiki. Operational playbook for docs:check_refs / docs:toc / tracker:check / wiki:sync, plus §Guard-craft — the craft of building a gate that actually catches: the recipe for adding a gate, its thesis, design rules, mutation-verify pitfalls, hardening checklist and the one-line index of the **blindness shapes** (a gate that under-implements its declared contract, a One-Home invariant with no gate behind it, a gate that constructs the defect it then reports, …) live in this skill's `guard-craft-index.md`, the shapes in full in `guard-craft.md`, both loading on demand — open the index when building, hardening or debugging a gate, and equally when writing a spec, mass-deleting, narrowing a rule or running a campaign. Defers the STANDARD itself to 00_06. Examples: \"edit 03_05\", \"is this value consistent across the docs?\", \"add a drift linter\", \"why is my guard green when it shouldn't be\", \"publish the docs to the wiki\", \"where does the Lorenz constant live?\""
---
# SSOT Maintenance
The *executable playbook* for keeping `docs/NN_NN_*.md` (and the firmware/backend they mirror) internally consistent. This skill is the **HOW**; it does **not** restate the standard or track state — those live elsewhere (see below). "SSOT drift slowly kills" — this skill is the antidote, and the engine for evolving better defenses.
## 📖 Read first — SSOT, do NOT restate here
These are the canonical homes. Read them before acting; never copy their content into this skill (that would itself be drift).
| Source | Owns |
|---|---|
| `docs/00_03_TRL_Matrix_HIL_and_Beyond.md` | **Readiness**: the NASA/ISO scale (1-9, never 10-12) and its `SRL`/`MRL` siblings (§1), TRL-Layered-Independence (§2), HIL + in-silico-as-HIL (§3), and the task-closure criterion + TRL Gate Events (§4). |
| `docs/00_06_SSOT_Documentation_Standard.md §0` | 🚦 **Validation Gate** — the admission criterion for a fact entering canon (an LLM proposes a *hypothesis*; it does NOT compute physics); roles vs instances → the `AI-vendor name One-Home` row, `00_06 §3.3`. |
| `docs/00_06_SSOT_Documentation_Standard.md §1` | **Canonical doc skeleton** (🎯 Мета / ✅ Статус / 🔗 Cross-references / 📑 auto-ToC / Content; blockers→00_07; no volatile counts). |
| `docs/00_06_SSOT_Documentation_Standard.md §2` | **Canonical-home registry** — *одна річ, один дім*. Seven tables of which fact lives where, partitioned by owning module (`§2.0`–`§2.6`; homes outside the module grid sit in `§2.0` under a **Поза модульною сіткою** label): TRL matrix→`00_03 §1`, AES modes→`03_05 §6`, Lorenz→`03_04 §1.2`, … |
| `docs/00_06_SSOT_Documentation_Standard.md §3` | **Drift-prevention tooling** — the CI-enforced guard registry, partitioned by SUBJECT (`§3.1`–`§3.6`); `§3.0` is its complement — what no gate sees. File a new guard under the matching subsection. ⛔ §3 is **never renumbered**: more inbound refs point at it than at the rest of the doc combined, so its number is an ADDRESS, and "tidying" it would break them all for cosmetics. |
| `docs/00_00_SSOT_Index.md` | SSOT index + reading order. |
> 🔑 **How to open `00_06` without reading all of it.** Its sections run `0 · 1 · 2 · 3 · 4` in file order, and the **first ~90 lines (≈28 kB) are everything that is NOT a registry** — the doc's own head, `§0` and `§1` in full, and all of `§2`'s prose, ending exactly at the first table (`### 2.0`). Do not read a list of what is in there from this line: the cut is defined by that BOUNDARY, and any enumeration here rots the moment a paragraph moves. That prefix is the default entry read; the two registries below it (`§2.0`–`§2.6`, `§3.1`–`§3.6`) are opened BY ADDRESS, never scanned. ⚠️ One exception, and it is the common one here: if the task TOUCHES A GATE, the prefix is not enough — `§3`'s own preamble — everything between its heading and `### 3.0` — must be read too; it carries the row-form contract, the registry's declared ceiling, and the operating rules for running the lane.
**Open work** lives in `00_07`; **campaign state** in memory, not here: `[[project_ssot_campaign_history]]` (Gen1 SSOT-standardization campaign, DORMANT) + `[[feedback_vilize_sweep_method]]` (the LIVE vilize method) + `[[project_vilize_00]]` / `[[project_doc_t33_t34_seed]]` (§00-tooling: markers, stan_audit), plus `[[feedback_no_volatile_counts]]`, `[[feedback_ssot_review_workflow]]`, `[[project_wiki_sync]]`.
## When to use
- Editing or creating any `docs/NN_NN_*.md` canon doc.
- "Is this value/fact consistent across the docs?" / suspect drift.
- Adding or tuning a docs linter / CI gate.
- "Where should fact X live?" (→ 00_06 §2 registry).
- Publishing canon to the GitHub wiki.
## Core principle
**Every fact has ONE canonical home (00_06 §2); everything else references it, never restates.** When a fact changes, edit it *only* at its home — references stay valid. A mirror must carry an explicit label saying it is one — the exact wording is `00_06 §2`'s to set, not this skill's. The linters below enforce *owner-only vocabulary*; the per-rule owner scoping lives in `00_06 §3.3` and is deliberately not restated here.
## Workflow — hunt → automate → fix
The loop that stabilises the SSOT (repeat per drift class):
```
1. HUNT Find a fact restated inconsistently across docs/code
(same value, two numbers; same string, two spellings).
Identify its canonical OWNER (00_06 §2).
2. AUTOMATE Write a precise, low-false-positive linter (recipe → guard-craft-index.md)
so the drift can never silently return.
3. FIX Clean every off-home restatement → make it a reference.
Re-run the gate until green; the gate now holds the line.
```
> Before editing any symbol the docs describe, honour the repo's blast-radius rule (CLAUDE.md §2): trace the symbol's callers first. Docs edits are low-risk, but value changes that mirror code are not.
### Module-restructure / extract-to-new-page
Moving a fact into its own canon page, merging two pages, dissolving one, or freeing/re-occupying a module number. **The method lives in `.claude/prompts/module_restructure.md`** (paste it, or «виконай …»); the norm — `00_06 §4`. ⚠️ Re-occupying a freed number is allowed (`00_05`), but it silently re-points every old ref at a different subject — a VERIFY event, not a renumber (its price → `00_06 §4`). Sibling loop, one item rather than one page → `item_cementation.md`.
### Cementing a 00_07 item (cement-trim or archive — code + SSOT + tracker together)
A heavier, specialised loop for *cementing* a tracker item — thinning a live one to a pointer (cement-trim) or retiring a done one (archive). Walk **every** code/doc site of the ID — forward **and** inbound refs, **semantic terms** (service names, constants, opcodes), not just the ID, across the WHOLE canon + code + spec + `.github`. ⛔ **Never write a NUMERIC RANGE of docs here** — take the list from the tree: `ls docs/[0-9][0-9]_[0-9][0-9]_*.md`; the canon-file set grows, a range rots silently. Verify each `✅` against real code + `git log` (don't trust prose-claims-done). Canonize what lives only in 00_07 prose **before** trimming (migrate-first); then **cement-trim** (open residual stays) or **archive** (fully done → §🗄️ table). Commit each ID separately. Founder disciplines (one edit at a time · READ the full canon section in every doc before thinning · orphan-ID sweep · re-audit cemented items) → `item_cementation.md` §Принципи + Шпаргалка + Фаза 2. ⚠️ No gate stands behind them: the zero-loss set-diff is grep-based, so a fact present-by-token but gutted-in-substance passes green. ⊕ Reverse sweep («хто чекав на МЕНЕ»; «резолвиться» ≠ «актуальний», §Guard-craft #50) → `item_cementation.md` Шпаргалка + Фаза 3. The full playbook (phases, breadcrumbs, anti-patterns) lives in **`.claude/prompts/item_cementation.md`** — this skill only points (One-Home).
### Closing ANY item (not just a cementation) — the mirrors outlive the item
🔴 **Канон переживає власну підставу рівно в мить, коли пункт ЗАКРИВАЮТЬ, і почервоніти цьому нічим** [DOC-T.91, 2026-08-27]. Асиметрія робить клас передбачуваним: закриття оновлює **ПУНКТ** — той артефакт, який ти й редагував, — а дзеркала лишаються, бо вони інший артефакт і ти в них не заходив. Жоден гейт їх не бачить: файл існує, лінк резолвиться, а речення **БУЛО правдивим** у мить написання. Виміряно на закритті `ARCH.111`+`OPS.34`: **сім** домів лишились стверджувати старе при зелених усіх осях `tracker:check` і зеленій смузі `Docs` — три казали «індексуються ЛИШЕ грошові події», чотири слали читача по **ВІДКРИТИЙ присуд** при ратифікованому й архівованому. Того ж проходу той самий клас знайшовся ще на трьох закриттях (токен-міграція UI · ціновий якір `E.67` · вердикт глосарію `lt`) і **на восьмому домі — у `CLAUDE.md`**. ⚠️ **Діапазон-скорочення (префікс, номер, дефіс, другий номер) тут писати НЕ можна** — `code_tracker_id_check` читає його як ОДИН ідентифікатор, якого в реєстрі немає, і червоніє. Перелічуй ID поодинці або описуй словами — гейт, що стереже форму, оподатковує кожен текст ПРО цю форму.
⛔ Порядок свіпу після ратифікації (виконувані артефакти → таблиці → проза) і чому таблиця дорожча за прозу → `00_05 §4` п. 2.
**Рефлекс при кожному `[x]`/✅ — три греби, жоден не по ID пункту:** (1) **значення**, яке щойно перестало бути правдою («індексуються лише X», «компілятора немає», «периметр = `shared/`»), корпус-широко — інфра-факт осідає в 3-12 домах (`00_06 §2`); ⊕ **форму ЗАПЕРЕЧЕННЯ** (`не існує` · `немає` · `нуль` · `no … gate` · `until … exists`) грепай окремо, у домах, суміжних зі збудованим: таке твердження не ділить токена з роботою, що його спростувала (2026-09-10: три з чотирьох знахідок закриваючого свіпу — «канал не реалізовний», «there is no `--check` gate here», «нуль згадок у `docs/`»); (2) **маршрути**, що обіцяють ВІДКРИТЕ («відкритий присуд», «вирішує ⚖️ у», «дім роботи») і вказують на ID, який ти щойно закрив — ⊕ **і на ID, чию ⚖️ ти щойно РАТИФІКУВАВ, хоч сам пункт лишився живим:** події закриття там немає, тож цей греб — єдине, що клас підхоплює. Виміряно 2026-09-20 на `SLASH-1`: рядок Кат-A у `05_05 §2` (грошовий канон) **24 дні** казав «Відкритий присуд, звужений виміром до пари опцій», тоді як присуд ухвалили 08-27 і він був ТРЕТЬОЮ опцією; реф резолвився, форма була бездоганна, пункт справді відкритий — просто вже не ТИМ питанням. **Питай не «чи резолвиться», а «чи ціль ще про ТЕ питання».**; (3) **прозу власного пункту** — `**Стан:**` пишуть один раз, а ноги закривають окремо, тож голова систематично стверджує дихотомію, яку її ж свіжа нога вже спростувала. ⚠️ Пункти (1)-(2) — це НЕ `item_cementation` (той важкий — цементує пункт цілком); вони коштують три греби й обовʼязкові навіть коли пункт лишається живим.
⊕ **ЧЕТВЕРТИЙ греб, і він єдиний, у якого НЕМАЄ хибного токена: дзеркало, що протухло не від ПРАВКИ, а від РОСТУ цілі** (2026-08-28, три інстанси за один sync-прохід). Норма — `00_05 §4` п. 4 («ціль ЗБАГАТІЛА»). Виміряні форми: (а) **часткова копія схеми** (два поля з семи — без тих, що несуть доказову поставу); (б) **абзац, що пережив ВЛАСНЕ закриття в тому ж доці**, коли спростування лежить кількома секціями нижче; (в) **код-блок канону, що цитує рядок, який став One-Home-константою** — цитата не є присвоєнням, тож пін-двигун не рятує (§Guard-craft #97). 🔑 **Дискримінатор, бо грепати нема чого:** питай не «що я змінив», а **«що канон КОПІЮЄ, і чи оригінал з тих пір не виріс»** — і найдешевший лік для (а) не оновити копію, а ЗНЯТИ її на користь вказівника: копія протухне знову, вказівник — ні. 🔴 **Четверта форма, знайдена 2026-09-01 і найпідступніша, бо несе ВЛАСНЕ СВІДЧЕННЯ про свіжість: копія, позначена словом «дослівно».** `S6.1` цитував рядок `06_01` про ізоляцію Redis із міткою «дослівна копія» — і мітка була правдива в день написання. Далі `INF.22` переписав оригінал («окремі БАЗИ» → «окремий інстанс/кластер на КОЖНОГО споживача», бо нумерованих баз в Upstash немає), копія лишилась, а мітка перетворилась на сертифікат точності, якого ніхто не видавав. **Ефект інверсний до звичайного дрейфу: звичайна копія викликає підозру й її перевіряють, а «дослівна» читається як УЖЕ звірена — тобто маркер фідельності купує копії довіру рівно тоді, коли вона її втратила.** 🔑 Рефлекс: `дослівно` · `verbatim` · `копія рядка` · `цитата з` у каноні — це не запевнення, а ЦІЛЬ для греба; і лік той самий, що для (а), лише терміновіший — знімай на вказівник, бо мітку доведеться перевіряти щоразу, а вказівник не доведеться жодного.
> 🔴 **СВІП «блокер проти ВЛАСНОГО присуду» — дешевий і дає найтихіший клас: пункт, чия робота ратифіковано звільнена, а STAGE лишився `🔗`** (2026-08-19). Хибне блокування ніхто не перемірює, бо воно **виглядає як обережність**, і жоден гейт його не бачить: `🔗` формально валідний, тригер названий, реф резолвиться. Прогін по всіх `🔗`-пунктах трекера дав ДВА з 29 — в одному `[x]`-присуд каже «наша машинна робота, НЕ gated партнерами» при STAGE `🔗`, у другому сам residual пише «не gated передумовами». **Рецепт:** візьми кожен `🔗`-пункт і грепни його ТІЛО на форми звільнення (`НЕ gated` · `наша машинна робота` · `РОЗБЛОКОВАНО` · `блокування знято/протухло`); хіт разом із живим `🤖`-residual = кандидат. ⚠️ Далі ЧИТАЙ: половина пунктів має дві половини під одним блокером (доменний контент партнера ⊥ операційний рядок застосунку), і лік там — РОЗДІЛИТИ residual, а не зняти STAGE. ⊕ Дзеркало вже записаного в `item_cementation` Фаза 3 («архівуючи ID, питай не на кого чекаю Я, а хто чекав на МЕНЕ»): там блокер тухне ззовні, тут — від власного присуду пункту.
>
> 🔴 **Рецепт вище ключується на СЛОВНИКУ звільнення — і саме тому пропускає найтихіший підвид, де жодного такого слова немає: суперечність між `**Стан:**` і ЧЕКБОКСОМ** (E.33, 2026-08-21). Там `Стан` уже записував «ЖИВІ канали — SMTP і Telegram; межа ~30 msg/s є ПЕРШИМ реальним предметом цього пункту», а чекбокс поруч і далі вимагав, щоб «адаптери існували». Ані `НЕ gated`, ані `РОЗБЛОКОВАНО` в тілі не було — пункт спростував себе ЗМІСТОМ, не словником. **Другий прохід рецепта: для кожного `🔗`-пункту прочитай `Стан` і чекбокс ПОРУЧ і спитай, чи перший не описує світ, у якому передумова другого вже виконана.** ⚠️ Той самий прохід дав і третю форму — **дублікат residual'а з РІЗНИМИ заявами про гейтування** (ARCH.75: те саме питання `duration`⟷`relevance` стояло пунктом (в) `🔗`-пакета І окремим «вільним» `⚖️`); там лік — злити в гейтований, бо ратифікований присуд каже, що до події воно не задається. 📊 База ставки, виміряна за один прохід: **три пункти з девʼяти** ділились на дві половини, де `🔗` ховав вільну (ARCH.31 · E.33 · ARCH.75-дубль) — тобто це не курйоз, а нормальний улов цементаційної кампанії.
>
> 🔴 **Дзеркало обох рецептів — НАДМІРНЕ звільнення, і воно тихіше, бо виглядає як виконаний присуд** (HW.33, 2026-09-14). Ратифікація звільняє ноги, що чекали САМЕ на неї, але нога буває гейтована ДВІЧІ, і свіп, що ключується на імені присуду, знімає обидва гейти одним рухом. Свіп присуду «вхід» (стелю передано в HW.9 входом) звільнив разом із приливом і ногу коміра, чию СТІНКУ не задає жоден артефакт, і креслення радома, що чекає корони (⚖️ HW.30); спіймано перечитуванням до коміту, гейт цього не бачить за побудовою — обидві ноги лишились формально валідними. **Звільняючи ногу, прочитай її текст цілком на інші «чекає · після · ⊂ · доки» і питай про КОЖЕН гейт окремо; звільнення — твердження про всі.**
## Commands
All via binstubs — **`bin/rails` / `bin/rspec`**, never `bundle exec` (`[[feedback_local_verify]]`).
| Command | Does | Engine |
|---|---|---|
| `bin/rails docs:check_refs` | The omnibus gate **within its own step** (one of the `docs_check` job's many — the lane is `docs_band.rb`): dangling `NN_NN` links (HARD) · §-label drift (HARD, DOC-T.48) · standard-conformance (**incl. H1** — DOC-T.49, checked before the skeleton exemptions) · and the rest. **The roster is `docs.rake`'s `failed <<` labels, mapped to `00_06 §3` rows by `DOCS_RAKE_LABELS` in `scripts/guard_registry_sync.rb` — never enumerated here.** **Every category is now HARD** — a hit fails the build, so "advisory while you triage" (the recipe in `guard-craft-index.md`) is a *transient* state, not a resting one. | `lib/docs_linter.rb`, `lib/docs_toc.rb` |
| `bin/rails docs:toc` | Regenerate the `📑 Зміст` auto-ToC between `<!-- TOC:AUTO:START/END -->` from current `## ` headings (curated `— descriptions` preserved). Run after changing headings. | `lib/docs_toc.rb` |
| `bin/rails tracker:check` | 00_07 DRY — full axis list in `00_06 §3.2`, not here. The one worth carrying in the head is **item visibility** (DOC-T.49): every `#### ` item must sit INSIDE a registry section (`## §NN`), because `parse` cannot see one that doesn't, and then every other check here iterates a set that silently lacks it. | `lib/tracker/dashboard.rb` |
| `ruby scripts/code_doc_section_refs.rb` | **HARD gate** (docs.yml, DOC-T.48): every `NN_NN §X` cited in a code comment, in the `.claude` routing layer or in the `.github` CI layer still resolves — **the exact perimeter lives in the script, not here**. Closes the structural blind spot that `docs:check_refs` scans `docs/**` and never reads code. The priciest step in `docs_check` (tens of seconds over the whole tree), so run it deliberately, not in a tight loop. | `scripts/code_doc_section_refs.rb` |
| `bin/rails docs:graph` | _(on-demand audit, NOT a CI gate)_ ref-graph view: orphan / dead-end pages, in/out-degree skew, one-way sibling links, comprehensive `#anchor` + linked-`§X` resolution. The graph lens the per-line gates can't give (they check refs flat, not the `NN_NN` link graph). | `lib/docs_graph.rb` |
| `bin/rails wiki:sync` | **Dry-run** (default): clone wiki, transform links + carry images, show `--stat` diff + unresolved links. Publishes nothing. | `lib/wiki_link_normalizer.rb` |
| `bin/rails wiki:sync PUSH=1` | Commit + push the canon `NN_NN` pages to the GitHub wiki (SSH to `*.wiki.git`). | ↑ |
| `COVERAGE=0 bin/rspec spec/lib/docs_linter_spec.rb spec/lib/docs_toc_spec.rb spec/lib/docs_graph_spec.rb` | Unit-test the linter / ToC / ref-graph engines (pure functions — `spec_helper`, **no Rails/DB**; `COVERAGE=0` skips the whole-suite coverage gate on a subset run). | — |
| `ruby scripts/docs_check.rb [refs\|tracker]` | **Fast local** alias — runs `docs:check_refs` + `tracker:check` with **no Rails boot** (~0.3s vs ~1.2s; no `bundle`/DB — only `ruby`+`rake`). Reuses the exact rake bodies → those two steps cannot drift from CI. ⚠️ **Two steps of the `docs_check` job (ask `docs_band.rb --list` how many there are — the count grows) — NOT the lane**; reading its green as a verdict about the Docs lane reddened `main` three times (OPS.25). Read-only (ToC regen stays `bin/rails docs:toc`). | reuses `lib/tasks/docs.rake` + `tracker.rake` |
| `ruby scripts/docs_band.rb` | **The WHOLE `CI · Docs` lane** (~1.5 min, on-demand): step list read from `docs.yml` itself, so a hand-written roster cannot rot beside it. NOT-RUN is classified BEFORE launching (interpreter resolve · `ruby -c` · `bundle check`) and reds alongside FAIL — an empty finding-set is "clean" only if every check EXECUTED. ⚠️ `git add` first: `spdx_headers` enumerates via `git ls-files`. | `.github/workflows/docs.yml` |
| `ruby scripts/stan_audit.rb` | _(advisory, on-demand — ганяти на цемент/vilize-сесіях, НЕ CI-gate)_ три осі по registry-айтемах 00_07 — третя = **[x]-staleness** (чекнутий бокс із датою `✅ YYYY-MM-DD` старший ~14 днів → цементуй у Стан/канон і зрізай; бездатні = лічильник). Дві осі по `**Стан:**`-рядках: **canon-claim** (код-символ ∉ заявлені канон-доми айтема — ловить «Механіка — `NN_NN §X`» без змісту; FP-класи вбиті historical sweep'ом DOC-T.37 — повний exempt-перелік у шапці скрипта) + **volatile-numbers** (число+лічильне слово, класи A/B/C/D очима). Кожен хіт розібрати очима: FP / wrong-дім → Стан-реф-дожим / діра → канонізувати-migrate; новий повторюваний FP-клас → вбити В СКРИПТ. Метод/історія → `00_06 §3` | `scripts/stan_audit.rb` (standalone; реюзить `Tracker::Dashboard`-парсинг) |
| `ruby scripts/tracker_report.rb --takeable` · `--critical-path` | _(advisory, on-demand — ПРИСУД, не борг)_ режими над `00_07`; ростер — `case` у скрипті. 🔴 Кожен друкує ВЛАСНУ сліпоту ПЕРЕД таблицею — читай преамбулу, не таблицю (інакше це палантір: числа правдиві над курованою множиною). Одиниця — НОГА, не пункт. Чому advisory і стелі → `00_06 §3.2` + шапка скрипта | `scripts/tracker_report.rb` (standalone; реюзить `Tracker::Dashboard`-парсинг) |
| `ruby scripts/tracker_volatile_counter_check.rb --strict` | **HARD** у `docs.yml` [DOC-T.99, ⚖️ founder 2026-09-05] — число рухомої множини в `- **Стан:**` чи `📊 WATCH`. Лік автора — **назвати членів замість лічити** або лишити число з КОМАНДОЮ, що його відтворює. Словник (`NOUN`), винятки й оголошена стеля — шапка скрипта; чому — `00_06 §3.2` | `scripts/tracker_volatile_counter_check.rb` |
| `ruby scripts/component_doc_sync.rb` | **component registry gate** (HARD, CI `docs.yml`, UI.12): `04_04` ⟷ `app/views/` — domain files ⟷ §6.4 rows (1:1) · `shared/{ui,iot,web3}` ⟷ §6.1–6.3 (1:1) · namespace dirs ⟷ the §1 hierarchy tree. Sibling of `model_doc_sync` and `workflow_doc_sync`. The load-bearing part is the PARSER: row forms (a FOURTH must be taught explicitly) + ceilings → script header. | `ruby scripts/component_doc_sync.rb` (CI `docs.yml`) |
| `ruby scripts/model_doc_sync.rb` | **code↔doc registry gate** (HARD, CI `docs.yml`): `04_01` ⟷ `app/models/` (model files ⟷ `### Model` headings §2..§7 1:1 · concerns ⟷ §1 · `PARTITIONED_TABLES` ⟷ §0/§11) **+ `04_02` ⟷ `app/services/**`+`app/workers/**`** (every class mentioned). Run it (or rely on CI) after adding a model/service/worker. Pure Ruby, no Rails. Method/why → `00_06 §3`. | `scripts/model_doc_sync.rb` (standalone) |
| `COVERAGE=0 bin/rspec spec/quality/no_future_dated_claims_spec.rb` | **HARD gate** (docs.yml cross-tree step, DOC-T.77): a record about something DONE must not carry a date that has not happened. The date is an ORDERING ANCHOR — a forward-shifted entry makes an older verdict look newer than the one disproving it; anchor = HEAD commit date (§Guard-craft #67); why and discriminator → spec header. ⚠️ **Writing about this gate taxes you:** quoting a future date as an *illustration* on a line that also carries a completion marker reds it — paraphrase, do not exemplify (same class as writing about tracker form). | `spec/quality/no_future_dated_claims_spec.rb` |
> 🔴 **ЛІНЗИ ЗАПУСКАЮТЬ, а не переказують — і прогалина тут вимірюється РОСТЕРОМ, не памʼяттю** (2026-08-19). Вимірено на собі: із режимів `memory_gate.sh` роками ганялись ДВА (`--audit`, `--selftest`); `--weight`/`--oneway`/`--routes`/`--stops`/`--genre` — жодного разу, попри те що скіл `memory-maintenance` прямо велить «run the SEPARATE modes». **Вказівник на ростер (на відміну від пойменованого списку) про себе не нагадує**, тож практика тихо вироджується в один звичний виклик. Ростер бери з ДЖЕРЕЛА: `grep -oE '^\s+--[a-z-]+\)' .claude/hooks/memory_gate.sh`. ⊕ Найцінніший із них для ЦЬОГО скіла — `--stops`: обчислюваний **реєстр уже ВІДХИЛЕНОГО**, який агент «з холоду» перелічити не може, а одна промашка коштує сесії; читати ПЕРЕД вибором роботи. ⚠️ Кожна лінза оголошує СТЕЛЮ, і її треба брати разом із числом: `--stops` — «підлога, повний READ дає ×3.2»; `spec_claim_audit` — «шортліст, не вердикт» (перевірено: на найгострішому кандидаті мутація дала ВІСІМ червоних, тобто покриття вчетверо щільніше за гіпотезу лінзи); `stan_audit` вісь 1 дає хибний хіт на приватний метод, коли канон-дім несе публічний, **і тригериться власним поясненням у тілі пункту** — форму пиши словами, не токеном.
CI: `docs.yml` is the home of the doc-gate RAKE PAIR. ⚠️ Not of every doc gate: the spec-shaped ones it runs (`spec/quality/*`, `spec/deploy/*`) are ALSO swept by `ci.yml`'s full-tree rspec, so those have two CI homes and double-run on a mixed PR. Its `changes` filter covers `docs/**` + `**.md` + **every source tree** — deliberately wide, because several gates scan them tree-wide (perimeters live in the scripts) and the pin/model-sync gates read specific files inside them; a gate whose input sits outside it never runs on its own PR (recipe step 6, `guard-craft-index.md`). `ci.yml` has no `paths-ignore`: its `changes` job skips code jobs on a docs-only diff and `ci-ok` still reports. Branch protection (`docs-ok`) → `06_07 §2`.
## 🛡️ Guard-craft — a gate that actually catches
**Lives in [`guard-craft-index.md`](guard-craft-index.md) — open it the moment the task is an EVENT rather than docs work** (building, hardening or debugging a gate · asking whether a spec can fail at all · mass-deleting · narrowing a rule · running a campaign · shipping anything a human executes by hand). It carries the recipe «Add a new drift guard», the craft's thesis, its design rules and the generated one-line index of the blindness shapes; their bodies are in [`guard-craft.md`](guard-craft.md), opened by number — cite `§Guard-craft #N`. Why it left this body, and the price → the head of that file.
## Checklists
**Before merge** (any docs change):
```
- [ ] ruby scripts/docs_check.rb → green — but it is TWO steps of the `docs_check` job, never the band
- [ ] ruby scripts/docs_band.rb → green — the WHOLE `CI · Docs` lane, step list read from `docs.yml`
itself. ⛔ A roster of gates rots by construction — never re-add one here.
- [ ] bin/rails docs:toc → run if headings changed, then re-check green (regen, not in the band)
- [ ] fact edited ONLY at its home (`00_06 §2`); mirrors labelled
- [ ] no volatile counts · no blocker section in canon · Cross-references at top
```
**Wiki publish — ZERO-TOUCH** (since 2026-06-24): `.github/workflows/wiki.yml` publishes on every push to `main` touching `docs/**` (off-switch: repo var `DISABLE_WIKI_AUTOSYNC=true`). Do **NOT** run `wiki:sync PUSH=1` by hand after a normal canon-push — it RACES the CI run (non-fast-forward reject). A manual `bin/rails wiki:sync` (dry-run) stays a useful link-check before a big restructure. Detail → `[[project_wiki_sync]]`.
## Gotchas (hard-won)
- **Subset `bin/rspec` runs trip the SimpleCov coverage gate** (a per-group `minimum_coverage` failure). That's an **artifact** of a partial resultset, not a real failure. The linter/ToC specs are pure units (`spec_helper`, no Rails/DB) — run them gate-free with `COVERAGE=0 bin/rspec spec/lib/docs_*_spec.rb`; for app-coverage truth run the full `bin/rspec` (`[[feedback_local_verify]]`).
- **`db/structure.sql`**: never stage drive-by Postgres-version line diffs — restore from HEAD (`[[feedback_structure_sql]]`).
- **`wiki:sync` needs SSH access** to the `*.wiki.git` repo; **always dry-run first**; read its "unresolved links" — they're often stale source links worth fixing. **Run it PLAIN** — never prepend `/usr/bin` to PATH (shadows the rvm ruby shim → system Ruby 2.6 → bundler crash); a `Gem::Resolver…GemParser` trace can also be a SentinelOne-eaten shim (`[[project_rvm_env_repair]]`).
- **A missing closing ` ``` ` fence silently desyncs EVERY fence-aware guard** (the `in_fence` toggle runs in every fence-aware `DocsLinter` method) + truncates the ToC — one unclosed fence disables them all at once. **Now HARD-gated**: `DocsLinter.unbalanced_code_fences` (DOC-T.45, 2026-07-18) counts fences by the SAME `line.start_with?` predicate the guards use, so `docs:check_refs` aborts on an odd count (reporting the opening line) — a mirror gate, self-consistent with what it protects.
- **An ILLUSTRATIVE path is indistinguishable from a live claim — in EVERY file a gate scans, and there are at least three such gates.** `guard_registry_sync` resolves each path a `00_06 §3` row cites; `spec/quality/claude_prose_path_refs_spec.rb` resolves each backticked file path in `.claude/**/*.md`; `path_check` does it for the memory corpus. A plausible example path fails all of them exactly like a dead real one. **Describe the form in words; never spell an example path, however obviously fictional it looks.**
- **Homoglyph / mixed-script drift** (a Cyrillic letter inside a Latin word) — HARD-gated by `ruby scripts/homoglyph_check.rb` (perimeter = its `GLOBS`); scan by hand only files outside it.
- **Stray COMBINING MARK inside a same-script word** — the homoglyph's sibling, and the cost is sharper: a word carrying `U+0301` is invisible to `grep` for that word, so it silently drops out of every sweep aimed at it (measured with a positive control — `grep` scored 0 on the affected line and 1 on a clean control). Same script, so no mixed-script scan sees it. Detector: `perl -CSD -ne 'while (/(\p{L}{0,6}\x{0301}\p{L}{0,6})/g) { print "$ARGV:$.: $1\n" }' $(git ls-files 'docs/*.md' '.claude/**/*.md' '.github/**')`. ⚠️ It stays a RECIPE, not a gate, and the reason is linguistic: Ukrainian legitimately marks stress to split homographs (the castle/lock pair is the schoolbook one), so artefact-vs-deliberate is a judgement no regex owns — read each hit. ⚠️ Describe that pair in words, never spell it: an accented example inside this file is itself a hit, and a detector whose first two findings are its own documentation is one a reader learns to skim (#10a, one surface over). Precision on the corpus at the time of writing was total (every hit an artefact, none a homograph), which is what made a hand sweep cheaper than any gate. ⛔ Do not "fix" these with a blind strip in a file you have not read: two of them were a mistyped word, not a mis-accented one, and stripping the mark alone would have cemented the typo.
- **Encoding trap in the detector ITSELF:** a `perl -CSD` one-liner decodes input to characters, but a Cyrillic literal in the SCRIPT stays bytes without `use utf8` — so the match silently fails and reports a clean tree. This bit the very sweep that found the class. **Verify any non-ASCII matcher with a positive control before believing its zero.**
- **`.c` firmware comments stay Ukrainian + the file's poetic house style** (`[[feedback_comment_style]]`).
- **zsh**: `status` is read-only; quote globs (`[[feedback_zsh_bash_gotchas]]`).
## Keep this skill bounded
This file is the **method**. It must not accumulate: the *standard* → `00_06`; *open work* → `00_07`; *campaign state* → memory; a *new guard* → its engine (`lib/docs_linter.rb` or `scripts/*.rb`) + its `00_06 §3` row; a *new blindness shape* → `guard-craft.md` (index regenerated). If you're tempted to add a fact here, it belongs in one of those homes — that discipline is the very thing this skill enforces.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!