@rt-tools/agent-kit 0.8.3 → 0.9.1
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/README.md +21 -0
- package/assets/checks/board.github.mjs +48 -1
- package/assets/checks/check-board.github.mjs +84 -1
- package/assets/checks/check-lib-layers.mjs +13 -524
- package/assets/checks/check-specs.mjs +11 -782
- package/assets/checks/check-styles.mjs +185 -15
- package/assets/checks/lib-boundaries.mjs +143 -0
- package/assets/checks/lib-common.mjs +149 -0
- package/assets/checks/lib-domains.mjs +205 -0
- package/assets/checks/lib-manifests.mjs +60 -0
- package/assets/checks/lib-reexports.mjs +101 -0
- package/assets/checks/rt-kit-checks.config.mjs +17 -0
- package/assets/checks/spec-anchors.mjs +297 -0
- package/assets/checks/spec-common.mjs +222 -0
- package/assets/checks/spec-contract.mjs +152 -0
- package/assets/checks/spec-scenarios.mjs +201 -0
- package/assets/defaults/project.sh +8 -0
- package/assets/hooks/git-guard-push-tests.sh +8 -4
- package/assets/hooks/skill-gate.sh +1 -1
- package/assets/hooks/sql-guard-parse.sh +187 -0
- package/assets/hooks/sql-guard-request.sh +117 -0
- package/assets/hooks/sql-guard-target.sh +134 -0
- package/assets/hooks/sql-guard-write.sh +212 -0
- package/assets/hooks/sql-guard.sh +26 -596
- package/assets/hooks/waiting-turn-guard.sh +42 -13
- package/assets/laws/delivery.md +7 -0
- package/assets/laws/work-conduct.md +9 -0
- package/assets/patterns/admin-lists-screen.md +25 -14
- package/assets/patterns/admin-nav-item.md +1 -1
- package/assets/patterns/component-structure-new.md +1 -1
- package/assets/patterns/entity-aside.md +4 -2
- package/assets/patterns/observability-record.md +9 -0
- package/assets/patterns/shared-code-new.md +2 -2
- package/assets/patterns/task-flow-close.md +7 -1
- package/assets/rules/git-workflow.azure.md +7 -0
- package/assets/rules/git-workflow.github.md +41 -0
- package/assets/rules/git-workflow.gitlab.md +7 -0
- package/assets/rules/lib-layers.md +4 -0
- package/assets/rules/lists.md +10 -10
- package/assets/rules/shared-code.md +1 -1
- package/assets/rules/task-flow.md +47 -7
- package/assets/rules/testing.md +31 -0
- package/assets/rules/typescript-conventions.md +7 -0
- package/assets/skills/agent-kit.md +36 -0
- package/assets/templates/proposal.md +21 -0
- package/bin/agent-kit.d.ts.map +1 -1
- package/bin/agent-kit.js +117 -87
- package/bin/agent-kit.js.map +1 -1
- package/index.d.ts +1 -0
- package/index.d.ts.map +1 -1
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/lib/argv.d.ts.map +1 -1
- package/lib/argv.js +6 -4
- package/lib/argv.js.map +1 -1
- package/lib/assets.d.ts.map +1 -1
- package/lib/assets.js +2 -1
- package/lib/assets.js.map +1 -1
- package/lib/cargo.d.ts +20 -0
- package/lib/cargo.d.ts.map +1 -1
- package/lib/cargo.js.map +1 -1
- package/lib/cascade.d.ts +55 -0
- package/lib/cascade.d.ts.map +1 -0
- package/lib/cascade.js +131 -0
- package/lib/cascade.js.map +1 -0
- package/lib/catalog.d.ts +0 -75
- package/lib/catalog.d.ts.map +1 -1
- package/lib/catalog.js +44 -127
- package/lib/catalog.js.map +1 -1
- package/lib/commands.d.ts.map +1 -1
- package/lib/commands.js +152 -85
- package/lib/commands.js.map +1 -1
- package/lib/companion.d.ts.map +1 -1
- package/lib/companion.js +5 -5
- package/lib/companion.js.map +1 -1
- package/lib/config.d.ts +2 -0
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +7 -5
- package/lib/config.js.map +1 -1
- package/lib/enroll.d.ts +64 -0
- package/lib/enroll.d.ts.map +1 -0
- package/lib/enroll.js +170 -0
- package/lib/enroll.js.map +1 -0
- package/lib/freshness.d.ts.map +1 -1
- package/lib/freshness.js +31 -17
- package/lib/freshness.js.map +1 -1
- package/lib/hooks-map.d.ts +30 -0
- package/lib/hooks-map.d.ts.map +1 -1
- package/lib/hooks-map.js +80 -18
- package/lib/hooks-map.js.map +1 -1
- package/lib/integrity.d.ts +1 -2
- package/lib/integrity.d.ts.map +1 -1
- package/lib/integrity.js +0 -1
- package/lib/integrity.js.map +1 -1
- package/lib/observations.d.ts.map +1 -1
- package/lib/observations.js +25 -12
- package/lib/observations.js.map +1 -1
- package/lib/order.d.ts +10 -0
- package/lib/order.d.ts.map +1 -0
- package/lib/order.js +14 -0
- package/lib/order.js.map +1 -0
- package/lib/picker.d.ts.map +1 -1
- package/lib/picker.js +8 -2
- package/lib/picker.js.map +1 -1
- package/lib/plan.js +1 -1
- package/lib/plan.js.map +1 -1
- package/lib/proposals.d.ts.map +1 -1
- package/lib/proposals.js +25 -8
- package/lib/proposals.js.map +1 -1
- package/lib/sections.js +1 -1
- package/lib/sections.js.map +1 -1
- package/lib/ship.d.ts.map +1 -1
- package/lib/ship.js +9 -1
- package/lib/ship.js.map +1 -1
- package/lib/shipment.d.ts.map +1 -1
- package/lib/shipment.js +14 -10
- package/lib/shipment.js.map +1 -1
- package/lib/snapshot.d.ts.map +1 -1
- package/lib/snapshot.js +2 -1
- package/lib/snapshot.js.map +1 -1
- package/lib/stamp.js +1 -1
- package/lib/stamp.js.map +1 -1
- package/lib/sync.d.ts +12 -2
- package/lib/sync.d.ts.map +1 -1
- package/lib/sync.js +11 -10
- package/lib/sync.js.map +1 -1
- package/lib/vars.d.ts.map +1 -1
- package/lib/vars.js +2 -3
- package/lib/vars.js.map +1 -1
- package/package.json +1 -1
- package/rt-tools-agent-kit-0.9.1.tgz +0 -0
- package/rt-tools-agent-kit-0.8.3.tgz +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# rt-hook: Stop
|
|
3
|
-
# Гард ожидания: ход,
|
|
4
|
-
# действия по следующей задаче. Stop.
|
|
3
|
+
# Гард ожидания: ход, сообщающий владельцу о чужом шаге, не заканчивается, пока в нём не было ни
|
|
4
|
+
# одного действия по следующей задаче. Stop.
|
|
5
5
|
#
|
|
6
6
|
# Зачем именно так. Статья «ожидание прогона работой не занимают» держится памятью исполнителя, и
|
|
7
7
|
# держится плохо: образец сообщения владельцу кончается фразой о следующей задаче, а фраза
|
|
@@ -9,17 +9,22 @@
|
|
|
9
9
|
# ничем себя не выдаёт: ни правкой файла, ни командой, — и промах виден только владельцу, только
|
|
10
10
|
# по тому, что работа не двигается, и только когда он спросит прямо.
|
|
11
11
|
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
12
|
+
# Чужой шаг узнаётся по двум признакам, и оба берутся из хода, а не из сети. Первый — открытие
|
|
13
|
+
# PR: работа отдана, и дальше её двигает владелец. Второй — прочитанный красный прогон: чинить
|
|
14
|
+
# его надо, но ход, в котором его прочитали и не сделали ничего, — та же пустота. Само чтение
|
|
15
|
+
# прогона признаком не является: конец прогона читают и затем, чтобы пойти чинить; значит
|
|
16
|
+
# красный ответ, а он лежит в выводе команды — там же, в записи хода.
|
|
17
|
+
#
|
|
18
|
+
# Спросить хостинг было бы точнее, но сетевой вызов на завершении хода падает вместе со связью и
|
|
19
|
+
# отбивал бы работу вместо промаха.
|
|
16
20
|
#
|
|
17
21
|
# Что считается действием: заведение задачи, заведение ветки, перевод колонки очереди работ,
|
|
18
22
|
# заведение папки задачи. Набор открыт и пополняется правкой — полнота его открытый вопрос, а не
|
|
19
23
|
# обещание.
|
|
20
24
|
#
|
|
21
|
-
# Чего гард не судит. Ход, в котором
|
|
22
|
-
# от хода, которому нечего было делать. Это известная его граница.
|
|
25
|
+
# Чего гард не судит. Ход, в котором о чужом шаге не сказано ничего, — здесь он молчит: пустой
|
|
26
|
+
# ход неотличим от хода, которому нечего было делать. Это известная его граница. Зелёный прогон
|
|
27
|
+
# он тоже не судит: за ним идёт своя работа — уборка и снятие черновика, — а не чужой шаг.
|
|
23
28
|
#
|
|
24
29
|
# ОТКАЗ В ПОЛЬЗУ РАБОТЫ: при любой ошибке, нехватке `jq`, отсутствии записи хода и повторном
|
|
25
30
|
# заходе ход РАЗРЕШАЕТСЯ (exit 0). Сломанный гард не имеет права заклинить разговор.
|
|
@@ -42,15 +47,23 @@ transcript="$(printf '%s' "$input" | jq -r '.transcript_path // empty' 2>/dev/nu
|
|
|
42
47
|
# ничего.
|
|
43
48
|
opened_re='gh[^|;&]*pr[[:space:]]+create|api[^|;&]*-X[[:space:]]+POST[^|;&]*/pulls|glab[^|;&]*mr[[:space:]]+create|az[[:space:]]+repos[[:space:]]+pr[[:space:]]+create'
|
|
44
49
|
|
|
50
|
+
# Чтение прогона — второй случай чужого шага. Сам по себе он ничего не значит: конец прогона
|
|
51
|
+
# читают и затем, чтобы пойти чинить. Значит красный ответ, а он лежит не в команде, а в её
|
|
52
|
+
# выводе — там же, в записи хода, и сети для этого не нужно.
|
|
53
|
+
read_re='run[[:space:]]+(list|view|watch)|pipelines[[:space:]]+runs'
|
|
54
|
+
red_re='completed[[:space:]]+failure|"conclusion"[[:space:]]*:[[:space:]]*"failure"|conclusion:[[:space:]]*failure|completed with .failure.|run[[:space:]]+failed'
|
|
55
|
+
|
|
45
56
|
# Первое действие по следующей задаче. Заведение папки стоит здесь наравне с командами: работа
|
|
46
|
-
# по уже заведённому номеру начинается именно с неё.
|
|
57
|
+
# по уже заведённому номеру начинается именно с неё. Правка кода в ветке того же PR действием
|
|
58
|
+
# по следующей задаче не считается — она чинит прежнюю, а не двигает работу дальше.
|
|
47
59
|
moved_re='task:new|task:move|checkout[[:space:]]+-b|docs/tasks/'
|
|
48
60
|
|
|
49
61
|
# Ход — это всё, что записано после последнего настоящего ввода владельца. Ответ инструмента
|
|
50
62
|
# приходит той же ролью, поэтому строки с `tool_result` вводом не считаются.
|
|
51
63
|
#
|
|
52
64
|
# Хвост в 400 строк: запись хода растёт всю сессию, а судится только последний ход.
|
|
53
|
-
verdict="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r
|
|
65
|
+
verdict="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r \
|
|
66
|
+
--arg opened "$opened_re" --arg moved "$moved_re" --arg read "$read_re" --arg red "$red_re" '
|
|
54
67
|
def is_input:
|
|
55
68
|
.type == "user"
|
|
56
69
|
and (((.message.content // []) | if type == "array"
|
|
@@ -61,14 +74,30 @@ verdict="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r --arg opened "$opene
|
|
|
61
74
|
| (if $i == null then [] else .[$i:] end) as $turn
|
|
62
75
|
| [$turn[] | select(.type == "assistant") | (.message.content // [])[] | select(.type == "tool_use")] as $uses
|
|
63
76
|
| ($uses | map((.input.command // "")) | join("\n")) as $ran
|
|
77
|
+
# Ответ инструмента берётся текстом, а не в записи JSON: у той таб и кавычка уезжают
|
|
78
|
+
# экранированными, и образец по ним не попадает вовсе. Приходит он и строкой, и списком
|
|
79
|
+
# блоков — обе формы сводятся к одному тексту.
|
|
80
|
+
| ([$turn[] | select(.type == "user") | .message.content // [] | select(type == "array") | .[]
|
|
81
|
+
| select(.type == "tool_result") | .content
|
|
82
|
+
| if type == "string" then .
|
|
83
|
+
elif type == "array" then (map(if type == "object" then (.text // "") else tostring end) | join("\n"))
|
|
84
|
+
else tostring end] | join("\n")) as $out
|
|
64
85
|
| ($ran | test($opened; "i")) as $opened_pr
|
|
86
|
+
| (($ran | test($read; "i")) and ($out | test($red; "i"))) as $red_run
|
|
65
87
|
| ($ran | test($moved; "i")) as $went_on
|
|
66
|
-
| if $
|
|
88
|
+
| if $went_on then "pass"
|
|
89
|
+
elif $opened_pr then "owe:pr"
|
|
90
|
+
elif $red_run then "owe:run"
|
|
91
|
+
else "pass" end
|
|
67
92
|
' 2>/dev/null)"
|
|
68
93
|
|
|
69
|
-
|
|
94
|
+
case "$verdict" in
|
|
95
|
+
owe:pr) said="в этом ходе открыт PR" ;;
|
|
96
|
+
owe:run) said="в этом ходе прочитан красный прогон" ;;
|
|
97
|
+
*) exit 0 ;;
|
|
98
|
+
esac
|
|
70
99
|
|
|
71
|
-
reason="BLOCKED by waiting-turn-guard:
|
|
100
|
+
reason="BLOCKED by waiting-turn-guard: ${said}, а действия по следующей задаче в нём нет ни одного. Ожидание чужого шага заходом не занимают: прогон, разбор и слияние идут на стороне и быстрее от взгляда не становятся.
|
|
72
101
|
|
|
73
102
|
Сказать «беру следующую задачу» — не то же самое, что взять её: фраза живёт до конца хода, а работа не двигается, и заметить это может только владелец.
|
|
74
103
|
|
package/assets/laws/delivery.md
CHANGED
|
@@ -109,6 +109,13 @@
|
|
|
109
109
|
человека и только про названный PR; молчание разрешением не бывает, а слово, сказанное об
|
|
110
110
|
одном PR, на следующий не переносится. Иначе разбор проходит тот, кого разбирают, и
|
|
111
111
|
очередь PR выглядит разобранной, не будучи ею.
|
|
112
|
+
- **Требование, стоящее перед необратимым шагом, стоит там, где этот шаг совершают.** Гард на
|
|
113
|
+
машине исполнителя судит его команды и молчит о том же действии, совершённом кнопкой у
|
|
114
|
+
хостинга: обход выходит не намеренным, а незамеченным — нажавший не знает, что чего-то не
|
|
115
|
+
хватало. Требование либо переносится туда, где нажимают, либо объявляется тому, кто нажимает,
|
|
116
|
+
до нажатия. Иначе оно держится не собой, а тем, что необратимый шаг каждый раз делает тот же
|
|
117
|
+
человек: папка закрытой задачи так и уехала в главную ветку вместе со своей договорённостью, и
|
|
118
|
+
вынимать её пришлось отдельной задачей.
|
|
112
119
|
- **Слияние в главную ветку ещё не означает, что правка доехала.** Отказ выкатки не трогает ни
|
|
113
120
|
задачу, ни очередь работ, поэтому расхождение главной ветки с тем, что работает, обязано быть
|
|
114
121
|
видно там, где очередь читают. Иначе следующие работы вливаются поверх поломки, которую не
|
|
@@ -13,6 +13,15 @@
|
|
|
13
13
|
- **Пробел закрывается вопросом владельцу, а не догадкой.** Догадка неотличима от знания:
|
|
14
14
|
она попадает в работу молча и обнаруживается только при приёмке, когда переделывать дороже
|
|
15
15
|
всего.
|
|
16
|
+
- **Работа, объявленная сборкой по образцу, начинается с чтения самого образца.** Пересказ
|
|
17
|
+
образца образцом не является: по нему собирается понимание того, кто пересказывал, и
|
|
18
|
+
расхождение всплывает на приёмке целой работой, а не строкой. Читается та часть образца,
|
|
19
|
+
которую работа повторяет, и читается целиком — об устройстве чужого дерева по одному его
|
|
20
|
+
файлу не судят.
|
|
21
|
+
- **Образец, названный однажды, доступен каждому заходу работы.** Где он лежит, записано
|
|
22
|
+
там, где заход найдёт это без владельца. Иначе второй заход собирает по памяти первого,
|
|
23
|
+
третий — по пересказу второго, и к четвёртому от образца не остаётся ничего, кроме слова
|
|
24
|
+
«образец».
|
|
16
25
|
- **Вопрос, у которого есть очевидный ответ, работу не останавливает.** Исполнитель называет
|
|
17
26
|
допущение, идёт дальше и записывает его туда же, где идёт работа. Останавливает только тот
|
|
18
27
|
пробел, при котором любая догадка делает работу опасной или бесполезной. Вопрос, заданный
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: admin-lists-screen
|
|
3
3
|
kind: pattern
|
|
4
4
|
rule: lists
|
|
5
|
-
description: Паттерн правила lists. Брать при сборке или правке списочного экрана админки — готовый порядок блоков, разметка <префикс>-table, клик по строке, меню строки с
|
|
5
|
+
description: Паттерн правила lists. Брать при сборке или правке списочного экрана админки — готовый порядок блоков, разметка <префикс>-table, клик по строке, меню строки с колонкой действий и предикатом строки, сортируемый заголовок, слоты тулбара, тост отказа.
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Собрать списочный экран
|
|
@@ -25,7 +25,7 @@ description: Паттерн правила lists. Брать при сборке
|
|
|
25
25
|
<header rtElem="header">
|
|
26
26
|
<!-- div rtElem="header-main" c h1 rtElem="title" + p rtElem="hint" -->
|
|
27
27
|
<<префикс>-toolbar>
|
|
28
|
-
<!--
|
|
28
|
+
<!-- <префикс>ToolbarLeft / <префикс>ToolbarRight -->
|
|
29
29
|
<div rtElem="scroll">
|
|
30
30
|
<!-- overflow-x: auto -->
|
|
31
31
|
<<префикс>-table rtElem="table">
|
|
@@ -42,9 +42,9 @@ description: Паттерн правила lists. Брать при сборке
|
|
|
42
42
|
## Таблица
|
|
43
43
|
|
|
44
44
|
```html
|
|
45
|
-
<<префикс>-table #rowsTable="
|
|
46
|
-
| transloco" [tableId]="tableId" [dataSource]="rows()" [columnsConfig]="columnsConfig()"
|
|
47
|
-
[loading]="loading()">
|
|
45
|
+
<<префикс>-table #rowsTable="<префикс>Table" rtElem="table" clickable [ariaLabel]="'recordsTableAria' | transloco"
|
|
46
|
+
[emptyMessage]="'recordsEmpty' | transloco" [tableId]="tableId" [dataSource]="rows()" [columnsConfig]="columnsConfig()"
|
|
47
|
+
[rowHasActions]="hasRowActions" [loading]="loading()">
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
- `tableId` — ключ, под которым хранится выбор столбцов; он же уходит в асайд настроек.
|
|
@@ -57,8 +57,8 @@ description: Паттерн правила lists. Брать при сборке
|
|
|
57
57
|
<tr
|
|
58
58
|
*cdkRowDef="let row; columns: rowsTable.displayedColumns()"
|
|
59
59
|
cdk-row
|
|
60
|
-
qa-dataid="
|
|
61
|
-
|
|
60
|
+
qa-dataid="records-row"
|
|
61
|
+
<префикс>TableRow
|
|
62
62
|
(activated)="openAside(row)"></tr>
|
|
63
63
|
```
|
|
64
64
|
|
|
@@ -67,13 +67,24 @@ description: Паттерн правила lists. Брать при сборке
|
|
|
67
67
|
## Меню строки
|
|
68
68
|
|
|
69
69
|
```html
|
|
70
|
-
|
|
70
|
+
<<префикс>-table … [showRowActions]="true" [rowHasActions]="hasRowActions" [loading]="loading()">
|
|
71
|
+
<ng-template <префикс>TableRowActions let-row [<префикс>TableRowActionsRowType]="rows()"></ng-template>
|
|
71
72
|
```
|
|
72
73
|
|
|
73
74
|
```typescript
|
|
74
|
-
protected readonly hasRowActions
|
|
75
|
+
protected readonly hasRowActions: I<Префикс>Table.RowActionsPredicate<IRecord.Row> = recordRowHasActions;
|
|
75
76
|
```
|
|
76
77
|
|
|
78
|
+
Входа два, и они не заменяют друг друга. `showRowActions` заводит саму колонку действий — по
|
|
79
|
+
умолчанию он опущен, и без него таблица рисует одни объявленные столбцы: ни шаблон действий, ни
|
|
80
|
+
предикат колонки не добавляют. `rowHasActions` решает, показывать ли кнопку у конкретной
|
|
81
|
+
строки, и колонки не заводит вовсе. Экран ролей так и вышел с четырьмя столбцами и
|
|
82
|
+
недостижимыми действиями: сборка, линт и юниты на правилах строки при этом зелёные, а нашёл
|
|
83
|
+
это прогон сквозной спеки.
|
|
84
|
+
|
|
85
|
+
Ячейка действий скрыта, пока указатель не на строке, — сквозная спека наводит на строку до
|
|
86
|
+
нажатия.
|
|
87
|
+
|
|
77
88
|
Доступность действия лежит полем строки (`canConfirm`, `canReject`), а не вызовом метода
|
|
78
89
|
компонента. Действие, которого записи нельзя сделать, из меню убирается целиком. Необратимое
|
|
79
90
|
несёт `danger`, `confirmTitle` и `confirmMessage` с последствием — не «Вы уверены?», а что
|
|
@@ -82,7 +93,7 @@ protected readonly hasRowActions = (row: IBooking.State): boolean => row.canConf
|
|
|
82
93
|
## Сортируемый заголовок
|
|
83
94
|
|
|
84
95
|
```html
|
|
85
|
-
<th *cdkHeaderCellDef cdk-header-cell
|
|
96
|
+
<th *cdkHeaderCellDef cdk-header-cell <префикс>SortHeader="createdAt">{{ 'recordsCreatedAt' | transloco }}</th>
|
|
86
97
|
```
|
|
87
98
|
|
|
88
99
|
Колонка помечается `sortable: true` в `columnsConfig`. Заголовок переключает сортировку по
|
|
@@ -95,8 +106,8 @@ protected readonly hasRowActions = (row: IBooking.State): boolean => row.canConf
|
|
|
95
106
|
|
|
96
107
|
## Тулбар
|
|
97
108
|
|
|
98
|
-
Тулбар поделён на две части слотами: `
|
|
99
|
-
`
|
|
109
|
+
Тулбар поделён на две части слотами: `<префикс>ToolbarLeft` — то, что меняет выборку,
|
|
110
|
+
`<префикс>ToolbarRight` — действия над списком. Своей раскладки внутри тулбара экран не заводит.
|
|
100
111
|
|
|
101
112
|
Левый слот — фильтры и поиск. Правый — иконки `<префикс>-icon-button variant="primary"` с парой
|
|
102
113
|
`tooltip` + `ariaLabel` одного текста: обновление (`sync`), настройки столбцов (`sliders-v`),
|
|
@@ -105,7 +116,7 @@ protected readonly hasRowActions = (row: IBooking.State): boolean => row.canConf
|
|
|
105
116
|
## Отказ загрузки
|
|
106
117
|
|
|
107
118
|
```typescript
|
|
108
|
-
this.#notifications.error(this.#transloco.translate(this.#store.errorKey() ?? '
|
|
119
|
+
this.#notifications.error(this.#transloco.translate(this.#store.errorKey() ?? 'recordsLoadFailed'));
|
|
109
120
|
```
|
|
110
121
|
|
|
111
122
|
Ключ читается сразу после запроса, а не подпиской на сигнал стора: стор делят список и панель
|
|
@@ -118,7 +129,7 @@ this.#notifications.error(this.#transloco.translate(this.#store.errorKey() ?? 'b
|
|
|
118
129
|
npx nx build admin
|
|
119
130
|
```
|
|
120
131
|
|
|
121
|
-
Продовая сборка обязательна: без `[
|
|
132
|
+
Продовая сборка обязательна: без `[<префикс>TableRowActionsRowType]` тип `let-row` выводится как
|
|
122
133
|
`unknown`, и падает только она — юниты и дев-сервер проходят.
|
|
123
134
|
|
|
124
135
|
## Частые промахи
|
|
@@ -65,7 +65,7 @@ description: Паттерн правила navigation. Брать при зав
|
|
|
65
65
|
- Обход одного верхнего ряда при поиске владельца адреса: разделы обходятся вместе с пунктами
|
|
66
66
|
их панелей, иначе вложенный экран остаётся без гейтинга.
|
|
67
67
|
- Подсказка у доступного пункта: она дословно повторяет подпись рядом и не добавляет ничего.
|
|
68
|
-
- `
|
|
68
|
+
- `<префикс>PopoverTrigger` не поставлен явно: директива попапа по умолчанию открывается нажатием, и
|
|
69
69
|
два соседних элемента одного ряда ведут себя по-разному.
|
|
70
70
|
- Переезд адреса без правки сквозных спек: они ходят по адресам, и прогон станет красным.
|
|
71
71
|
- Иконка, подобранная «похожая»: отсутствующая добавляется в спрайт и в `<префикс>-icon-names.ts`.
|
|
@@ -77,7 +77,7 @@ export class ComponentNameComponent {
|
|
|
77
77
|
## `qa-dataid` — на каждый интерактивный элемент
|
|
78
78
|
|
|
79
79
|
```html
|
|
80
|
-
<button
|
|
80
|
+
<button <префикс>Button qa-dataid="calendar-retry-prices" type="button" (click)="retryPrices.emit()">Повторить</button>
|
|
81
81
|
<div rtElem="grid" qa-dataid="admin-calendar-grid"></div>
|
|
82
82
|
```
|
|
83
83
|
|
|
@@ -132,5 +132,7 @@ constructor() {
|
|
|
132
132
|
обводку фокуса у края прокрутки.
|
|
133
133
|
- Панель, остающаяся открытой после успеха, не сбросила нетронутость (`markAsPristine`) —
|
|
134
134
|
вопрос о правках задаётся сразу после записи.
|
|
135
|
-
- Отключённые поля ввода вместо данных: запись, которую только смотрят, показывается
|
|
136
|
-
|
|
135
|
+
- Отключённые поля ввода вместо данных: запись, которую только смотрят, показывается готовым
|
|
136
|
+
списком свойств кита — `<префикс>-detail-list`, `<префикс>-info-item`. Своя разметка на `<dl>`
|
|
137
|
+
идёт последней и только как разовое отступление: она и есть отход от общего вида, а решает
|
|
138
|
+
его владелец, — перечень, ставящий её первой, учит обходить готовое раньше, чем спросить.
|
|
@@ -112,3 +112,12 @@ const response: Response = await fetch(RATES_URL, { signal: AbortSignal.timeout(
|
|
|
112
112
|
приложение при этом работает.
|
|
113
113
|
- **Ошибка положена в поле как есть, без разбора причины.** Объект ошибки сериализуется в
|
|
114
114
|
пустой `{}`, и в логе не останется ни текста, ни класса.
|
|
115
|
+
- **Незаполненная часть ключа группы стоит пропуском, а не пустой строкой.** Два отказа без
|
|
116
|
+
процедуры и без предмета работы обязаны сойтись в одну группу; с пропуском в ключе каждый
|
|
117
|
+
заводит свою, и лента наполняется одиночными строками об одном и том же.
|
|
118
|
+
- **Одинаковые отказы, пришедшие разом, дают две группы.** Между «такой группы ещё нет» и
|
|
119
|
+
вставкой успевает пройти второй, поэтому случай разбирается не проверкой перед вставкой, а
|
|
120
|
+
после того, как вставка не прошла.
|
|
121
|
+
- **Имя строки лога длиннее предела ключа не записывается вовсе.** Оно собирается подстановкой
|
|
122
|
+
и ничем не ограничено, а у ключа предел есть: без обрезки отказ с длинным именем теряется
|
|
123
|
+
молча — ни строки в ленте, ни отказа в логах.
|
|
@@ -47,14 +47,14 @@ export const DEFAULT_PAGE_SIZE: number = 20;
|
|
|
47
47
|
решает вызывающий.
|
|
48
48
|
|
|
49
49
|
```typescript
|
|
50
|
-
const operator:
|
|
50
|
+
const operator: TFilterOperatorType | null = listFilterOperatorOf(filter.operatorType);
|
|
51
51
|
if (!operator) {
|
|
52
52
|
throw new ConnectError(`filter operator is required: ${filter.propertyName}`, Code.InvalidArgument);
|
|
53
53
|
}
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
```typescript
|
|
57
|
-
const direction:
|
|
57
|
+
const direction: TListSortOrderType = listSortOrderOf(rawDirection) ?? EListSortOrder.ASC;
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
Сервер отбивает запрос, экран берёт умолчание. Общий маппер здесь не годится: он подал бы
|
|
@@ -41,9 +41,15 @@ PR открыт черновиком — и с этой минуты работ
|
|
|
41
41
|
```
|
|
42
42
|
PR #<номер> открыт черновиком. Жду прогона: пока он идёт, о работе известно только то, что
|
|
43
43
|
она запушена. Как закончится — разберу папку задачи последним коммитом, сниму черновик и
|
|
44
|
-
попрошу тебя влить.
|
|
44
|
+
попрошу тебя влить. Следующая задача уже взята: #<номер>, ветка <имя ветки>.
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
+
Последняя строка называет взятое, а не намерение взять, и это не оборот речи. Образец, который
|
|
48
|
+
кончается обещанием, исполняется как обещание: заход произносит последнюю строку и на этом
|
|
49
|
+
кончает ход — сообщение при этом выглядит полным, и пустоты за ним не видно ни владельцу, ни
|
|
50
|
+
самому заходу. Образец, который кончается номером заведённой ветки, так исполнить нельзя: пока
|
|
51
|
+
ветки нет, строку писать нечем. Разбор — `2026-08-16-next-task-said-not-taken.md`.
|
|
52
|
+
|
|
47
53
|
Прогон зелёный, папка разобрана и запушена, черновик снят:
|
|
48
54
|
|
|
49
55
|
```
|
|
@@ -168,6 +168,13 @@ flowchart TD
|
|
|
168
168
|
отбивает пуш, а не печатается рядом с ним: напечатанное предупреждение исполнитель читает как
|
|
169
169
|
разрешение. Дважды подряд правка, прошедшая гейт целиком, была отбита конвейером — и оба раза
|
|
170
170
|
зелёный гейт был прочитан как «локально всё зелено».
|
|
171
|
+
- **Сверка раскладки стоит в наборе гейта пуша наравне с линтом и сборкой.** Правка, положенная
|
|
172
|
+
в разложенную копию мимо источника, в день, когда её делают, не ломает ничего: дерево
|
|
173
|
+
работает, проверки зелёные, а расхождение видно только тому, кто позовёт сверку сам. Копится
|
|
174
|
+
оно молча и всплывает на чужой работе — раскладка отказывает по правленому файлу целиком и не
|
|
175
|
+
кладёт ни одного другого, так что цену платит тот, кто правил соседний ресурс. Статьёй выше
|
|
176
|
+
эта строка не покрывается: сверки нет в конвейере, а значит нет и шага, который она бы
|
|
177
|
+
закрывала, — в набор она ставится прямо, а не выводится из его полноты.
|
|
171
178
|
- **После вливания главной ветки набор проверок пересматривается по тому, что ветка везёт
|
|
172
179
|
теперь.** Вливание меняет состав правки: проверять по тому, что правил автор, — значит
|
|
173
180
|
проверять половину, а отвечает ветка целиком. Ветка, не тронувшая ни строки показа, прогоняет
|
|
@@ -126,6 +126,12 @@ flowchart TD
|
|
|
126
126
|
умолчаниями, которые он разрешает. Умолчание образа задаётся в самом образе.
|
|
127
127
|
- **Образы выкатываются по sha коммита, а не по метке «последний».** Метка в реестре отстаёт
|
|
128
128
|
от главной ветки, и прод молча возвращается к прежней версии, продолжая отвечать.
|
|
129
|
+
- **Убирает за собой и та машина, которая образы собирает.** Отбор у обеих один — своё имя
|
|
130
|
+
реестра, три последних sha, поднятые контейнеры остаются, — и зовётся он одним сценарием:
|
|
131
|
+
разойдясь, две чистки начали бы оставлять разное, а заметить это нечем. Отличаются они
|
|
132
|
+
хвостом: сервер снимает следом висячие слои и кэш сборки, машина сборки оставляет их себе,
|
|
133
|
+
иначе каждая сборка идёт как первая. Чистка на сборке не ждёт мержа: образ ветки занимает
|
|
134
|
+
столько же места, в реестр не уезжает вовсе и точкой отката не бывает.
|
|
129
135
|
- **Выкатка убирает за собой старые образы, оставляя три последних sha.** Помеченный sha образ
|
|
130
136
|
висячим не бывает никогда, и чистка висячего его не касается: за полгода они съедают диск
|
|
131
137
|
сервера целиком. Три sha — это глубина отката, и меньше брать нельзя: поломка, замеченная
|
|
@@ -142,6 +148,15 @@ flowchart TD
|
|
|
142
148
|
- **PR проверяется до мержа тем же конвейером, что и главная ветка.** Проверки и сборки
|
|
143
149
|
образов идут на событии `pull_request`, выкатка — нет: её держит условие по главной ветке у
|
|
144
150
|
своего задания, а образ PR в реестр не уезжает.
|
|
151
|
+
- **Вершина открытого PR без прогона видна сверкой очереди работ.** Страница PR без прогона
|
|
152
|
+
выглядит так же, как страница с зелёным: цвета у неё нет ни там, ни там, — и вершину, за
|
|
153
|
+
которой прогон не встал, замечали только тем, что открывали список прогонов руками. Сверка
|
|
154
|
+
спрашивает вершину, а не ветку, считает сам факт прогона, а не его цвет, и свежей вершине
|
|
155
|
+
даёт время: между пушем и прогоном проходят минуты.
|
|
156
|
+
- **Черновик при зелёном прогоне на вершине — расхождение сверки.** У черновика кнопка слияния
|
|
157
|
+
заблокирована самим хостингом: зелёная страница PR владельцу ничего не разрешает, а список, в
|
|
158
|
+
котором всё серое, читается как «работа не сделана». Гард снятия черновика сюда не достаёт —
|
|
159
|
+
он судит один ход и молчит, пока ветка везёт папку своей задачи.
|
|
145
160
|
- **Расхождение прода с главной веткой видно сверкой очереди работ.** Задача уходит из очереди
|
|
146
161
|
мержем, но мерж — ещё не прод: отказавшая выкатка не трогает ни задачу, ни её колонку, и
|
|
147
162
|
заметить её неоткуда. Сверка спрашивает последний прогон главной ветки и судит только
|
|
@@ -164,6 +179,13 @@ flowchart TD
|
|
|
164
179
|
отбивает пуш, а не печатается рядом с ним: напечатанное предупреждение исполнитель читает как
|
|
165
180
|
разрешение. Дважды подряд правка, прошедшая гейт целиком, была отбита конвейером — и оба раза
|
|
166
181
|
зелёный гейт был прочитан как «локально всё зелено».
|
|
182
|
+
- **Сверка раскладки стоит в наборе гейта пуша наравне с линтом и сборкой.** Правка, положенная
|
|
183
|
+
в разложенную копию мимо источника, в день, когда её делают, не ломает ничего: дерево
|
|
184
|
+
работает, проверки зелёные, а расхождение видно только тому, кто позовёт сверку сам. Копится
|
|
185
|
+
оно молча и всплывает на чужой работе — раскладка отказывает по правленому файлу целиком и не
|
|
186
|
+
кладёт ни одного другого, так что цену платит тот, кто правил соседний ресурс. Статьёй выше
|
|
187
|
+
эта строка не покрывается: сверки нет в конвейере, а значит нет и шага, который она бы
|
|
188
|
+
закрывала, — в набор она ставится прямо, а не выводится из его полноты.
|
|
167
189
|
- **После вливания главной ветки набор проверок пересматривается по тому, что ветка везёт
|
|
168
190
|
теперь.** Вливание меняет состав правки: проверять по тому, что правил автор, — значит
|
|
169
191
|
проверять половину, а отвечает ветка целиком. Ветка, не тронувшая ни строки показа, прогоняет
|
|
@@ -269,6 +291,16 @@ flowchart TD
|
|
|
269
291
|
список прогонов ветки смотрится тем же движением, что и пуш — `gh run list --branch <ветка>`.
|
|
270
292
|
Один такой отказ простоял в списке до мержа, и на него никто не посмотрел: выкатка после
|
|
271
293
|
мержа отказала ровно тем же.
|
|
294
|
+
- **Прогон, не вставший на пуш, возвращается повтором события, а не разбором ветки.** Замером
|
|
295
|
+
проверены обе законные дороги: и открытие PR, и пуш в уже открытый PR прогон заводят —
|
|
296
|
+
текстовый коммит и учётная запись, которой пушат, тут ни при чём. Пропавшие события пришлись
|
|
297
|
+
на час, когда хостинг отвечал `429` на загрузке действия и `503` на API, а списком прогонов
|
|
298
|
+
«не завёлся» от «не создан» не отличить. Поэтому вершину без прогона называет сверка очереди
|
|
299
|
+
работ, а событие возвращается новым коммитом либо перезакрытием PR.
|
|
300
|
+
- **Красное на шаге подготовки задания — отказ хостинга, а не дефект ветки.** Раннер не смог
|
|
301
|
+
скачать действие чекаута и получил `429 Too Many Requests`; до кода прогон при этом не дошёл
|
|
302
|
+
вовсе. Лечится перезапуском прогона, и от красного по существу отличается тем, на каком шаге
|
|
303
|
+
оно встало: три прогона одного дня упали именно так.
|
|
272
304
|
- **Контекст `runner` в `env` задания отбивает весь файл конвейера.** Там доступны только
|
|
273
305
|
`github`, `needs`, `strategy`, `matrix`, `vars`, `secrets` и `inputs`; `runner` появляется на
|
|
274
306
|
уровне шага, где то же значение приходит переменной окружения. Такой файл не принимается
|
|
@@ -279,6 +311,15 @@ flowchart TD
|
|
|
279
311
|
пересечения нет, раннер стоит `online` и не берёт ничего, а задания уходят в облако — по
|
|
280
312
|
состоянию это выглядит настроенным. Владельцу называют выполненное задание с его номером, а
|
|
281
313
|
не строку состояния.
|
|
314
|
+
- **Раннер на своей машине делает прогон общим ресурсом, и стенд у прогонов один.** Порт,
|
|
315
|
+
имя базы и каталог сборки зашиты в дереве одним значением на всех: два прогона разом
|
|
316
|
+
поднимают два стенда на один порт, и второй падает целиком. Дороже всего не падение, а его
|
|
317
|
+
вид — в отчёте оно выглядит десятком красных спек про экраны, то есть дефектом правки,
|
|
318
|
+
которого нет; три прогона подряд так и упали на трёх ветках, не тронувших кода. Лечится с
|
|
319
|
+
двух сторон сразу: конвейеру объявляется группа очереди на всё дерево, а имена стенда
|
|
320
|
+
читаются из окружения с нынешними значениями в умолчании — иначе прогон и гейт пуша, зовущий
|
|
321
|
+
ту же команду, столкнутся и при очереди. Одной очереди мало, одних имён — тоже: прогоны делят
|
|
322
|
+
ещё диск, кэш сборщика и демон образов.
|
|
282
323
|
- **Вход в реестр образов из раннера, запущенного службой, отказывает молча.** Служба идёт без
|
|
283
324
|
сеанса пользователя, а клиент реестра уходит в системный помощник хранения ключей и получает
|
|
284
325
|
отказ во взаимодействии — задание падает до сборки. Свой каталог настроек с пустым помощником
|
|
@@ -167,6 +167,13 @@ flowchart TD
|
|
|
167
167
|
отбивает пуш, а не печатается рядом с ним: напечатанное предупреждение исполнитель читает как
|
|
168
168
|
разрешение. Дважды подряд правка, прошедшая гейт целиком, была отбита конвейером — и оба раза
|
|
169
169
|
зелёный гейт был прочитан как «локально всё зелено».
|
|
170
|
+
- **Сверка раскладки стоит в наборе гейта пуша наравне с линтом и сборкой.** Правка, положенная
|
|
171
|
+
в разложенную копию мимо источника, в день, когда её делают, не ломает ничего: дерево
|
|
172
|
+
работает, проверки зелёные, а расхождение видно только тому, кто позовёт сверку сам. Копится
|
|
173
|
+
оно молча и всплывает на чужой работе — раскладка отказывает по правленому файлу целиком и не
|
|
174
|
+
кладёт ни одного другого, так что цену платит тот, кто правил соседний ресурс. Статьёй выше
|
|
175
|
+
эта строка не покрывается: сверки нет в конвейере, а значит нет и шага, который она бы
|
|
176
|
+
закрывала, — в набор она ставится прямо, а не выводится из его полноты.
|
|
170
177
|
- **После вливания главной ветки набор проверок пересматривается по тому, что ветка везёт
|
|
171
178
|
теперь.** Вливание меняет состав правки: проверять по тому, что правил автор, — значит
|
|
172
179
|
проверять половину, а отвечает ветка целиком. Ветка, не тронувшая ни строки показа, прогоняет
|
|
@@ -70,6 +70,10 @@ flowchart TD
|
|
|
70
70
|
семьям, живёт в `common/platform`.
|
|
71
71
|
- **Основание семейства видит только `util`.** Его зовут все домены семьи, и любая его
|
|
72
72
|
зависимость становится общей для всех сразу.
|
|
73
|
+
- **Поведение, которого основанию семейства не видно, приходит к нему токеном.** Токен
|
|
74
|
+
объявляется в `util` общего домена, а `data-access` подставляет туда свою реализацию: так
|
|
75
|
+
основание зовёт её, не видя её либы. Расширение границ основания ради одного вызова
|
|
76
|
+
открывает эту либу всем доменам семьи сразу и обратно уже не сужается.
|
|
73
77
|
- **Домен заводится под предмет, а не под механику.** Механика, общая нескольким доменам,
|
|
74
78
|
едет в либу, которой она уже видна: у фронта это основание семейства, у бэкенда — слой
|
|
75
79
|
`util`, перечисленный у каждого домена.
|
package/assets/rules/lists.md
CHANGED
|
@@ -13,14 +13,14 @@ description: Правило под «Закон о списке записей»
|
|
|
13
13
|
|
|
14
14
|
## Как это называется здесь
|
|
15
15
|
|
|
16
|
-
| В законе | Здесь
|
|
17
|
-
| ------------------------- |
|
|
18
|
-
| таблица записей | `rt-table` из `@rt-tools/ui-kit-v2`, вход `[dataSource]`
|
|
19
|
-
| состав и порядок столбцов | `[columnsConfig]`, хранятся по ключу `tableId`
|
|
20
|
-
| карточка на узком экране | ветка `<префикс>-table`, а не своя разметка
|
|
21
|
-
| тулбар | `<префикс>-toolbar` со слотами `
|
|
22
|
-
| выборка | `IList.Query.State` — страница, сортировка, условия отбора, строка поиска
|
|
23
|
-
| панель настройки столбцов | асайд по маршруту `path: 'table-settings'`
|
|
16
|
+
| В законе | Здесь |
|
|
17
|
+
| ------------------------- | ------------------------------------------------------------------------------- |
|
|
18
|
+
| таблица записей | `rt-table` из `@rt-tools/ui-kit-v2`, вход `[dataSource]` |
|
|
19
|
+
| состав и порядок столбцов | `[columnsConfig]`, хранятся по ключу `tableId` |
|
|
20
|
+
| карточка на узком экране | ветка `<префикс>-table`, а не своя разметка |
|
|
21
|
+
| тулбар | `<префикс>-toolbar` со слотами `<префикс>ToolbarLeft` и `<префикс>ToolbarRight` |
|
|
22
|
+
| выборка | `IList.Query.State` — страница, сортировка, условия отбора, строка поиска |
|
|
23
|
+
| панель настройки столбцов | асайд по маршруту `path: 'table-settings'` |
|
|
24
24
|
|
|
25
25
|
## Где это лежит
|
|
26
26
|
|
|
@@ -76,7 +76,7 @@ flowchart TD
|
|
|
76
76
|
- **Сортируема та колонка, у чьей ячейки шапки стоит заголовок сортировки.** Отдельного
|
|
77
77
|
признака рядом со списком столбцов нет, и расходиться нечему.
|
|
78
78
|
- **Клик по строке открывает запись, а меню — для действий над ней.** Вид нажимаемой строки
|
|
79
|
-
даёт `clickable`, активацию мышью и с клавиатуры —
|
|
79
|
+
даёт `clickable`, активацию мышью и с клавиатуры — `<префикс>TableRow`; клик по кнопке внутри строки
|
|
80
80
|
активацией не считается.
|
|
81
81
|
- **Доступность действия лежит полем строки, а не вызовом метода компонента.** Метод из шаблона
|
|
82
82
|
пересчитывался бы на каждой проверке.
|
|
@@ -105,7 +105,7 @@ flowchart TD
|
|
|
105
105
|
|
|
106
106
|
## Ловушки
|
|
107
107
|
|
|
108
|
-
- Без `[
|
|
108
|
+
- Без `[<префикс>TableRowActionsRowType]` тип `let-row` выводится как `unknown`, и падает только
|
|
109
109
|
продовая сборка — юниты и дев-сервер проходят.
|
|
110
110
|
- Прокрутке нужны оба правила вместе: контейнер с `overflow-x`, таблица с
|
|
111
111
|
`min-width: max-content`. С одним столбцы сжимаются вместо сдвига.
|
|
@@ -17,7 +17,7 @@ description: Правило под «Закон об общем коде при
|
|
|
17
17
|
| общий пакет | `@rt-tools/utils` — собран без фреймворка, зависит от одного `tslib`, грузится под голым Node |
|
|
18
18
|
| общая либа проекта | `@<область>/common/util`; её тег входит в набор `UNIVERSAL` и виден всем трём приложениям |
|
|
19
19
|
| число-настройка | `DEFAULT_PAGE_SIZE`, `MAX_PAGE_SIZE` |
|
|
20
|
-
| набор значений | `
|
|
20
|
+
| набор значений | `EFilterOperatorType`, `EListSortOrder` |
|
|
21
21
|
| сверка значения с набором | `listSortOrderOf`, `listFilterOperatorOf` |
|
|
22
22
|
| выборка списка | `IPageModel`, `ISortModel`, `IFilterModel`, `IListState` |
|
|
23
23
|
|