Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Backend

ASecurity

Use when working on the silken_net Rails 'Web2 core' — data models (app/models/ + concerns), the REST API v1 controllers (app/controllers/api/v1/), auth/RBAC (Bearer + salt-bound session cookie, M2M Ed25519, Pundit policies), the non-money services/workers (app/services/, app/workers/), and the MaintenanceRecord / Evidence-Protocol domain. Knows the non-obvious gotchas — role enum prefix:true (role_admin?, NOT admin?; no 'patrol' role exists), the IDOR sibling-guard for client-supplied FKs (f...

23 stars
0 votes
0 copies
0 views
Added 9/20/2026
databasesgorubysqlrailstestinggitapifrontendbackendsecurity

Works with

cliapi

Security Analysis

A100/100

Scanned 9/20/2026

Install to Claude Code

$npx -y skills add Alexey-Lukin/silken_net --skill backend --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Backend?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Backend
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/alexey-lukin-backend/badge)](https://www.skillsdirectory.com/skills/alexey-lukin-backend)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: backend
description: "Use when working on the silken_net Rails 'Web2 core' — data models (app/models/ + concerns), the REST API v1 controllers (app/controllers/api/v1/), auth/RBAC (Bearer + salt-bound session cookie, M2M Ed25519, Pundit policies), the non-money services/workers (app/services/, app/workers/), and the MaintenanceRecord / Evidence-Protocol domain. Knows the non-obvious gotchas — role enum prefix:true (role_admin?, NOT admin?; no 'patrol' role exists), the IDOR sibling-guard for client-supplied FKs (foreign-but-existing → 404, missing → 422), the session[:ps] password-salt stamp, M2M token = its own :m2m_access purpose + BaseController allowlist (SEC.16), Idempotency-Key → 400 not 422, the webhook HMAC fail-closed pattern, exact HKDF info-strings per owner type, Gateway#online? = Gateway::LIVENESS_WINDOW_S (виміряний каденс прошивки, НЕ config_sleep_interval_s), self.primary_key = 'id' on partitioned models, Auditable = after_update_commit + saved_change (NOT AASM after_all_transitions), the FactoryBot initialize_with reuse for Tree's auto-created wallet/calibration. Routes to the 04_01/04_02/04_03/04_06 canon + CLAUDE.md §5/§6, does not restate. The gotchas are indexed here one line each and written in full in this skill's `gotchas.md`, which loads on demand — open it when a one-line index entry stops you and you need the mechanism, the incident that bought it, or its bounds. NOT this skill: money-path/minting/slashing → web3-pipeline; telemetry uplink/TelemetryLog → telemetry-pipeline; Phlex/Tailwind UI → frontend. Examples: 'add a REST endpoint', 'add a model / column / validation', 'change a role or policy check', 'why does my spec create a second wallet', 'add a maintenance action_type', 'why 404 on a foreign cluster_id', 'rotate a hardware key', 'add an audit-trail hook'."
---

# Backend (Models + Services + REST API — the Web2 core)

Navigation aid + non-obvious gotchas. **SSOT = canon-доки нижче + код** — цей скіл вказує, не
реставляє (щоб не дрейфував). Домен: моделі (`04_01`) + non-money сервіси/воркери (`04_02`) +
REST API/auth/RBAC (`04_03`) + MaintenanceRecord. Money-path, телеметрія, UI — сусідні
скіли (межі внизу).

## SSOT Documents — Read These First

| Document | What it covers |
|----------|----------------|
| `docs/04_01_Data_Models_and_Entities.md` | **Model-layer SSOT** — §0 Postgres-інфра (партиції), §1 Concerns (Auditable, EthAddressValidatable, HasArgon2Password, …), §2–§6 доменні моделі, §7 аудит/інтелект (AuditLog, **MaintenanceRecord**, SystemParameter), §9–§11 індекси/зв'язки/принципи БД, §12 drift-register (`db/structure.sql` = authoritative reality) |
| `docs/04_02_Business_Logic_and_Services.md` | **Services/workers SSOT** — §1 засади (ApplicationService), §5–§8 verification/identity, NaaS, emergency, hardware+security, §11 Workers Registry (черги!), §12 call chains |
| `docs/04_03_REST_API_v1_Reference.md` | **API SSOT** — §1 auth (Bearer / session-cookie / публічні / M2M), §3 RBAC, §4 повна таблиця ендпоінтів, §5 детальні описи, §7 headers |
| `docs/04_06_Testing_Guide_and_Coverage.md` | RSpec-конвенції (§A) + відомі coverage-стелі (§B) |
| `docs/03_05_Hardware_Symmetric_Crypto_and_Security.md` | Key-model канон (двоключова модель, HKDF, FW.17 ratchet) — `HardwareKey`/`HardwareKeyService` мають дім ТУТ |
| `CLAUDE.md §5/§6` | Load-bearing інваріанти (Sidekiq strict, KENOSIS, partition-pruning, `oracle_status_*`, `manual_review`, AES-keys-in-process, thin controllers) — тут НЕ дубльовані |

## Source Files (code map)

| Path | Role |
|------|------|
| `app/models/` + `concerns/` | AR-моделі; 7 concerns — `Auditable` [ARCH.57], `EthAddressValidatable` (shape-regex `0x`+40hex **+ EIP-55 checksum** — mixed-case verified, all-same-case accepted as unchecksummed; predicate `.eip55_valid?` reused by the boot guard), `NormalizeIdentifier`, `GeoLocatable`, `Firmwareable`, `OtaChunkable`, `HasArgon2Password` |
| `app/controllers/api/v1/` | ~30 контролерів < `BaseController` (dual-auth Bearer+cookie, CSRF-bypass лише для Bearer, `rescue_from`-драбина, `render_dashboard`/`render_auth_page` = Phlex-транспорт) |
| `app/policies/` | Pundit — **п'ять** top-level (`application`, `user_context`, `user`, `wallet`, `naas_contract`). **Дефолт = DENY** [SEC.16, 07-30]: `index?`/`show?` → `false`, `Scope#resolve` → `scope.none`, тож політика без явного читання не пускає нікого. 🔴 **Асоціативний скоуп — АРХІТЕКТУРА, ратифіковано ⚖️ 2026-07-31:** Pundit кличуть рівно три контролери, решта скоуплять від `acting_organization!` — і це сильніша форма (чужий запис не матеріалізується, 404 без існування-оракула), а не борг. Наслідки, які вже виконані: **десять** мертвих політик знято разом зі спеками, `verify_authorized` закрито як won't-do. Тож policy-клас, якого не кличе жоден контролер, тут — залишок відкинутого підходу, а не заготовка. Дім доктрини — `04_03 §3` |
| `app/services/` (non-money) | `hardware_key_service`, `insight_generator_service`, `emergency_response_service`, `security/`, `downlink/`, `factory_flashing/`, `cryptography/` |
| `app/workers/` (non-money) | `ecosystem_healing_worker` (critical) · `actuator_command_worker`, `reset_actuator_state_worker`, `actuator_safety_sweep_worker`, `key_rotation_downlink_worker`, `ota_transmission_worker` (downlink) · `*_notification_worker` (alerts) · `insight_generator_orchestrator_worker`, `audit_log_worker` (low) |
| `db/structure.sql` + `db/migrate/` | Schema-SSOT: squash-анкер `init_consolidated` — зміна схеми = **нова міграція поверх анкера → `db:migrate` → `db:schema:dump`** (задокументовано в самому анкері; періодичний re-squash pre-launch = окрема процедура там же); data-INSERT'и в міграціях ЗАБОРОНЕНІ (seeds/rake); dump-дисципліна → CLAUDE §2 |

## Gotchas Not Obvious From Docs


Один рядок на пункт нижче — це **носій**, не зміст: він мусить спинити тебе в мить, коли ти збираєшся порушити правило. Механізм, інцидент, що його купив, і межі — у `gotchas.md` (читається на вимогу, не вантажиться щосесії).

<!-- BACKEND-GOTCHAS-INDEX:AUTO — generated from gotchas.md by `ruby scripts/guard_craft_index.rb --write`; edit rules THERE, never here -->

1. Предикат ролі — `role_admin?`, НІКОЛИ `admin?`: `enum … prefix: true`, і ролі «patrol» не існує
2. Клієнтський FK на write-ендпоінті ЗОБОВʼЯЗАНИЙ звірятись проти `acting_organization!`: чужий-але-існуючий → 404, відсутній → 422
3. M2M-токен несе ВЛАСНУ purpose `:m2m_access` і діє лише через allowlist `BaseController` [SEC.16]
4. `session[:ps]` — сольовий штамп пароля: зміна пароля мусить убивати ЖИВІ сесії, і це єдиний наявний механізм відкликання [SEC.16]
4a. Прапорець, якого не читає ЖОДЕН шлях, — це ЗАЯВКА, а не контроль; і закривати таку заявку треба за НАПРЯМКОМ, не за екшеном
5. Вебхук звіряє HMAC над СИРИМ тілом і fail-CLOSED падає без секрета в проді — ніколи не пропускай запит «бо секрета немає»
6. Відсутній `Idempotency-Key` — це 400, НЕ 422; а `STOP`/`EMERGENCY_*` мусять минати 409, інакше аварійна зупинка недосяжна саме тоді, коли потрібна
7. DID деривується СЕРВЕРОМ із silicon-UID і ніколи не приймається від клієнта — інакше пристрій може назватись чужим
8. Довжину `HardwareKey` задає ТИП ВЛАСНИКА, і кожен тип має власний HKDF info-рядок — сплутати їх означає тихо вивести не той ключ
9. Dual-Key Grace: старий ключ живе до першого uplink на новому, а `rotate!` при живому grace ЗОБОВʼЯЗАНИЙ кидати, не перезаписувати
10. `Gateway#online?` рахує вікно з ОДНОГО дому, і SQL-двійник `scope :online` мусить рухатись разом із ним
11. Партиційна модель мусить оголосити `self.primary_key = "id"` — композитний PK лишається в БД, а без цього `.id` віддає МАСИВ
12. `Auditable` вішай на `after_update_commit` + `saved_change`, НІКОЛИ на AASM `after_all_transitions` — той отруйний для side-effect-хуків [ARCH.57]
13. Фабрика `Tree` створює гаманець і калібрування САМА — реюзай їх через `initialize_with`, інакше спека мовчки заводить другий
14. `EwsAlert#message` — це РЕНДЕР, а не колонка: у БД лежать `message_key` + `message_params`, фраза збирається в момент показу
15. MaintenanceRecord Evidence Protocol — фото обовʼязкові на КОЖЕН save, і виняток несе КОЛОНКА `system_generated`, а не транзієнтна ознака
15a. Питань про фото ТРИ, і межі в них РІЗНІ — предикат, що відповідає на два з них, гарантовано бреше про одне
16. Актуатор у стані `active` мовчки вбиває нові накази
16a. І «override від оператора» в переліку вище був ТЕОРЕТИЧНИМ до 2026-08-13 — механізм жив, пускача не існувало
16b. Свіпер, що рахує ДІЇ, мусить давати голос НУЛЮ — інакше він німий рівно тоді, коли підозрілих рядків найбільше
17. `insert_all` може створити рядок, який НЕ ВМІЄ ПОМЕРТИ
18. A cached per-tenant value needs the org in the KEY, not only in the query — and the key is the half that gets forgotten
19. Організація запиту — це КОНТЕКСТ, а не властивість користувача
20. `if model.bang_method!` обіцяє булеву гілку, якої метод може не мати — і тоді `else` це МЕРТВИЙ код, а справжня відмова летить у 500
21. Лічильник над `AiInsight` мусить рахувати ДЕРЕВА, а не РЯДКИ — `.select(:analyzable_id).distinct.count`
22. Редирект із не-GET мутації віддає `status: :see_other` — але це КОНВЕНЦІЯ (RFC 9110 §15.4.4), а не лік від бага: механізм, записаний тут двома проходами, СПРОСТОВАНО браузерним виміром 2026-08-10
23. `render_dashboard` без `status:` у НЕ-GET гілці — мовчазний баг, і симптом його «дія вдалась, а сторінка не ворухнулась»
24. Сусід #23 із протилежного боку: гілка ВІДМОВИ без `respond_to` взагалі
24a. Гард Turbo перевіряє РІВНО `200`, тож `2xx`-рендер сторінки його минає — і ламає не оновлення, а АДРЕСУ
25. Межу адреси визначає ХТО ВІДВАНТАЖУЄ КЛІЄНТА, а не формат відповіді — `api/v1` це каталог, не адреса [ARCH.77]
25b. `as_json(only:)` МОВЧКИ ігнорує alias-атрибути — ключ у списку роками віддає НІЩО, і жоден пін цього не бачить
25a. `error` (однина) і `errors` (множина) — це ДВА різні контракти, і ключ відповіді сам називає, яка гілка відповіла
26. `dependent: :delete_all` виконується ПЕРЕД пізніше-оголошеним `before_destroy` — тому destroy-guard мусить `prepend: true`
26a. `skip_before_action :authenticate_user!` — ПОЛОВИНА роботи: `verify_authenticity_token` лишається в ланцюгу
27. `User#full_name` ФОЛБЕКАЄ на `email_address`, і жодне з імен не має `presence`-валідації — тож на будь-якій поверхні, видимій ПОЗА організацією власника, це витік адреси
28. Request-спека з назвою про ПОВЕДІНКУ, що міряє лише статус-код, зелена при видаленому механізмі — і додати пін на вміст замало: вирішує ФІКСТУРА
28b. Контекст, названий за шляхом, який зовнішній `before` СТАБИТЬ — і внутрішній `before`, що чистить СТАН, стаб не знімає
28a. Лінивий `let` робить пін вакуумним БЕЗ помилки в самому ассерті — і виявляє це лише мутація, яка ПРОЙШЛА
29. Сиблінг #23 на іншій осі: там усередині одного `respond_to` розходився СТАТУС, тут розходиться ЗМІСТ — і платить за це та гілка, яка нічого не показує
30. Обмежувачів ТУТ ДВА, вони не знають один про одного, і винятки в них РІЗНІ
31. Парсинг часу з НЕДОВІРЕНОГО входу — `Time.zone.iso8601`, ніколи `Time.iso8601`
32. `config/application.rb` тримає ДВА несучі рішення, і обидва виглядають як стиль
33. `joins(assoc)` — це INNER JOIN, тож рядок із NULL-FK для скоупа НЕ ІСНУЄ; а коли асоціація опційна за дизайном, це не оптимізація, а тихе вилучення з видимості
34. Сиблінг #33 на тій самій колонці, і він НЕВИДИМИЙ при читанні моделі: DB-FK існує, а `has_many` — ні, тож `restrict` нема куди поставити
35. Докстрінг, що НАЗИВАЄ споживачів, — це твердження про механізм, і воно гниє тихіше за код
36. Ключ, яким ЧИТАЄШ денний агрегат, мусить бути тим, яким його ЗАПИСАНО — а розходження тихе, бо порожня вибірка не є помилкою
37. Перш ніж сперечатись, ЯКИЙ дефолт безпечніший, спитай, чи він не збігається з РЕАЛЬНИМ виміром — якщо збігається, суперечка закінчена
38. Повідомлення валідації — це КЛЮЧ, не текст; і форм авторства ЧОТИРИ, тож інвентар по одній із них систематично бреше
39. `allow_nil: true` НЕ покриває порожній рядок — і на plain-string колонці, досяжній із селекта з порожньою опцією, це ламає не «рідкісну дію», а КОЖНЕ збереження форми
40. `Notifications::DeliveryChannels` — дім питання «чи має платформа ТРАНСПОРТ для каналу»
41. Слід про НЕЗВОРОТНУ дію мусить бути СИНХРОННИМ — інакше він відтворює рівно ту пару властивостей, проти якої ставиться
42. Два `after_*_commit` з ОДНИМ іменем методу злипаються в ОДИН колбек, а їхні умови ЗЛИВАЮТЬСЯ через AND — тобто гард стає недосяжним, і колбек не спрацьовує ЖОДНОГО разу
43. TTL — це ПРОКСІ для «щось змінилось»; якщо пускач відомий, гаси кеш НА ЗАПИСІ
44. `where(col: value)` — це SQL `= value`, і NULL не дорівнює НІЧОМУ: рядок із порожньою колонкою випадає з обох боків розвилки й лишається жити
45. Прибираючи вигаданий дефолт, прочитай СПОЖИВАЧА — заміна на «немає значення» відкриває нову гілку, і в неї своя вісь: ТЕРМІНАЛЬНА чи РЕТРАЙНА
46. Пошта: транспорт ENV-керований, і питання «чи жива пошта» має ОДИН предикат на трьох споживачів
47. Алерт БЕЗ кластера не видно нікому — тож писати його без резолвера означає створювати рядок, який ніхто не закриє
48. Дублікат формули ховається не там, де шукають One-Home: не другий ВИКЛИК, а другий ВИВІД
49. Умовний броадкаст — це ДВІ незалежні умови, і вони МНОЖАТЬСЯ: множина тригерів ⊥ чи писач узагалі пускає колбеки
50. Rails ДЕДУПЛІКУЄ `before_action` за іменем фільтра — тож «зареєструвати той самий хук ще раз, пізніше» не додає прохід, а ПЕРЕСУВАЄ єдиний
51. Валідація-обмежувач із гардом `return unless X.present?` робить безпеку ОПЦІОНАЛЬНОЮ — і тоді ЗАПОВНЕННЯ межі є тим, що ламає фічу, а порожнеча — тим, що її «лікує»
52. Новий `critical` `alert_type` за замовчуванням ШТРАФУЄ ОПЕРАТОРА — і мовчки, бо upliftʼ гейтований прапорцем, який колись увімкнуть
53. Конфіг-колонка без СПОЖИВАЧА на пристрої — вигадана величина з виглядом виміряної; гейт, збудований на ній, помиляється в ОБИДВА боки
54. Твердження про поведінку ЛЮДИНИ, виведене з поведінки МАШИН, — підміна виміру з власним іменем; і глушити її треба на рівні МЕХАНІЗМУ, а не рядка даних
55. Фільтр придатності у `WHERE` зливає стани, які для ЛЮДИНИ різні — і тоді порожній набір відповідає на два питання одним мовчанням
56. Міняєш форму enqueue — негативний пін мусить переїхати на ТОЙ САМИЙ метод, інакше він стає вакуумним, а не червоним
57. Розширюючи скоуп sweep-воркера новою ногою, спитай не «чи знайде він рядок», а «чи застосовна до нього ДІЯ» — інакше прохід рахується виконаним, а робота не робиться
58. JSON-блок буває ДЗЕРКАЛОМ живої HTML-панелі того самого екшена — тоді «поле, якого ніхто не читає» є хибним діагнозом, а зняття ламає дзеркало
58a. Дзеркало #58 з протилежного боку, і воно дорожче: `render json:` віддає СИРУ AR-модель, тобто всі колонки, а стелею мусить бути те, що ВЖЕ друкує HTML того ж екшена
59. Лінк на РЯДОК партиційної таблиці мусить нести `created_at`, інакше промах ТИХИЙ — і на екрані він невидимий
60. Свіп за ФОРМОЮ знаходить те, чого свіп за іменем не бачить — і найдорожче лежить у МОДЕЛІ, куди «вʼюшний» клас не заглядає
61. Машина має право знімати твердження про СИГНАЛ і не має — про СВІТ за сигналом; різниця вирішує, чи авто-резолвер узагалі законний
62. Однакове ПИТАННЯ не означає однакову ФОРМУ: ратифікований прецедент не переноситься, доки не переміряно ПЕРЕДУМОВУ, на якій він виграв
63. `store_accessor` на JSONB тримає рівно вантаж форми — тобто РЯДОК; і `allow_nil` порожнього рядка не покриває
64. Похідне поле, чиє ІМʼЯ стверджує повноту, не сміє ховати невідомий доданок — і `.to_f` на nullable-вході знімає останній шанс це помітити
65. `have_key` — це пін на НАЯВНІСТЬ, і він зелений на будь-якому значенні; клас, який він пропускає, — саме той, що коштує грошей
66. Воркер із НУЛЕМ enqueuer'ів читається як живий — бо його хуки тримає зеленими сюїта, що смикає їх руками
67. Механізм чесності, збудований на ОДНОМУ ярусі, годується неміряним агрегатом з ярусу НИЖЧЕ — і власний гард цього не бачить за побудовою
68. Схема — НЕ поверхня моделі: `db/structure.sql` не знає про `store_accessor`/`delegate`/`alias_attribute`/`attribute`, тож «такої колонки немає» є заявою про ІНСТРУМЕНТ
69. Два step-up гарди в ОДНОМУ контролері мають РІЗНУ форму, і це присуд, а не недогляд — «уніфікувати» їх означає зламати безпеку тихо
70. Шим, що зберігає API КЛАСУ, може губити СЕМАНТИКУ — і його власна шапка стверджує протилежне
71. Коли зовнішня бібліотека «нормалізує» поле замість тебе, питай не «чи є значення», а «чим саме вона позначає НЕВАЛІДНЕ»
72. Чергу ActiveJob-джоби обирає ДЕФОЛТ ГЕМА, і `sidekiq_options` цього класу не бачить — тому пріоритет тракту ламається на ОСТАННЬОМУ кроці
73. `where.not(a: x, b: y)` — це ЗАПЕРЕЧЕННЯ КОНʼЮНКЦІЇ, тобто `NOT (a = x AND b = y)` = «АБО», а не «І по обох»
74. Вихід із `transaction do` через `return`/`next`/`break` на Rails 8.1 КОМІТИТЬ уже записане — а коп, який мав би це стерегти, вимкнений САМИМ гемом на премісі, яку наш рантайм спростовує
75. `before_action … only: [ … ]` — це ПОІМЕННИЙ реєстр, і він НЕ росте разом із контролером: новий екшен їде без гарда, і мовчки
76. `nil`-гард для стану, який ТВІЙ ЖЕ писач створити не може, — це мертвий код із виглядом обачності; знаходить його гілкова підлога покриття, а лікує ЗНЯТТЯ гілки, не тест
77. «Гейт дасть людині ВІКНО» — це твердження про ЧАС, і вимірюється воно життям ДЖОБИ, а не наміром
78. Гейт може стерегти двері, ключ від яких видає ЙОГО Ж тракт: предикат читає канал, у який пише той самий актор, якого гейт відсікає
79. Дедуп-ключ, вужчий за множину ПРИЧИН, губить другий вердикт МОВЧКИ — і найдорожче тут не втрата запису, а те, що на цьому ж ключі висить чужий глушник
80. Реєстр, що класифікує КОЛОНКИ, до ВМІСТУ JSONB сліпий за побудовою — і ціну задає не колонка, а КАНАЛ, у який той вміст їде
81. Предикат `may_<event>?` буває ГАРДОМ ВИПАДКОВО — і тоді розширення AASM-події тихо роззброює кожного, хто на ньому стояв
82. Периметр ENUM'а знає ГЕЙТ, не твій греп — і найдешевший спосіб дізнатись невідомий дім це прогнати СЮЇТУ, а не вигадати ще один запит
83. Канал живості (`trees.last_seen_at`) має рівно ОДНОГО легітимного писача, і критерій не «машина чи людина», а «чи доводить цей кадр, що почули САМЕ ЦЕЙ вузол»
84. `admin` — роль ОРГАНІЗАЦІЙНА, і формула `admin_or_above?` (`role_admin? || role_super_admin?`) не переноситься на ресурс, що не проходить через `acting_organization!`
85. `lib/daemons/coap_listener` — однопроцесний, однопотоковий демон, і саме ЦЕ, не бізнес-логіка, робить `ActuatorCommand`-AASM безпечним без `FOR UPDATE`
86. `MaintenanceRecord`: засвідчення НЕ замикає запис — і протокол лісника сьогодні обходить це інструкцією людині, не кодом

<!-- /BACKEND-GOTCHAS-INDEX -->

## Common Tasks

- **Add a REST endpoint**: контролер < `BaseController`, thin (params + authz + render — CLAUDE §6); клієнтський FK → IDOR-guard (#2); Blueprint-серіалізатор; spec per `04_06 §A`. **Спершу відповідь на #23 — хто відвантажує клієнта:** браузерний → маршрут у `scope module: "api/v1"` + рядок у `04_03 §4.1`; машинний → у `namespace :api/:v1` + рядок у `§4.2` + запис у реєстр `spec/security/path_literal_route_consistency_spec.rb`, якщо на нього ставиться throttle. **Гейт: `spec/quality/route_doc_parity_spec.rb`** — рядок у `04_03 §4` більше не на совісті [DOC-T.87]; доти цей рецепт був правилом БЕЗ носія, і один ендпоінт прожив без рядка 17 діб при зеленому CI. ⚠️ Гейт судить лише пару (метод, шлях) — колонку `Доступ` він не звіряє, тож хибне `🔑 Auth` пройде зеленим. ⊕ **Три речі, які цей рецепт не називав до 2026-08-24 і які коштували червоної сюїти в самому кінці проходу:** (1) 🔴 **`before_action … only: [ … ]` — ПОІМЕННИЙ реєстр, і він не росте сам** — новий екшен їде без `set_*` (500) і без authz (тихо, і саме це дорого) → гоча #75, там і дискримінатор між двома провалами; (2) **member-маршрут вимагає крихти breadcrumb у ЧОТИРЬОХ локалях** — популяцію гейт деривує з `Rails.application.routes`, тож новий сегмент червонить `spec/i18n/breadcrumb_segment_parity_spec.rb`, а не i18n-tasks (дім механіки — скіл `frontend`, гоча #10-родина); (3) **рядок у `04_03 §4` несе НОМЕР**, тож вставка в середину таблиці означає перенумерацію хвоста — робити скриптом із перевіркою діфу, бо номери слабо-несучі (прецедент уже стався — три коментарі `actuators_controller` роками звали `command_status` «ендпоінтом #48», тоді як це **#49**, і `route_doc_parity` номера не читає за оголошеною стелею).
- **Remove a column**: ⛔ **ДВА кроки, і одним комітом НЕ ВИЙДЕ** — `strong_migrations` відкине `remove_column`, і він має рацію: під час rolling-деплою старий контейнер живе з КЕШОВАНОЮ схемою, тож зняття в тому ж деплої дає вікно, де AR очікує колонку, якої в БД уже немає. **Крок 1** — `self.ignored_columns += %w[col]` у моделі, ЗАДЕПЛОЇТИ. **Крок 2** — окремим комітом `remove_column` міграцією поверх анкера, і ТИМ ЖЕ комітом зняти `ignored_columns`-рядок, інакше він переживе свою підставу. 🔴 **«Я виміряв, читачів нуль» — НЕ підстава продавити `safety_assured`** (спроба 2026-09-05, `clusters.entropy_score`): ризик не у ВАШИХ викликах, а в процесі, який ще не помер. ⚖️ **Межа правила названа виміром 2026-09-05, і без неї рецепт коштує роботи ні за що: два кроки стережуть ROLLING-ДЕПЛОЙ З ДАНИМИ, а pre-launch сквош цю ПЕРЕДУМОВУ зносить** — база дропається й сіється наново, тож `clusters.entropy_score` зрештою пішов ALTER-ом у dev перед дампом, без жодного `ignored_columns`, і двокрокова нога прожила пів години (`00_07` E.64). ⛔ Не читай це як дозвіл на `safety_assured`: підстава відпадає лише поки бази З ДАНИМИ не існує. ⚠️ І тоді ОБОВʼЯЗКОВО лиши провенанс мінус-рядка `structure.sql` у шапці анкера — `git log -S` ALTER перед дампом не покаже, а гейт дампу правильно не відрізняє легітимне зняття від дрейфу dev-БД. 🔴 **ТРЕТІЙ випадок, виміряний 2026-09-06 на canopy, і жоден із двох вище його не покриває: живий слот ІЗ ДАНИМИ, чий деплой-ланцюг міграцій НЕ ГАНЯЄ.** `grep -c "db:migrate" config/deploy.yml .github/workflows/deploy*.yml` → нуль скрізь; живі `.kamal/hooks` схеми не чіпають, а ті, що могли б, лишились семплами. Отже **крок 2 не виконається НІКОЛИ**, і сумлінне виконання рецепта вище дає деплой, який зняв КОД і лишив КОЛОНКУ персональних даних. Порядок тут зворотний до інтуїтивного і має ТРИ кроки: **(1) ДЕПЛОЙ коду** (інакше старий образ валідує колонку, якої вже нема); **(2) `ALTER … DROP COLUMN` із гардом У СКРИПТІ** — лічи непорожні значення й `abort`, якщо не нуль; **(3) РЕСТАРТ контейнерів**. 🔑 Третій крок і є тим, чого в первісному плані не буває: Rails кешує перелік колонок У ПРОЦЕСІ, тож після `ALTER` живі web/job тримають привида й `User.create!` будує `INSERT` із неіснуючою колонкою. **«Код знає» і «процес знає» — два різні твердження, і деплой закриває лише перше.** ⚠️ Перевіряй не деплой-лог, а `User.column_names` на живому вузлі. ⊕ І перш ніж знімати — розрізни клас: **колонка** без писача й читача знімається, а **enum-значення** в історичних рядках лишається, бо рендер читає його з даних (`EwsAlert#message`); рендер ⊥ колонка.
- **Add a model / column**: дім `04_01`; схема = нова міграція поверх squash-анкера → `db:migrate` → `db:schema:dump` (рецепт в `init_consolidated`; re-squash — окремо); uniqueness-валідація → дзеркальний unique-index (ARCH.56); enum → `prefix`; привілейована мутація → Auditable-хук (#12). Гейт: `ruby scripts/model_doc_sync.rb`. 🔴 **`strong_migrations` ВІДХИЛИТЬ наївну форму, і це не порада, а стіна:** голий `add_index` падає з «Adding an index non-concurrently blocks writes», `add_foreign_key` — з вимогою двох кроків. Ратифікована форма (прецедент двох перших post-анкерних міграцій, 2026-08-24): `disable_ddl_transaction!` + `add_index …, algorithm: :concurrently` + `add_foreign_key …, validate: false` і `reversible { |dir| dir.up { validate_foreign_key … } }`. ⛔ **`safety_assured` НЕ використовувати за замовчуванням** — це твердження про МАЙБУТНЄ («таблиця буде мала, коли міграція виконається»), якого ніхто не гарантує, тоді як safe-форма правильна в кожному сценарії й коштує три рядки; прецеденту `safety_assured` у `db/migrate/` свідомо нема.
- **Add a service / worker**: `app/services/<domain>/` або `app/workers/`; чергу обґрунтуй проти CLAUDE §5 (strict-priority!); реєструй у `04_02 §11`.
- **Local-verify**: `bin/rubocop -a` → `bin/rspec` (full перед push) → `ruby scripts/model_doc_sync.rb` при touch models/services/workers. ⊕ `rubocop` і `brakeman` з 2026-08-28 ще й БЛОКУЮТЬ на `pre-push` [OPS.28], тобто спрацюють і без цього рядка — але `model_doc_sync` носія не має, і саме він тут лишається на дисципліні.

## Keep Bounded — route, don't restate

- **money-path / minting / slashing / Wallet-баланси / BlockchainTransaction-AASM** → скіл `web3-pipeline` (`04_02 §4/§9/§10`, `05_0x`); `EMISSION_THRESHOLD` (TokenomicsEvaluatorWorker + SystemParameter override) — теж там.
- **telemetry uplink→mint / TelemetryLog / KENOSIS / черги / partition-pruning** → скіл `telemetry-pipeline` (CLAUDE §5/§6, `05_02`).
- **Phlex / Tailwind / Stimulus / Turbo** → скіл `frontend` (`04_04`); `render_dashboard` тут — лише транспорт.
- Інваріанти CLAUDE §5/§6 — дім ТАМ; цей скіл їх не дублює.

Attribution

Alexey-LukinAlexey-Lukin
View sourceMore from Alexey-Lukin →
SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Related Skills

Mysql Best Practices

MySQL development best practices for schema design, query optimization, and database administration

2481 votes

Clickhouse Io

ClickHouse数据库模式、查询优化、分析和数据工程最佳实践,适用于高性能分析工作负载。

2456590 votes

Postgres Patterns

基于Supabase最佳实践的PostgreSQL数据库模式,用于查询优化、架构设计、索引和安全。

2456590 votes

Jpa Patterns

Spring Boot中的JPA/Hibernate实体设计、关系、查询优化、事务、审计、索引、分页和连接池模式。

2456590 votes

Database Optimizer

Expert database optimizer specializing in modern performance

458250 votes
View all in databases →