@rt-tools/agent-kit 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/checks/check-file-size.mjs +19 -4
- package/assets/checks/check-state-next.mjs +202 -0
- package/assets/checks/check-states.mjs +142 -0
- package/assets/checks/check-turn-map.mjs +146 -0
- package/assets/checks/rt-kit-checks.config.mjs +16 -2
- package/assets/defaults/project.sh +67 -7
- package/assets/defaults/turn-map.md +48 -0
- package/assets/hooks/browser-device-id.sh +2 -0
- package/assets/hooks/browser-guard-device-id.sh +5 -1
- package/assets/hooks/browser-guard-no-asking.sh +5 -1
- package/assets/hooks/browser-guard-no-listing.sh +2 -0
- package/assets/hooks/browser-guard-no-other-drivers.sh +7 -3
- package/assets/hooks/browser-guard-require-select.sh +6 -2
- package/assets/hooks/claim-guard.sh +5 -1
- package/assets/hooks/commit-msg.sh +2 -0
- package/assets/hooks/conscience-guard.sh +5 -1
- package/assets/hooks/constitution-index.sh +2 -0
- package/assets/hooks/dev-server-guard.sh +7 -3
- package/assets/hooks/dispatch.sh +69 -0
- package/assets/hooks/docs-guard.sh +8 -4
- package/assets/hooks/exam-guard.sh +7 -3
- package/assets/hooks/git-guard-delivery-signature.sh +2 -0
- package/assets/hooks/git-guard-delivery.sh +39 -5
- package/assets/hooks/git-guard-main.sh +8 -4
- package/assets/hooks/git-guard-push-tests.sh +8 -4
- package/assets/hooks/glossary-load.sh +2 -0
- package/assets/hooks/grill-gate.sh +6 -2
- package/assets/hooks/handoff-entry-guard.sh +6 -2
- package/assets/hooks/handoff-write.sh +124 -0
- package/assets/hooks/hook-input.sh +54 -0
- package/assets/hooks/lint-after-edit.sh +7 -3
- package/assets/hooks/observe.sh +2 -0
- package/assets/hooks/postmortem-guard.sh +5 -1
- package/assets/hooks/proposal-guard.sh +5 -1
- package/assets/hooks/prose-style-guard.sh +7 -3
- package/assets/hooks/qa-dataid-guard.sh +6 -2
- package/assets/hooks/rerun-guard.sh +7 -3
- package/assets/hooks/reuse-first-guard.sh +7 -3
- package/assets/hooks/roles.sh +2 -0
- package/assets/hooks/rule-article.sh +99 -0
- package/assets/hooks/skill-gate-layers.sh +2 -0
- package/assets/hooks/skill-gate-rearm.sh +5 -1
- package/assets/hooks/skill-gate.sh +25 -2
- package/assets/hooks/skill-loaded.sh +5 -1
- package/assets/hooks/sql-guard-parse.sh +2 -0
- package/assets/hooks/sql-guard-request.sh +4 -1
- package/assets/hooks/sql-guard-target.sh +2 -0
- package/assets/hooks/sql-guard-write.sh +2 -0
- package/assets/hooks/sql-guard.sh +6 -2
- package/assets/hooks/task-context-load.sh +2 -0
- package/assets/hooks/task-flow-guard.sh +8 -4
- package/assets/hooks/turn-entry-load.sh +62 -0
- package/assets/hooks/turn-exit-guard.sh +44 -17
- package/assets/hooks/utf8.sh +35 -0
- package/assets/hooks/waiting-turn-guard.sh +5 -1
- package/assets/hooks/window-fill-guard.sh +37 -5
- package/assets/laws/work-conduct.md +34 -9
- package/assets/patterns/dependencies-upgrade.md +1 -1
- package/assets/patterns/doc-style-write.md +3 -3
- package/assets/patterns/git-workflow-commit.azure.md +2 -202
- package/assets/patterns/git-workflow-commit.github.md +2 -258
- package/assets/patterns/git-workflow-commit.gitlab.md +1 -217
- package/assets/patterns/git-workflow-docker.md +3 -3
- package/assets/patterns/git-workflow-merge.md +3 -2
- package/assets/patterns/git-workflow-migration.md +3 -3
- package/assets/patterns/git-workflow-pr.azure.md +224 -0
- package/assets/patterns/git-workflow-pr.github.md +280 -0
- package/assets/patterns/git-workflow-pr.gitlab.md +240 -0
- package/assets/patterns/git-workflow-restart.md +3 -3
- package/assets/patterns/git-workflow-secrets.md +3 -3
- package/assets/patterns/task-flow-archive.md +193 -0
- package/assets/patterns/task-flow-close.md +11 -160
- package/assets/patterns/task-flow-handoff.md +22 -4
- package/assets/patterns/task-flow-resume.md +6 -0
- package/assets/patterns/task-flow-start.md +41 -0
- package/assets/patterns/turn-entry-map.md +81 -0
- package/assets/pitfalls/doc-style.md +80 -0
- package/assets/pitfalls/git-workflow.azure.md +50 -0
- package/assets/pitfalls/git-workflow.github.md +78 -0
- package/assets/pitfalls/git-workflow.gitlab.md +49 -0
- package/assets/pitfalls/spec-driven.md +36 -0
- package/assets/pitfalls/styling-bem.md +45 -0
- package/assets/pitfalls/task-flow.md +62 -0
- package/assets/pitfalls/testing.md +70 -0
- package/assets/rules/deploy-flow.azure.md +106 -0
- package/assets/rules/deploy-flow.github.md +113 -0
- package/assets/rules/deploy-flow.gitlab.md +108 -0
- package/assets/rules/doc-style.md +25 -76
- package/assets/rules/git-workflow.azure.md +6 -92
- package/assets/rules/git-workflow.github.md +14 -127
- package/assets/rules/git-workflow.gitlab.md +6 -93
- package/assets/rules/spec-driven.md +39 -30
- package/assets/rules/styling-bem.md +20 -39
- package/assets/rules/task-flow.md +17 -186
- package/assets/rules/testing.md +3 -64
- package/assets/rules/turn-conduct.md +206 -0
- package/assets/rules/turn-entry.md +93 -0
- package/assets/rules/typescript-conventions.md +15 -0
- package/assets/skills/agent-kit.md +35 -12
- package/assets/templates/pitfalls.md +10 -0
- package/assets/templates/rule.md +5 -3
- package/lib/assets.d.ts.map +1 -1
- package/lib/assets.js +6 -1
- package/lib/assets.js.map +1 -1
- package/lib/cargo.d.ts +42 -0
- package/lib/cargo.d.ts.map +1 -1
- package/lib/cargo.js +2 -0
- package/lib/cargo.js.map +1 -1
- package/lib/cascade.d.ts.map +1 -1
- package/lib/cascade.js +19 -1
- package/lib/cascade.js.map +1 -1
- package/lib/commands.d.ts.map +1 -1
- package/lib/commands.js +65 -4
- package/lib/commands.js.map +1 -1
- package/lib/config.d.ts +16 -1
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +8 -0
- package/lib/config.js.map +1 -1
- package/lib/hooks-map.d.ts +13 -0
- package/lib/hooks-map.d.ts.map +1 -1
- package/lib/hooks-map.js +33 -1
- package/lib/hooks-map.js.map +1 -1
- package/lib/observations.d.ts +35 -1
- package/lib/observations.d.ts.map +1 -1
- package/lib/observations.js +14 -2
- package/lib/observations.js.map +1 -1
- package/lib/ship.d.ts +2 -0
- package/lib/ship.d.ts.map +1 -1
- package/lib/ship.js +2 -0
- package/lib/ship.js.map +1 -1
- package/lib/thresholds.d.ts +49 -0
- package/lib/thresholds.d.ts.map +1 -0
- package/lib/thresholds.js +151 -0
- package/lib/thresholds.js.map +1 -0
- package/package.json +1 -1
- package/rt-tools-agent-kit-0.12.0.tgz +0 -0
- package/assets/commands/agent-kit-digest.md +0 -88
- package/assets/commands/rules-review.md +0 -98
- package/rt-tools-agent-kit-0.10.0.tgz +0 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: turn-entry-map
|
|
3
|
+
kind: pattern
|
|
4
|
+
rule: turn-entry
|
|
5
|
+
description: Паттерн правила turn-entry. Брать при правке карты хода и хука, который её подаёт, — что в карту входит, чем она отличается от правила, как хук молчит о недостающем и как это проверяется. Не брать для формы самой передачи — это паттерн task-flow-handoff.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Карта хода и её подача — готовый код
|
|
9
|
+
|
|
10
|
+
Паттерн правила `turn-entry`. Что при этом должно быть верно — закон
|
|
11
|
+
`docs/constitution/work-conduct.md`.
|
|
12
|
+
|
|
13
|
+
## Когда брать
|
|
14
|
+
|
|
15
|
+
- В правило ведения работы добавили состояние — карта отстала.
|
|
16
|
+
- Правится хук входа или порядок, в котором части входа кладутся в контекст.
|
|
17
|
+
- Проверка карты покраснела.
|
|
18
|
+
|
|
19
|
+
## Что входит в карту, а что нет
|
|
20
|
+
|
|
21
|
+
Карта отвечает на вопрос «что делать», правило — на вопрос «почему». Признак отбора один: строка,
|
|
22
|
+
которую заход прочитает и после которой сделает следующий шаг, — в карту; строка, которая
|
|
23
|
+
объясняет, откуда требование взялось, — в правило.
|
|
24
|
+
|
|
25
|
+
| В карту | В правило |
|
|
26
|
+
| ---------------------------------------------- | -------------------------------------------------- |
|
|
27
|
+
| имя состояния и его обязательное действие | почему это действие обязательно |
|
|
28
|
+
| паттерн, который состояние ведёт | разбор происшествия, из которого оно выросло |
|
|
29
|
+
| четыре выхода хода и чем каждый подтверждается | что бывает, когда ход кончают иначе |
|
|
30
|
+
| строка о том, что остальное — продолжение хода | перечень того, чем ход кончать нельзя, с примерами |
|
|
31
|
+
|
|
32
|
+
Разбор происшествия в карту не переезжает никогда: он объясняет, а объяснение — это правило.
|
|
33
|
+
|
|
34
|
+
## Подача: сперва передача, потом карта
|
|
35
|
+
|
|
36
|
+
Порядок не безразличен. Передача говорит, где именно стоит эта работа, карта — что делают в
|
|
37
|
+
таком месте вообще. Прочитанная первой, карта отвечает на вопрос, которого заход ещё не задал.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# передача — по имени текущей ветки, не выбором из каталога
|
|
41
|
+
handoff="$ROOT/${RT_HANDOFF_DIR:-.claude/handoff}/$(git branch --show-current).md"
|
|
42
|
+
[ -r "$handoff" ] && { printf 'ПЕРЕДАЧА ПРОШЛОГО ЗАХОДА\n\n'; cat "$handoff"; }
|
|
43
|
+
|
|
44
|
+
# карта — своим файлом, а не разбором правила
|
|
45
|
+
[ -r "$map" ] && { printf 'КАРТА ХОДА\n\n'; cat "$map"; }
|
|
46
|
+
|
|
47
|
+
exit 0
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Три вещи в этом куске обязательны и легко теряются:
|
|
51
|
+
|
|
52
|
+
- **`-r`, а не `-f`.** Файл может существовать и не читаться; `-f` тогда пропускает `cat`
|
|
53
|
+
дальше, и хук печатает заголовок над пустотой.
|
|
54
|
+
- **`exit 0` в конце и никаких других выходов.** Хук входа ничего не отбивает: заход без части
|
|
55
|
+
контекста лучше, чем отбитый запуск.
|
|
56
|
+
- **Имя файла собирается из ветки.** Выбор «первого попавшегося» в каталоге подаёт чужую
|
|
57
|
+
передачу, и выглядит она как своя.
|
|
58
|
+
|
|
59
|
+
## Чем это проверяется
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
node tools/check-turn-map.mjs # размер, полнота состояний в обе стороны, четыре выхода
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Проверка сверяет имена состояний карты с таблицей правила в обе стороны: состояние, заведённое
|
|
66
|
+
правилом и забытое в карте, и состояние, оставшееся в карте после переименования, — оба
|
|
67
|
+
расхождения.
|
|
68
|
+
|
|
69
|
+
Живая проба хука делается на дереве, где обе части лежат, и повторяется четырежды: обе части,
|
|
70
|
+
без передачи, без карты, без обеих. Последний случай обязан дать пустой вывод и нулевой код —
|
|
71
|
+
хук, промолчавший с ненулевым кодом, читается как отбитый запуск.
|
|
72
|
+
|
|
73
|
+
## Ловушки
|
|
74
|
+
|
|
75
|
+
- **Предел размера назначается замером, а не на глаз.** Первое число выбрали «вдвое больше
|
|
76
|
+
нынешней карты» — и проверка покраснела на собственном тексте в первом же прогоне: карта в
|
|
77
|
+
кириллице весит вдвое больше, чем кажется по числу строк.
|
|
78
|
+
- **Выход за предел означает деление карты, а не подъём предела.** Поднятый однажды, он
|
|
79
|
+
поднимается и во второй раз, и карта тихо становится вторым экземпляром правила.
|
|
80
|
+
- **Карта, разобранная из правила на месте, ломается молча.** Правку разметки таблицы не видит
|
|
81
|
+
ни одна проверка, а карта после неё приходит пустой — и заход об этом не узнает.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Тексты проекта — холодная часть
|
|
2
|
+
|
|
3
|
+
Ловушки: грабли, на которые уже наступали. Грузится не вместе с правилом, а по
|
|
4
|
+
требованию — при обычном решении она не нужна.
|
|
5
|
+
|
|
6
|
+
Правило — `doc-style`; статьи, которыми держится закон, стоят там.
|
|
7
|
+
|
|
8
|
+
## Ловушки
|
|
9
|
+
|
|
10
|
+
- **Оставшаяся работа не записывается в документ, а заводится задачей.** `docs/BACKLOG.md`
|
|
11
|
+
держит только то, что задачей не бывает: договорённости и решения, которые решено не
|
|
12
|
+
править. Признак — утверждение остаётся, если править его никто не собирается. «Сделать
|
|
13
|
+
потом» в плане, README или спеке — второй список работ: он расходится с бордой молча, а
|
|
14
|
+
разбирать его потом дороже, чем завести задачу сразу. Из 1411 строк документа действующими
|
|
15
|
+
оказались 71, и на разбор остальных ушла отдельная задача. Как разбирать накопившееся —
|
|
16
|
+
паттерн `doc-style-sweep`.
|
|
17
|
+
- **Словарь действует и на разговор с владельцем, не только на файлы.** Он приходит в контекст
|
|
18
|
+
на запуске сессии, поэтому «не читал» основанием не бывает. Слово из левой колонки «Так не
|
|
19
|
+
пишем» всплывало именно в ответах: в дереве его уже вычистили, а в PR о сделанном оно
|
|
20
|
+
оставалось, и владелец читал ровно то слово, от которого отказались.
|
|
21
|
+
- **Термин берётся из `docs/GLOSSARY.md`, а не придумывается на месте.** Слова, которого там
|
|
22
|
+
нет, у читателя нет тоже: «журнал приложения» простоял в спеке почты, пока владелец не
|
|
23
|
+
спросил, что это, — оказалось, логи бэкенда, а слово «журнал» здесь уже занято журналом
|
|
24
|
+
событий. Новое слово либо заводится в словаре вместе с правкой, либо заменяется тем, что
|
|
25
|
+
уже есть.
|
|
26
|
+
- **Проход по словарю глазами слово не находит.** «Формулировки приведены к словарю» означает
|
|
27
|
+
ровно те строки, которые в тот момент читали: «спека» пережила такой проход и осталась в
|
|
28
|
+
соседней строке того же файла. Слово из левой колонки таблицы «Так не пишем» вычищается
|
|
29
|
+
грепом по всему дереву, а не вычиткой. Форма задаётся точно: «спек» — документ — склоняется
|
|
30
|
+
в «спека» и «спеки» тоже, и совпадений по корню законных больше, чем нарушений; ищутся
|
|
31
|
+
сочетания («спеки на … нет», «спека проверяет»), а не корень.
|
|
32
|
+
- **Снятое имя вычищается одним грепом по всему дереву:** правила, их зеркала в скилах,
|
|
33
|
+
документы и комментарии. Описание того, чего в коде уже нет, читается как действующее
|
|
34
|
+
указание.
|
|
35
|
+
- **У снятого слова второе значение возвращается после сплошной замены, а не обходится до
|
|
36
|
+
неё.** Слово снимают ровно потому, что оно стояло над двумя вещами, и второе значение при
|
|
37
|
+
этом остаётся законным. Отобрать его заранее нечем: какое из двух значений в строке, видно
|
|
38
|
+
только по соседнему тексту, а строк бывают сотни. Порядок обратный — сплошная замена, затем
|
|
39
|
+
сплошной просмотр самой правки, и найденное второе значение возвращается поимённо. Из 353
|
|
40
|
+
замен так вернулись шесть, и две первые были поломкой: сверка печатала новое имя дважды
|
|
41
|
+
подряд, а комментарий обещал «поломку вместо PR о том, что долгов нет». Просматривается
|
|
42
|
+
правка, а не дерево после неё: в дереве обе стороны выглядят одинаково верными.
|
|
43
|
+
- **Поиск по дереву не покрывает того, что уже уехало наружу.** Заголовок задачи и её тело,
|
|
44
|
+
заголовок PR и его тело, заголовки коммитов лежат вне файлов, и проверки текстов их не
|
|
45
|
+
читают вовсе. Вычистив слово в дереве, обходят те же места в очереди работ и в истории:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
<клиент хостинга> api "<путь к PR>" --jq '.title, .body' | grep -i '<слово>'
|
|
49
|
+
<клиент хостинга> api "<путь к задаче>" --jq '.title, .body' | grep -i '<слово>'
|
|
50
|
+
git log --format='%s%n%b' <база>..HEAD | grep -i '<слово>'
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Заголовок PR правится вызовом хостинга, заголовок коммита — только переписыванием ветки,
|
|
54
|
+
поэтому его проверяют до пуша. Выдуманное слово было вычищено из трёх файлов и объявлено
|
|
55
|
+
снятым, а в заголовке PR и в заголовке коммита осталось — владелец прочитал именно его.
|
|
56
|
+
|
|
57
|
+
- **Число в тексте пересчитывается командой в том же коммите, где пишется.** Оно стареет
|
|
58
|
+
внутри одной ветки: «шестнадцать пар» стало неправдой через два коммита после того, как
|
|
59
|
+
было написано, и нашёл это владелец, а не проверка. Число, которое придётся пересчитывать
|
|
60
|
+
при каждой правке, лучше не писать вовсе. Число, полученное разбором текста, сверяется на
|
|
61
|
+
выборке руками до того, как его называют: разбор, не знающий второй формы записи, ошибается
|
|
62
|
+
молча — «51 пункт без задачи» оказался шестью, потому что номер стоял и отдельной строкой, и
|
|
63
|
+
в заголовке подраздела.
|
|
64
|
+
- **Названная в тексте проверка запускается, а не пересказывается.** «Проверка есть» и
|
|
65
|
+
«проверка проходит» — разные утверждения, и второго в тексте обычно нет вовсе. Из четырёх
|
|
66
|
+
проверок, названных правилом, три оказались не в том состоянии, в каком текст их описывает:
|
|
67
|
+
одна отдавала полтора десятка замечаний, вторая переписывала файлы самим запуском, третья
|
|
68
|
+
была красной и роняла общую сводку вместе с собой. Ни одна из трёх не входила в выкатку,
|
|
69
|
+
поэтому молчание было полным. Проверку, которая переписывает файлы, запускают на чистом
|
|
70
|
+
дереве: иначе её правки уедут чужим коммитом.
|
|
71
|
+
- **Сделанность читается по дереву, а не по тексту, который о ней написан.** Это верно в обе
|
|
72
|
+
стороны: строка про README обеих либ была вычеркнута как сделанная, а README остался с
|
|
73
|
+
прежним числом импортёров; задача, названная владельцу несделанной, оказалась наполовину
|
|
74
|
+
закрытой и покрытой сценариями хука. Пункт плана и тело задачи описывают день, когда их
|
|
75
|
+
написали, и с тех пор не менялись.
|
|
76
|
+
- **Комментарий в файле — такое же утверждение, как строка в документе.** Обоснование «строки
|
|
77
|
+
идут во всю ширину панели, иначе подсветка обрывается» было выдумано, прожило три сессии и
|
|
78
|
+
каждый раз читалось как основание вёрстку не трогать.
|
|
79
|
+
- **Чужие проекты не упоминаются нигде** — ни имени репозитория, ни «портировано из», ни
|
|
80
|
+
ссылок на его файлы. Описывается то, что код делает здесь, в терминах этого проекта.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Поставка — холодная часть
|
|
2
|
+
|
|
3
|
+
Ловушки: грабли, на которые уже наступали в дереве на Azure DevOps. Грузится не вместе с
|
|
4
|
+
правилом, а по требованию — при обычном решении она не нужна.
|
|
5
|
+
|
|
6
|
+
Правило — `git-workflow`; статьи, которыми держится закон, стоят там.
|
|
7
|
+
|
|
8
|
+
## Ловушки
|
|
9
|
+
|
|
10
|
+
- **Одна работа — одна задача, сколько бы файлов она ни задела.** Числа, за которым правка
|
|
11
|
+
становится вторым рабочим элементом, здесь нет: делится то, что придётся откатывать порознь.
|
|
12
|
+
Сплошная правка текстов дерева была заведена тремя задачами «по объёму» — пришлось стирать
|
|
13
|
+
два рабочих элемента, закрывать два PR и переносить коммиты по одному с двумя конфликтами.
|
|
14
|
+
Одна из трёх не дала коммита вовсе: правка тел уже заведённых задач веткой не бывает и задачей
|
|
15
|
+
под ветку тоже.
|
|
16
|
+
- **Рабочий элемент заводится командой, а не вызовами подряд.** Доска показывает элементы своей
|
|
17
|
+
области и итерации, и заведённый мимо них в очереди работ не виден: со стороны это выглядит
|
|
18
|
+
так же, как незаведённый. Команда заведения ставит все поля разом — род, состояние,
|
|
19
|
+
исполнителя, область и итерацию, — и печатает готовую строку заведения ветки. Замеченный по
|
|
20
|
+
ходу дефект проходит тот же путь.
|
|
21
|
+
- **Ветка заводится вторым вызовом, а не тем же.** Гард главной ветки отклоняет составную
|
|
22
|
+
«создать ветку и сразу коммитить» целиком: ветки в момент разбора ещё нет.
|
|
23
|
+
- **Сторона конфликта бывает удалением, и «сохранить обе стороны» заводит второе объявление.**
|
|
24
|
+
Главная ветка снимает объявление, потому что символ переехал, — в конфликте это выглядит как
|
|
25
|
+
сторона, которая ничего не дописала. Разбирается чтением версии главной ветки целиком, а не по
|
|
26
|
+
хунку, и сверяется проверкой повторов: обе копии сами по себе исправны, сборка и линт зелёные.
|
|
27
|
+
- **Учётная запись для пуша и автор PR выбираются отдельно.** Если пушить пришлось из-под другой
|
|
28
|
+
записи, на следующий вызов это не переносится: PR открывают токеном учётной записи машинной
|
|
29
|
+
работы, и от того, чьей записью он открыт, зависит, кого можно назначить ревьювером. Однажды
|
|
30
|
+
смена записи ради пуша утекла в публикацию — PR вышел от владельца.
|
|
31
|
+
- **Невалидный файл конвейера виден прогоном нулевой длительности сразу после пуша.** Прогон
|
|
32
|
+
заводится и кончается на разборе файла, не начав ни одного задания: в списке он стоит
|
|
33
|
+
отказом, а внутри нет ни задания, ни лога — читается только длительность. Поэтому список
|
|
34
|
+
прогонов ветки смотрится тем же движением, что и пуш: `az pipelines runs list` по своей
|
|
35
|
+
ветке.
|
|
36
|
+
- **`online` у агента на своей машине означает запущенный процесс, а не работающий конвейер.**
|
|
37
|
+
Две стороны сходятся отдельно: требования заданий и возможности самого агента в его пуле.
|
|
38
|
+
Пока пересечения нет, агент стоит `online` и не берёт ничего, а задания ждут размещённого
|
|
39
|
+
пула — по состоянию это выглядит настроенным. Владельцу называют выполненное задание с его
|
|
40
|
+
номером, а не строку состояния.
|
|
41
|
+
- **Вход в реестр образов из агента, запущенного службой, отказывает молча.** Служба идёт без
|
|
42
|
+
сеанса пользователя, а клиент реестра уходит в системный помощник хранения ключей и получает
|
|
43
|
+
отказ во взаимодействии — задание падает до сборки. Свой каталог настроек с пустым помощником
|
|
44
|
+
не спасает: клиент переписывает пустое значение обратно сам. Готовые команды — паттерн
|
|
45
|
+
`git-workflow-docker`.
|
|
46
|
+
- **Новое рабочее дерево получает только то, что лежит в индексе.** `git worktree add`
|
|
47
|
+
разворачивает коммит, а настройки, ключи, локальные разрешения и зависимости в коммит не
|
|
48
|
+
входят: свежее дерево выглядит готовым и упирается в нехватку не сразу, а на первом гарде,
|
|
49
|
+
которому нужен ключ. Что именно переносится руками, названо списком в компаньоне правила, и
|
|
50
|
+
список пополняется тем же движением, которым заводится новый файл вне индекса.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Поставка — холодная часть
|
|
2
|
+
|
|
3
|
+
Ловушки: грабли, на которые уже наступали в дереве на GitHub. Грузится не вместе с
|
|
4
|
+
правилом, а по требованию — при обычном решении она не нужна.
|
|
5
|
+
|
|
6
|
+
Правило — `git-workflow`; статьи, которыми держится закон, стоят там.
|
|
7
|
+
|
|
8
|
+
## Ловушки
|
|
9
|
+
|
|
10
|
+
- **Одна работа — одна задача, сколько бы файлов она ни задела.** Числа, за которым правка
|
|
11
|
+
становится второй задачей, здесь нет: делится то, что придётся откатывать порознь. Сплошная
|
|
12
|
+
правка текстов дерева была заведена тремя задачами «по объёму» — пришлось стирать две,
|
|
13
|
+
закрывать два PR и переносить коммиты по одному с двумя конфликтами. Одна из трёх не дала
|
|
14
|
+
коммита вовсе: правка тел уже заведённых задач веткой не бывает и задачей под ветку тоже.
|
|
15
|
+
- **Задача заводится командой, а не четырьмя вызовами подряд.** Борда к репозиторию не
|
|
16
|
+
привязана, и задача попадает на неё только явным добавлением: две задачи так и простояли вне
|
|
17
|
+
очереди работ, потому что шаг переписывали руками. Команда заведения делает все четыре — issue,
|
|
18
|
+
номер в его заголовке, добавление на борду, начальную колонку, — и печатает готовую строку
|
|
19
|
+
заведения ветки. Замеченный по ходу дефект проходит тот же путь.
|
|
20
|
+
- **Ветка заводится вторым вызовом, а не тем же.** Гард главной ветки отклоняет составную
|
|
21
|
+
«создать ветку и сразу коммитить» целиком: ветки в момент разбора ещё нет.
|
|
22
|
+
- **Сторона конфликта бывает удалением, и «сохранить обе стороны» заводит второе объявление.**
|
|
23
|
+
Главная ветка снимает объявление, потому что символ переехал, — в конфликте это выглядит как
|
|
24
|
+
сторона, которая ничего не дописала. Разбирается чтением версии главной ветки целиком, а не по
|
|
25
|
+
хунку, и сверяется проверкой повторов: обе копии сами по себе исправны, сборка и линт зелёные.
|
|
26
|
+
- **Учётная запись для пуша и автор PR выбираются отдельно.** Если пушить пришлось из-под другой
|
|
27
|
+
записи, на следующий вызов это не переносится: PR открывают токеном учётной записи машинной
|
|
28
|
+
работы, и от того, чьей записью он открыт, зависит, кого можно назначить ревьювером. Однажды
|
|
29
|
+
смена записи ради пуша утекла в публикацию — PR вышел от владельца. Разница между читающим и
|
|
30
|
+
пишущим вызовом в самом тексте команды не видна: личность приходит окружением, поэтому у
|
|
31
|
+
вызова на запись токен называется явно, а открытая заявка проверяется ответом хостинга о её
|
|
32
|
+
авторе — напечатанная ссылка говорит, что заявка создана, и молчит о том, кем. Чинится это
|
|
33
|
+
только переоткрытием: автора у заявки не сменить.
|
|
34
|
+
- **Невалидный файл конвейера виден прогоном нулевой длительности сразу после пуша.** GitHub
|
|
35
|
+
заводит такой прогон и на ветке, на которую ни один триггер не подписан: в списке он стоит
|
|
36
|
+
отказом, а внутри у него нет ни задания, ни лога — читается только длительность. Поэтому
|
|
37
|
+
список прогонов ветки смотрится тем же движением, что и пуш — `gh run list --branch <ветка>`.
|
|
38
|
+
Один такой отказ простоял в списке до мержа, и на него никто не посмотрел: выкатка после
|
|
39
|
+
мержа отказала ровно тем же.
|
|
40
|
+
- **Прогон, не вставший на пуш, возвращается повтором события, а не разбором ветки.** Замером
|
|
41
|
+
проверены обе законные дороги: и открытие PR, и пуш в уже открытый PR прогон заводят —
|
|
42
|
+
текстовый коммит и учётная запись, которой пушат, тут ни при чём. Пропавшие события пришлись
|
|
43
|
+
на час, когда хостинг отвечал `429` на загрузке действия и `503` на API, а списком прогонов
|
|
44
|
+
«не завёлся» от «не создан» не отличить. Поэтому вершину без прогона называет сверка очереди
|
|
45
|
+
работ, а событие возвращается новым коммитом либо перезакрытием PR.
|
|
46
|
+
- **Красное на шаге подготовки задания — отказ хостинга, а не дефект ветки.** Раннер не смог
|
|
47
|
+
скачать действие чекаута и получил `429 Too Many Requests`; до кода прогон при этом не дошёл
|
|
48
|
+
вовсе. Лечится перезапуском прогона, и от красного по существу отличается тем, на каком шаге
|
|
49
|
+
оно встало: три прогона одного дня упали именно так.
|
|
50
|
+
- **Контекст `runner` в `env` задания отбивает весь файл конвейера.** Там доступны только
|
|
51
|
+
`github`, `needs`, `strategy`, `matrix`, `vars`, `secrets` и `inputs`; `runner` появляется на
|
|
52
|
+
уровне шага, где то же значение приходит переменной окружения. Такой файл не принимается
|
|
53
|
+
вовсе: прогон кончается за ноль секунд, не начав ни одного задания. Разбор YAML этого не
|
|
54
|
+
ловит — синтаксис верный, а доступность контекстов синтаксисом не является.
|
|
55
|
+
- **`online` у раннера на своей машине означает запущенный процесс, а не работающий
|
|
56
|
+
конвейер.** Две стороны сходятся отдельно: `runs-on` у заданий и метки самого раннера. Пока
|
|
57
|
+
пересечения нет, раннер стоит `online` и не берёт ничего, а задания уходят в облако — по
|
|
58
|
+
состоянию это выглядит настроенным. Владельцу называют выполненное задание с его номером, а
|
|
59
|
+
не строку состояния.
|
|
60
|
+
- **Раннер на своей машине делает прогон общим ресурсом, и стенд у прогонов один.** Порт,
|
|
61
|
+
имя базы и каталог сборки зашиты в дереве одним значением на всех: два прогона разом
|
|
62
|
+
поднимают два стенда на один порт, и второй падает целиком. Дороже всего не падение, а его
|
|
63
|
+
вид — в отчёте оно выглядит десятком красных спек про экраны, то есть дефектом правки,
|
|
64
|
+
которого нет; три прогона подряд так и упали на трёх ветках, не тронувших кода. Лечится с
|
|
65
|
+
двух сторон сразу: конвейеру объявляется группа очереди на всё дерево, а имена стенда
|
|
66
|
+
читаются из окружения с нынешними значениями в умолчании — иначе прогон и гейт пуша, зовущий
|
|
67
|
+
ту же команду, столкнутся и при очереди. Одной очереди мало, одних имён — тоже: прогоны делят
|
|
68
|
+
ещё диск, кэш сборщика и демон образов.
|
|
69
|
+
- **Вход в реестр образов из раннера, запущенного службой, отказывает молча.** Служба идёт без
|
|
70
|
+
сеанса пользователя, а клиент реестра уходит в системный помощник хранения ключей и получает
|
|
71
|
+
отказ во взаимодействии — задание падает до сборки. Свой каталог настроек с пустым помощником
|
|
72
|
+
не спасает: клиент переписывает пустое значение обратно сам. Готовые команды — паттерн
|
|
73
|
+
`git-workflow-docker`.
|
|
74
|
+
- **Новое рабочее дерево получает только то, что лежит в индексе.** `git worktree add`
|
|
75
|
+
разворачивает коммит, а настройки, ключи, локальные разрешения и зависимости в коммит не
|
|
76
|
+
входят: свежее дерево выглядит готовым и упирается в нехватку не сразу, а на первом гарде,
|
|
77
|
+
которому нужен ключ. Что именно переносится руками, названо списком в компаньоне правила, и
|
|
78
|
+
список пополняется тем же движением, которым заводится новый файл вне индекса.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Поставка — холодная часть
|
|
2
|
+
|
|
3
|
+
Ловушки: грабли, на которые уже наступали в дереве на GitLab. Грузится не вместе с
|
|
4
|
+
правилом, а по требованию — при обычном решении она не нужна.
|
|
5
|
+
|
|
6
|
+
Правило — `git-workflow`; статьи, которыми держится закон, стоят там.
|
|
7
|
+
|
|
8
|
+
## Ловушки
|
|
9
|
+
|
|
10
|
+
- **Одна работа — одна задача, сколько бы файлов она ни задела.** Числа, за которым правка
|
|
11
|
+
становится второй задачей, здесь нет: делится то, что придётся откатывать порознь. Сплошная
|
|
12
|
+
правка текстов дерева была заведена тремя задачами «по объёму» — пришлось стирать две,
|
|
13
|
+
закрывать два MR и переносить коммиты по одному с двумя конфликтами. Одна из трёх не дала
|
|
14
|
+
коммита вовсе: правка тел уже заведённых задач веткой не бывает и задачей под ветку тоже.
|
|
15
|
+
- **Задача заводится командой, а не вызовами подряд.** Доска показывает те issue, чью метку
|
|
16
|
+
знает, и задача без метки списка в очереди работ не видна: со стороны это выглядит так же, как
|
|
17
|
+
незаведённая. Команда заведения ставит всё разом — issue, номер в его заголовке, метку списка,
|
|
18
|
+
исполнителя, — и печатает готовую строку заведения ветки. Замеченный по ходу дефект проходит
|
|
19
|
+
тот же путь.
|
|
20
|
+
- **Ветка заводится вторым вызовом, а не тем же.** Гард главной ветки отклоняет составную
|
|
21
|
+
«создать ветку и сразу коммитить» целиком: ветки в момент разбора ещё нет.
|
|
22
|
+
- **Сторона конфликта бывает удалением, и «сохранить обе стороны» заводит второе объявление.**
|
|
23
|
+
Главная ветка снимает объявление, потому что символ переехал, — в конфликте это выглядит как
|
|
24
|
+
сторона, которая ничего не дописала. Разбирается чтением версии главной ветки целиком, а не по
|
|
25
|
+
хунку, и сверяется проверкой повторов: обе копии сами по себе исправны, сборка и линт зелёные.
|
|
26
|
+
- **Учётная запись для пуша и автор MR выбираются отдельно.** Если пушить пришлось из-под другой
|
|
27
|
+
записи, на следующий вызов это не переносится: MR открывают токеном учётной записи машинной
|
|
28
|
+
работы, и от того, чьей записью он открыт, зависит, кого можно назначить ревьювером. Однажды
|
|
29
|
+
смена записи ради пуша утекла в публикацию — PR вышел от владельца.
|
|
30
|
+
- **Невалидный файл конвейера виден отказом сразу после пуша, а не упавшим заданием.** Конвейер
|
|
31
|
+
на такой файл не заводится вовсе: в списке стоит запись об ошибке разбора, а внутри нет ни
|
|
32
|
+
задания, ни лога. Поэтому список конвейеров ветки смотрится тем же движением, что и пуш —
|
|
33
|
+
`glab ci list --branch <ветка>`, — а сам файл до пуша судит проверка `.gitlab-ci.yml` в
|
|
34
|
+
проекте.
|
|
35
|
+
- **`online` у раннера на своей машине означает запущенный процесс, а не работающий
|
|
36
|
+
конвейер.** Две стороны сходятся отдельно: `tags` у заданий и теги самого раннера. Пока
|
|
37
|
+
пересечения нет, раннер стоит `online` и не берёт ничего, а задания ждут общего раннера — по
|
|
38
|
+
состоянию это выглядит настроенным. Владельцу называют выполненное задание с его номером, а
|
|
39
|
+
не строку состояния.
|
|
40
|
+
- **Вход в реестр образов из раннера, запущенного службой, отказывает молча.** Служба идёт без
|
|
41
|
+
сеанса пользователя, а клиент реестра уходит в системный помощник хранения ключей и получает
|
|
42
|
+
отказ во взаимодействии — задание падает до сборки. Свой каталог настроек с пустым помощником
|
|
43
|
+
не спасает: клиент переписывает пустое значение обратно сам. Готовые команды — паттерн
|
|
44
|
+
`git-workflow-docker`.
|
|
45
|
+
- **Новое рабочее дерево получает только то, что лежит в индексе.** `git worktree add`
|
|
46
|
+
разворачивает коммит, а настройки, ключи, локальные разрешения и зависимости в коммит не
|
|
47
|
+
входят: свежее дерево выглядит готовым и упирается в нехватку не сразу, а на первом гарде,
|
|
48
|
+
которому нужен ключ. Что именно переносится руками, названо списком в компаньоне правила, и
|
|
49
|
+
список пополняется тем же движением, которым заводится новый файл вне индекса.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Документация проекта — холодная часть
|
|
2
|
+
|
|
3
|
+
Ловушки: грабли, на которые уже наступали. Грузится не вместе с правилом, а по
|
|
4
|
+
требованию — при обычном решении она не нужна.
|
|
5
|
+
|
|
6
|
+
Правило — `spec-driven`; статьи, которыми держится закон, стоят там.
|
|
7
|
+
|
|
8
|
+
## Ловушки
|
|
9
|
+
|
|
10
|
+
- **`tasks.md` в спеке не заводить.** Шаги — артефакт сессии, им место в ветке или в описании
|
|
11
|
+
PR. Как только в директории появляются «шаги», спек снова становится планом и умирает после
|
|
12
|
+
мержа.
|
|
13
|
+
- **Спек описывает установившееся, а не предстоящее.** Единственное место, где он говорит о
|
|
14
|
+
будущем, — `proposed/<фича>/`. После выкатки его текст вливается в спек домена, директория
|
|
15
|
+
удаляется, идентификаторы сценариев не меняются.
|
|
16
|
+
- **Семантику полей не сверяет ничто.** Проверка знает имена процедур, коды отказа и связь
|
|
17
|
+
сценариев с тестами; что означает пустое поле — не знает. Правка `.proto` поэтому тянет
|
|
18
|
+
спеки всех доменов, чьи процедуры она задела, в той же ветке.
|
|
19
|
+
- **Живость символа считается совпадением имени по всему дереву, а не вызовом.** Символу
|
|
20
|
+
хватает второго упоминания где угодно — в чужом поле с тем же именем, в атрибуте разметки.
|
|
21
|
+
Место, где правило исполняется на самом деле, подтверждается только чтением кода.
|
|
22
|
+
- **Якорь в `tools/*.mjs` сверяется почти ничем:** живость считается только для `.ts`, а
|
|
23
|
+
исходники обходятся по `apps`, `libs` и `prisma`. Правило, привязанное к проверке, поэтому
|
|
24
|
+
читается вместе с её телом.
|
|
25
|
+
- **Зелёная проверка не значит, что структура верна.** Спутники с привязкой сначала лежали
|
|
26
|
+
рядом с законами, и проверка была зелёной именно потому, что структура совпадала с тем,
|
|
27
|
+
чего проверка сама и ждала.
|
|
28
|
+
- **Конфликт мержа в спеке разрешается сохранением обеих сторон, а не выбором одной.** Две
|
|
29
|
+
ветки дописывают в конец одних и тех же списков — сценариев, правил, строк привязки, — и
|
|
30
|
+
обе стороны верны: конфликт здесь не спор, а две дописи в одно место. Номера сценариев при
|
|
31
|
+
разрешении не пересчитываются: идентификатор — ключ связи с тестами, и сдвиг номеров рвёт
|
|
32
|
+
сверку у соседей, которых правка не касалась. Порядок сохранённых сторон держится
|
|
33
|
+
одинаковым в `spec.md`, `scenarios.md` и `implementation.md`: иначе правило, его сценарий и
|
|
34
|
+
его привязка перестают находиться друг по другу. После разрешения гоняется
|
|
35
|
+
`npm run check:specs` — конфликт в спеке кода не задевает, и ни сборка, ни линтеры его не
|
|
36
|
+
увидят.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Оформление — холодная часть
|
|
2
|
+
|
|
3
|
+
Ловушки: грабли, на которые уже наступали. Грузится не вместе с правилом, а по
|
|
4
|
+
требованию — при обычном решении она не нужна.
|
|
5
|
+
|
|
6
|
+
Правило — `styling-bem`; статьи, которыми держится закон, стоят там.
|
|
7
|
+
|
|
8
|
+
## Ловушки
|
|
9
|
+
|
|
10
|
+
- **`rtElem` без предка с `rtBlock` роняет отрисовку в рантайме** — сборка и линт молчат.
|
|
11
|
+
- **Спроецированный узел блока-предка не имеет.** `rtElem` берёт имя блока инъекцией от
|
|
12
|
+
ближайшего предка **по месту объявления шаблона**, а не по месту вставки: элемент, который
|
|
13
|
+
экран объявляет у себя и отдаёт в проекцию чужого компонента, ищет `rtBlock` в своём шаблоне
|
|
14
|
+
и не находит. Отрисовка падает в рантайме, сборка и линт зелёные. Класс на такой узел
|
|
15
|
+
вешается правилом по селектору кита в общем слое раскладки, а не директивой.
|
|
16
|
+
- **Элемент с `backdrop-filter` или своим `z-index` замыкает потомков в свой слой.** Липкая
|
|
17
|
+
шапка с размытием — самый частый случай: номер слоя у того, что лежит внутри неё,
|
|
18
|
+
сравнивается не с соседями по странице, а только с соседями внутри шапки, и нижняя панель
|
|
19
|
+
накрывает открытую шторку вместе с её кнопкой. Проверяется это `elementFromPoint` в центре
|
|
20
|
+
кнопки: сборка, линт и скриншот показывают тут целую страницу.
|
|
21
|
+
- **До узла, вынесенного к `<body>`, стили компонента не достают.** Превью и заглушку переноса
|
|
22
|
+
кладёт туда библиотека, а правила компонента заскоуплены атрибутом: файл выглядит рабочим и
|
|
23
|
+
не красит ничего. Такие правила объявляются в общем слое приложения. Ни сборка, ни линт, ни
|
|
24
|
+
проверка «класс без правила» этого не видят: класса такого в шаблоне нет вовсе, и пролежать
|
|
25
|
+
это может несколько задач подряд.
|
|
26
|
+
- **Имя токена не сверяется ничем.** Ссылка на несуществующий токен собирается, проходит
|
|
27
|
+
stylelint и проверку класса без правила, а свойство молча берёт наследованное значение:
|
|
28
|
+
правило выглядит написанным и не красит ничего. Ловится это только замером в браузере, а
|
|
29
|
+
имена берутся из объявлений кита, а не по догадке о том, как токен должен был бы называться.
|
|
30
|
+
- **`rtBlock` на `<ng-container>` класса не ставит вовсе:** узел это комментарий, и имя блока
|
|
31
|
+
он только объявляет потомкам. Класс блока экрана вешает хост через `host: { class: … }`.
|
|
32
|
+
- **`justify-content: center` во flex-контейнере с `overflow-x` уводит первые элементы за
|
|
33
|
+
нулевой скролл** — доскроллить до них невозможно. В прокручиваемых лентах —
|
|
34
|
+
`justify-content: safe center`.
|
|
35
|
+
- **`scrollbar-gutter: stable` на корне не заводить:** резерв под полосу прокрутки сужает
|
|
36
|
+
содержащий блок для `position: fixed`, и попап, выровненный по правому краю, встаёт на
|
|
37
|
+
ширину резерва левее своей кнопки.
|
|
38
|
+
- **`& + :host` невалиден:** изнутри компонента до соседнего хоста не дотянуться. Разделитель
|
|
39
|
+
между повторяющимися хостами — `:host(:not(:first-of-type))`.
|
|
40
|
+
- **`[attr.aria-disabled]` визуального состояния не даёт:** браузер стилизует `:disabled`, но
|
|
41
|
+
атрибуты `aria-*` — нет. К каждому `aria-disabled` заводится правило `[aria-disabled='true']`.
|
|
42
|
+
- **Гарнитуру с `body` элементы формы не наследуют:** браузер задаёт `button`, `input`,
|
|
43
|
+
`select` и `textarea` свой шрифт. Наследование включено глобально — сбрасывать его нельзя.
|
|
44
|
+
- **Комментарии-выключатели stylelint не ставятся.** Селекторы объединяются вложенностью.
|
|
45
|
+
- **При переносе стилей новых объявлений не появляется** — только перемещение существующих.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Ведение работы — холодная часть
|
|
2
|
+
|
|
3
|
+
Ловушки и поведение по разборам происшествий. Грузится не вместе с правилом, а по требованию:
|
|
4
|
+
при обычном решении она не нужна — она нужна тому, кто разбирает промах или спорит с гардом.
|
|
5
|
+
|
|
6
|
+
Правило — `task-flow`; статьи, которыми держится закон, стоят там.
|
|
7
|
+
|
|
8
|
+
## Ловушки
|
|
9
|
+
|
|
10
|
+
- **Папка называется именем ветки, один в один.** Хук запуска ищет её по
|
|
11
|
+
`git branch --show-current`, и папка, названная иначе, не находится ничем: работа идёт с
|
|
12
|
+
пустым контекстом, а владельца просят пересказать то, что уже записано.
|
|
13
|
+
- **Разбор просьбы задним числом не переписывается.** Пересказ незаметно подгоняется под уже
|
|
14
|
+
сделанное, и сверять результат становится не с чем. Решение, изменённое по ходу, дописывается
|
|
15
|
+
в ход работы, а не правится в разборе.
|
|
16
|
+
- **Договорённость о продукте не кладётся в папку задачи.** Папка умирает с мержем, а
|
|
17
|
+
договорённость обязана его пережить: её сценарии получают номера в общей нумерации домена,
|
|
18
|
+
и на них ссылаются заголовки тестов. Обратное тоже верно — ход работы не кладётся в
|
|
19
|
+
`proposed/`: спек, в котором завелись шаги, снова становится планом и умирает после мержа.
|
|
20
|
+
- **У меню нет строки «вопрос не тот».** Меню годится для выбора значения из закрытого набора;
|
|
21
|
+
пока постановка вопроса не подтверждена, отвергнуть её владельцу нечем — он выбирает из
|
|
22
|
+
вариантов, выведенных из неверной посылки. Настройки владельца, требующие меню, требование
|
|
23
|
+
не снимают: тогда к каждому вопросу добавляется свободный вариант, и он же — единственное
|
|
24
|
+
место, где вопрос отвергается целиком. Три вопроса ушли одним меню, у одного постановка была
|
|
25
|
+
ложной, и сказать «вопрос не тот» было нечем. Выбор слова, имени и термина узким вопросом не
|
|
26
|
+
является никогда.
|
|
27
|
+
- **Субагент вопросов владельцу не задаёт.** Ни роли, ни конвейер до него не достучатся —
|
|
28
|
+
они возвращают текст главному агенту. Поэтому разбор ведёт главный агент, а роли стоят по
|
|
29
|
+
обе стороны от него.
|
|
30
|
+
- **Если дефект чинится правкой одного общего числа, спроси владельца, тем ли способом ты его
|
|
31
|
+
чинишь.** Замер показывает, что дефект ушёл, — но не то, что причину вылечили. В одной
|
|
32
|
+
задаче так ушли две правки подряд: сначала подняли общее число у соседнего узла, потом
|
|
33
|
+
перенесли узел в другое место разметки. Обе владелец отверг, а нужный способ назвал сам.
|
|
34
|
+
Спрашивают до правки, а не показывают замер после.
|
|
35
|
+
- **Путь, предложенный человеку, судится числом его шагов и тем, чем ему для этого надо
|
|
36
|
+
владеть.** Со стороны кода вариант выглядит дешёвым — «меньше путей», «строку запуска не
|
|
37
|
+
трогаем», — а человеку он стоит захода на сервер. Однажды рекомендуемым вариантом так стояла
|
|
38
|
+
выдача токена, за которой владельцу надо было идти по ssh в работающий контейнер; отбивал
|
|
39
|
+
этот вариант он сам. Цена называется со стороны того, кто пойдёт: сколько шагов и что ему для
|
|
40
|
+
них нужно. Пересказ действующего порядка без такой оценки владелец читает как одобрение.
|
|
41
|
+
- **Эпик по теме читается до того, как решается раскладка.** Замысел эпика держит решения,
|
|
42
|
+
которые пережили десяток задач, и разведка по коду их не находит: снятое решение следа в
|
|
43
|
+
дереве не оставляет. Домен, заведённый генератором и снесённый через полчаса, стоял в замысле
|
|
44
|
+
прямым запретом — но замысел открыли уже после того, как он был заведён во второй раз.
|
|
45
|
+
- **Работа, которая разбирает чужую папку задачи, разбирает и свою — одним коммитом.** Свою
|
|
46
|
+
папку она заводит наравне со всеми: исключения из этого требования нет. Круг, которым
|
|
47
|
+
исключение оправдывали, закрывается не отказом от папки, а порядком разбора: последний
|
|
48
|
+
коммит снимает обе, и после работы неубранного не остаётся. Однажды такой разбор оставил
|
|
49
|
+
свою папку, и на неё пришлось заводить третью задачу — лечится это порядком, а не правом
|
|
50
|
+
работать без замысла на диске. Как разобрать две папки — паттерн `task-flow-archive`.
|
|
51
|
+
- **Слово для нового понятия берётся из `docs/GLOSSARY.md` или заводится там же.** Третий файл
|
|
52
|
+
папки задачи называется `progress.md`, а не `journal.md`, ровно поэтому: журнал в этом
|
|
53
|
+
дереве один, и он другой.
|
|
54
|
+
- **Черновик папки задачи называется тем же коротким именем, что и будущая ветка.** Команда
|
|
55
|
+
заведения ищет черновик по нему и, не найдя, молча собирает папку с образца: работа при этом
|
|
56
|
+
идёт дальше, а разбор просьбы остаётся лежать в брошенном каталоге, и следующий заход
|
|
57
|
+
расспрашивает владельца заново. Имя черновику дают словами просьбы, а ветке — терминологией
|
|
58
|
+
договорённости; те же слова, да не те же.
|
|
59
|
+
|
|
60
|
+
## Поведение исполнителя — по разборам происшествий
|
|
61
|
+
|
|
62
|
+
Переезжает из правила следующей задачей эпика.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Проверка — холодная часть
|
|
2
|
+
|
|
3
|
+
Ловушки: грабли, на которые уже наступали. Грузится не вместе с правилом, а по
|
|
4
|
+
требованию — при обычном решении она не нужна.
|
|
5
|
+
|
|
6
|
+
Правило — `testing`; статьи, которыми держится закон, стоят там.
|
|
7
|
+
|
|
8
|
+
## Ловушки
|
|
9
|
+
|
|
10
|
+
- **Зелёный `nx test <проект>` не значит, что хоть один файл исполнялся.** Либа без своего
|
|
11
|
+
`vitest.config.mts` не запускает ничего — так тесты домена броней не запускались ни разу.
|
|
12
|
+
Либа с конфигом, но без единого `*.spec.ts`, проходит зелёной из-за `passWithNoTests: true`,
|
|
13
|
+
который обычно стоит в каждом конфиге дерева, и на глаз эти два случая неотличимы: в обоих
|
|
14
|
+
прогон успешен. Доля либ без единого теста меряется пересчётом ниже — в дереве, где его
|
|
15
|
+
завели впервые, она вышла почти в две трети. Перед правкой в
|
|
16
|
+
незнакомой либе проверяется, есть ли в ней хоть один `*.spec.ts`; если нет — первый
|
|
17
|
+
заводится этой же правкой, а не откладывается: откладывать здесь не с чего, долг уже
|
|
18
|
+
накоплен. Пересчёт: `for d in $(find libs -name vitest.config.mts -exec dirname {} \;); do
|
|
19
|
+
[ -z "$(find "$d" -name '*.spec.ts')" ] && echo "$d"; done | wc -l`.
|
|
20
|
+
- **Зелёная сводка покрытия не значит, что тесты проходят.** Сверка читает заголовки тестов и
|
|
21
|
+
сопоставляет их со сценариями спека; исполняется ли тест и чем он кончается — она не знает
|
|
22
|
+
вовсе, и падающий тест значится в ней покрытием. Три сценария одной панели падали и до правки
|
|
23
|
+
экрана, а нашлось это только прогоном. Перед правкой экрана его сквозные тесты гоняются один
|
|
24
|
+
раз до первой строки кода: иначе чужое падение читается как своя регрессия, а своё — как
|
|
25
|
+
чужое.
|
|
26
|
+
- **«Executable doesn't exist» — состояние машины, а не дефект правки.** Установлен только
|
|
27
|
+
chromium, `firefox` и `webkit` падают всегда: гонять `--project=chromium`, узкий экран —
|
|
28
|
+
`--project=mobile-chrome`. Та же ошибка приходит после смены версии Playwright: браузер
|
|
29
|
+
ставится под конкретную версию, и после подъёма нужен повторный
|
|
30
|
+
`npx playwright install chromium`. Девять тестов так и упали, и это выглядело регрессией
|
|
31
|
+
обновления.
|
|
32
|
+
- **Первому прогону сразу после установки браузера верить нельзя.** Два падения сквозного
|
|
33
|
+
набора не повторились ни при отдельном прогоне тех же тестов, ни при втором полном. Такой
|
|
34
|
+
прогон повторяют, а выводы делают по второму.
|
|
35
|
+
- Сквозная спека, которой нужен вход, без учётных данных в окружении пропускается молча — в
|
|
36
|
+
отчёте она значится `skipped`, и прогон выглядит успешным. Имена переменных — при дереве.
|
|
37
|
+
- **Справочник флоу вторых сценариев не заводит.** В `docs/E2E_<ДОМЕН>_FLOWS.md` кладут то,
|
|
38
|
+
чего в спеке домена нет и быть не должно: `qa-dataid` элементов, состояния разметки, ловушки
|
|
39
|
+
стенда. Обещанное поведение остаётся сценарием в `scenarios.md`: если списать его во второе
|
|
40
|
+
место, копии разойдутся молча — `npm run check:specs` этого не увидит.
|
|
41
|
+
- `npx nx serve` проверкой не считается: это шаг из правила `browser-verification`, а не тест.
|
|
42
|
+
- **Кадр, зависящий от загрузки машины, проверяет машину, а не вёрстку.** Ожидание отсчётом
|
|
43
|
+
времени этим и кончается: на свободной машине набор зелен целиком, на занятой падает, и какой
|
|
44
|
+
именно кадр не успел — дело случая. Лечится ожиданием события, а не удлинением отсчёта: шрифты
|
|
45
|
+
подняты, картинки нарисованы, движение остановлено, положение узла не менялось два кадра
|
|
46
|
+
подряд. Пока ожидание идёт по времени, «проверено снимками» означает «машина была свободна», и
|
|
47
|
+
перезапуск, давший зелёное, этого не отменяет, а прячет. Восемь кадров расходились с эталоном
|
|
48
|
+
на 0,15–0,74 % в задании конвейера и проходили на той же машине вне его.
|
|
49
|
+
- **Стенд, поднятый предыдущим шагом, останавливается перед съёмкой.** Оставленный работать, он
|
|
50
|
+
соревнуется за машину с тем, что снимают, и делает исход прогона зависящим от того, чем занят
|
|
51
|
+
сосед. Нагрузка, которую задание создаёт себе само — соседняя витрина, только что законченная
|
|
52
|
+
сборка, — ничем не отличается от чужой.
|
|
53
|
+
- **Свой стенд снимается перед тем, как звать набор.** Прогон переиспользует поднятое на его
|
|
54
|
+
портах, и стенд, оставленный для замера, отдаёт ему чужую сборку с чужими данными. Красное при
|
|
55
|
+
этом приходит не строкой про занятый порт, а десятком спек про экраны — то есть выглядит
|
|
56
|
+
дефектом правки: за один заход так покраснели сначала шесть новых тестов, потом гейт пуша, и
|
|
57
|
+
оба раза причиной был свой же стенд. Разобранный занятый порт эту сторону не закрывает: он про
|
|
58
|
+
чужой стенд, а этот — про свой.
|
|
59
|
+
- **Разбор упавшего кадра начинается с чисел, а не с картинки расхождения.** Доля площади
|
|
60
|
+
говорит, сколько разошлось, и молчит о том, что именно: сдвиг всего кадра на пиксель,
|
|
61
|
+
переставленные строки и рябь на сглаженных уголках выглядят на картинке одинаково — «стало
|
|
62
|
+
другим». Читаются координаты разошедшихся точек и величина расхождения по каналу: сдвинутые
|
|
63
|
+
границы блоков — это раскладка, разошедшийся текст при неподвижных границах — это данные,
|
|
64
|
+
единица-две по каналу на кривых краях — это цвет. Три расхождения одного набора разобрались
|
|
65
|
+
ровно так, и ни одно из трёх не оказалось дефектом экрана.
|
|
66
|
+
- **Ожидаемое значение теста не берётся из кода, который тест проверяет.** Вывезенное из
|
|
67
|
+
проверяемой либы, оно делает тест зелёным при любом значении: «колесо показывает пять строк»
|
|
68
|
+
сходится и тогда, когда строк стало три. Ожидаемое пишется числом в самой спеке рядом с
|
|
69
|
+
проверкой, а общий модуль сквозных спек держит приёмы — открыть, дождаться, снять со
|
|
70
|
+
страницы, — но не то, что от страницы ожидается.
|