Quality gates, documentation, release automation та internal tools
Автоматизація стає небезпечною не тоді, коли падає. Небезпечний момент настає, коли команда покладається на її verdict, але не розуміє evidence та recovery. На прикладі однієї зміни в TeamOrbit пов'яжемо merge gate, docs drift, release proposal, artifact promotion і безпечний повтор частково виконаного release.
- вирішимо, який signal отримує право блокувати merge;
- зробимо docs impact відтворюваним, а не суб'єктивним;
- відокремимо release proposal від незворотного publish;
- перевіримо зовнішній state перед повторним запуском;
- не перетворимо особистий script на team dependency без власника.
Gate стає залежністю команди
Required check займає 18 хвилин і часом червоніє без зміни code. Через тиждень розробники натискають rerun, не читаючи log. Такий gate уже перевіряє не якість, а терпіння команди.
- рішення - gate дозволяє merge або зупиняє роботу;
- ціна - wait time, false blocks, maintenance та обхідні шляхи;
- межа - якщо failure mode не можна назвати, check залишається informational.
Сам би не тримав noisy check у required до останнього. Спочатку відновив би довіру до нього, а вже потім дав би право блокувати.
Ризик визначає evidence та verdict
TeamOrbit додає --timezone до CLI-команди експорту. Код зелений, але operator guidance може не оновитися. Gate починається з цього ризику, а не з вибору action.
gate: cli-doc-contract
risk: public CLI changed but operator guidance stayed stale
evidence: npm run check:docs -- export
mode: observe
owner: sam@teamorbit
failure_artifact: artifacts/cli-doc-contract.diff
risk -> evidence -> verdict -> owner, а неtool -> status;- evidence відтворюється локально й закриває один failure mode;
- роль owner може взяти на себе один розробник, а не окрема DevX-команда.
Required status працює як API для рішень команди. Його output contract важливіший за назву action.
Перевірка заслуговує на право блокувати
Гарна ідея ще не означає, що check має право зупинити кожен PR. Спочатку він спостерігає за реальними змінами, показує would-block cases і доводить, що команда може відрізнити signal від шуму.
Право можна відкликати: noisy check повертається до informational mode, а signal, що дублюється, прибирають із pipeline.
| Період | Wrong blocks | Median |
|---|---|---|
| перші 20 PR | 1 із 4 | 48 sec |
| наступні 20 PR | 0 із 3 | 42 sec |
40 PR - дані TeamOrbit, а не стандарт. Після суттєвих змін у checker або самому ризику gate знову проходить етап observe.
Hard gate лише для відтворюваного факту
AI finding корисний як lead: він називає ризик, який ще не оформлений у rule. Але status з'являється лише там, де команда може повторити перевірку без моделі.
| Required evidence | Advisory evidence |
|---|---|
| tests, lint, type-check | Claude review finding |
| contract та executable example | architecture або docs suspicion |
| calibrated coverage та basic scanning | release notes phrasing |
| artifact integrity та release preflight | usability concern |
Кілька AI reviewers дають кілька hypotheses, а не кворум. Якщо finding повторюється, його можна відтворити й описати як machine-checkable contract, команда пише deterministic check і калібрує його в observe. Решта залишається advisory або переходить у human review.
Червоний gate повертає наступний крок
Червона позначка без пояснення перекладає підтримку gate на кожного автора PR. Failure output має одразу показати mismatch і команду для відтворення.
FAIL cli-doc-contract
changed: src/commands/export.ts
mismatch: --timezone defaults to UTC; README says local
reproduce: npm run check:docs -- export
owner: sam@teamorbit
artifact: artifacts/cli-doc-contract.diff
- що порушено - конкретний contract;
- де evidence - raw diff, а не AI summary;
- як повторити - project-native command;
- хто лагодить checker, якщо зламався саме він.
Так, це довше за повідомлення quality gate failed. Зате developer не починає розслідування з пошуку власника чужої загадки. І ще: rerun без нового evidence - не recovery.
Docs gate ловить drift, а не відсутність файлу
Правило code changed, docs must change швидко породжує фіктивні README-edits. Потрібен інший зв'язок: public surface змінився, отже конкретний executable contract має підтвердити documentation.
Claude розширює пошук affected docs, але exact mismatch підтверджує generator, schema diff або executable example. Модель пропонує напрямок, command повертає verdict.
- CLI, API, config, env та migration запускають targeted checks;
- internal refactor не зобов'язаний змінювати README;
- docs diff живе в тому самому PR, бо описує той самий change.
TeamOrbit: code зелений, інструкція бреше
Ось у чому конфлікт. Test suite цього не ловить, бо runtime behavior коректний. Помилку бачить людина, яка повірила README.
$ teamorbit export --help
--timezone <iana> Default: UTC
$ rg "timezone" README.md
Export uses the operator local timezone by default.
$ npm run check:docs -- export
FAIL default mismatch: CLI=UTC docs=local
CI передає diff і generated help у read-only run: без tools, без session persistence, з лімітом turns. Тут важливий новий output contract, а не повтор CLI flags:
{
"affected_docs": ["README.md"],
"evidence": ["CLI default UTC != docs local"],
"uncertainty": ["docs/cli.md was not part of this run input"]
}
- CI читає поля з
structured_output, але developer звіряє їх із raw evidence; - названу uncertainty закриває людина:
rg "timezone" docs/cli.mdіншої розбіжності не знаходить; - developer приймає невеликий README diff і project fragment
changes/1842.added.md; - той самий deterministic command завершується успішно.
Review тепер обговорює реальний default, а не якість прози.
Генеруй reference, пиши reasoning
Не весь текст варто синхронізувати вручну. Машина добре синхронізує факти, які вже є в code; причини й обмеження все одно належать автору рішення.
| Тип знання | Робоча практика |
|---|---|
| CLI flags, OpenAPI, config reference | генерувати й diff-ити |
| code sample, install command | виконувати в CI |
| migration, trade-off, limitation | писати й review-ити вручну |
| runbook | перевіряти команди, owner та assumptions |
Claude може підготувати draft за наявним evidence, але не повинен вигадувати rationale або compatibility claim. Найкраща docs automation зменшує обсяг тексту, який команда має підтримувати вручну.
Release починається з proposal
Merge збирає change, але ще не створює release. Спочатку потрібен review surface, де version, user impact і artifact identity видно в одному diff.
- version proposal - який compatibility claim заявляє команда;
- project fragments - curated changelog із
changes/*.md; - release notes - draft із linked PR та issue;
- manifest - commit SHA, artifact та integrity;
- approval - людина ухвалює рішення до tag та publish.
Claude редагує draft і позначає uncertain impact, але не визначає SemVer за commit titles. Почати можна без робота: перший release PR цілком можна зібрати вручну.
Release просуває перевірений artifact
Release пов'язує три identity: Git commit, package integrity і registry version. Повторний build створює четвертий об'єкт, якого ці checks не бачили.
Tag вказує на commit 8f31c2a. Manifest пов'язує цей commit із package integrity, а proposal і registry record зберігають mapping. Version без commit та integrity не відповідає на запитання, що саме пройшло checks.
- release job завантажує candidate і перевіряє manifest;
- tag target має збігатися з commit із manifest;
- втрачений artifact або mismatch зупиняє publish.
Identity chain ламається на першому ідентифікаторі, який automation не звірила.
Publish має пережити повтор
Registry прийняв @teamorbit/cli@2.8.0, а timeout стався до tag. Rerun не знає, що встигло змінитися, тому спочатку отримує single-writer lock і читає state.
Перший run і resume проходять через одну state machine. missing, found та read error - різні стани: після write job система знову читає registry, а після створення tag ще раз перевіряє target commit.
npm public registry не дозволить повторно використати ту саму пару name@version, навіть після unpublish. Integrity mismatch або чужий tag target зупиняють job: automation не переписує зовнішню історію.
package@version обслуговує один release run. Роль lock у GitHub Actions відіграє concurrency group: другий запуск чекає в черзі й не скасовує перший.Не кожен повтор вартий нового інструмента
Після четвертого ручного release audit хочеться одразу зібрати plugin. Але кількість користувачів визначає лише потребу в distribution. Сам механізм обираємо за типом роботи: deterministic operation або Claude reasoning.
| Спостережуване завдання | Мінімальна відповідь | Прихована ціна |
|---|---|---|
| одноразове й ще не зрозуміле | не будувати tool | майже нульова |
| повторювана deterministic operation | script або CLI | tests та interface |
| повторюване Claude reasoning | skill | prompt eval та onboarding |
| потрібні live external capabilities | MCP | auth та availability |
| кілька extension assets у різних repo | plugin | compatibility та support |
Це decision matrix, а не maturity ladder. Shared script може обслуговувати всю команду; skill потрібен не через кількість людей, а тоді, коли model judgment стає частиною workflow. У TeamOrbit досить scripts/release-audit.mjs.
Другий користувач перетворює script на team asset
Merge показує, що script існує. Другий користувач показує, що ним можна користуватися без автора поруч. Це і є перший acceptance test внутрішнього інструмента.
- clean checkout - другий користувач запускає tool за README або
--help; - contract - stable input/output, meaningful exit codes та один contract test;
- safety -
--dry-run, disable path і зрозумілий fallback; - ownership - названо людину, яка лагодить script, коли ламається саме він.
Для repo-local script version і support boundary можуть успадковуватися від repository. Окремі versioning, telemetry та compatibility policy з'являються, коли є зовнішні consumers або великий blast radius.
Recovery обирає дію за станом
Rollback підходить не для кожного side effect. Спочатку читаємо external state і порівнюємо з manifest, потім обираємо recovery.
Звичайний функціональний npm-баг: конкретну версію позначають deprecated і випускають patch.
- targeted rerun доречний, коли причина відома;
- disable path готують заздалегідь: умова
if: vars.CLI_DOC_CONTRACT_ENABLED == 'true'прибирає зламаний check без редагування workflow; - після override залишаються actor, reason та follow-up;
- для recurring або high-impact failure створюють incident note; для звичайного досить issue або fix у PR.
Якщо йдеться про secret, malware або private data, потрібен інший path: stop, revoke credentials і рішення про unpublish за incident procedure, за потреби з registry support.
Фінал: автоматизація заслуговує на authority
Фінальний claim стосується одного change, одного package й одного перевіреного стану release. Усе, що лишилося за межами evidence, названо прямо.
CHANGE
PR 1842: add --timezone to teamorbit export
GATES
cli-doc-contract: PASS, required, owner sam@teamorbit
Claude docs impact: advisory, reviewed
DOCS
generated help tested; README corrected
change fragment: changes/1842.added.md
RELEASE
proposal: 2.8.0 approved
lock: one run for @teamorbit/cli@2.8.0
commit: 8f31c2a
integrity: sha512-q1v3...G9w==
partial publish: resumed after integrity and tag-target checks
INTERNAL TOOL
release-audit: informational, second user verified
NOT PROVEN
production rollout health; production permission policy
Automation отримує authority не тому, що вона автоматична. Gate має бути відтворюваним і owned; release - traceable та retry-safe; internal tool - корисним не лише автору й таким, що його можна вимкнути без розкопок.