@rt-tools/agent-kit 0.5.0 → 0.5.2

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.
Files changed (86) hide show
  1. package/README.md +73 -0
  2. package/assets/checks/check-doc-paths.mjs +200 -30
  3. package/assets/checks/check-specs.mjs +42 -7
  4. package/assets/checks/rt-kit-checks.config.mjs +12 -0
  5. package/assets/commands/agent-kit-digest.md +83 -0
  6. package/assets/commands/next-session.md +122 -0
  7. package/assets/commands/skill-curator.md +33 -1
  8. package/assets/defaults/gate-map.sh +23 -1
  9. package/assets/defaults/project.sh +37 -1
  10. package/assets/docs/GLOSSARY.md +77 -0
  11. package/assets/hooks/docs-guard.sh +18 -1
  12. package/assets/hooks/git-guard-delivery.sh +30 -3
  13. package/assets/hooks/git-guard-main.sh +8 -0
  14. package/assets/hooks/git-guard-push-tests.sh +8 -1
  15. package/assets/hooks/grill-gate.sh +38 -16
  16. package/assets/hooks/lint-after-edit.sh +10 -3
  17. package/assets/hooks/observe.sh +90 -0
  18. package/assets/hooks/postmortem-guard.sh +92 -0
  19. package/assets/hooks/profile-check.sh +43 -0
  20. package/assets/hooks/qa-dataid-guard.sh +9 -2
  21. package/assets/hooks/reuse-first-guard.sh +9 -2
  22. package/assets/hooks/skill-gate.sh +15 -0
  23. package/assets/hooks/skill-loaded.sh +7 -0
  24. package/assets/hooks/task-context-load.sh +16 -2
  25. package/assets/hooks/task-flow-guard.sh +9 -2
  26. package/assets/hooks/window-fill-guard.sh +157 -0
  27. package/assets/laws/code-structure.md +10 -0
  28. package/assets/laws/delivery.md +8 -1
  29. package/assets/laws/project-documentation.md +9 -0
  30. package/assets/laws/verifiability.md +9 -0
  31. package/assets/laws/work-conduct.md +47 -0
  32. package/assets/patterns/git-workflow-commit.azure.md +16 -12
  33. package/assets/patterns/git-workflow-commit.github.md +16 -12
  34. package/assets/patterns/git-workflow-commit.gitlab.md +16 -12
  35. package/assets/patterns/spec-driven-domain.md +19 -0
  36. package/assets/patterns/task-flow-close.md +4 -4
  37. package/assets/patterns/task-flow-handoff.md +115 -0
  38. package/assets/patterns/task-flow-resume.md +14 -2
  39. package/assets/patterns/task-flow-start.md +40 -2
  40. package/assets/rules/doc-style.md +39 -1
  41. package/assets/rules/git-workflow.azure.md +39 -0
  42. package/assets/rules/git-workflow.github.md +38 -0
  43. package/assets/rules/git-workflow.gitlab.md +38 -0
  44. package/assets/rules/spec-driven.md +14 -0
  45. package/assets/rules/task-flow.md +70 -3
  46. package/assets/skills/agent-kit.md +32 -0
  47. package/assets/templates/postmortem.md +32 -0
  48. package/assets/templates/proposal.md +39 -0
  49. package/bin/agent-kit.d.ts.map +1 -1
  50. package/bin/agent-kit.js +50 -1
  51. package/bin/agent-kit.js.map +1 -1
  52. package/lib/catalog.d.ts +33 -0
  53. package/lib/catalog.d.ts.map +1 -1
  54. package/lib/catalog.js +55 -1
  55. package/lib/catalog.js.map +1 -1
  56. package/lib/commands.d.ts +41 -0
  57. package/lib/commands.d.ts.map +1 -1
  58. package/lib/commands.js +315 -3
  59. package/lib/commands.js.map +1 -1
  60. package/lib/config.d.ts +11 -1
  61. package/lib/config.d.ts.map +1 -1
  62. package/lib/config.js +6 -0
  63. package/lib/config.js.map +1 -1
  64. package/lib/hooks-map.d.ts +15 -3
  65. package/lib/hooks-map.d.ts.map +1 -1
  66. package/lib/hooks-map.js +47 -11
  67. package/lib/hooks-map.js.map +1 -1
  68. package/lib/observations.d.ts +72 -0
  69. package/lib/observations.d.ts.map +1 -0
  70. package/lib/observations.js +126 -0
  71. package/lib/observations.js.map +1 -0
  72. package/lib/proposals.d.ts +48 -0
  73. package/lib/proposals.d.ts.map +1 -0
  74. package/lib/proposals.js +111 -0
  75. package/lib/proposals.js.map +1 -0
  76. package/lib/submit.d.ts +24 -0
  77. package/lib/submit.d.ts.map +1 -0
  78. package/lib/submit.js +26 -0
  79. package/lib/submit.js.map +1 -0
  80. package/lib/sync.d.ts +9 -1
  81. package/lib/sync.d.ts.map +1 -1
  82. package/lib/sync.js +4 -6
  83. package/lib/sync.js.map +1 -1
  84. package/package.json +1 -1
  85. package/rt-tools-agent-kit-0.5.2.tgz +0 -0
  86. package/rt-tools-agent-kit-0.5.0.tgz +0 -0
@@ -0,0 +1,122 @@
1
+ ---
2
+ description: Закрытие захода — главная ветка подтянута, влитые ветки сняты, передача написана
3
+ argument-hint: '[пусто | <что дописать в передачу от себя>]'
4
+ ---
5
+
6
+ Закрой заход: приведи дерево к главной ветке, убери влитые ветки и напиши передачу для
7
+ следующего захода. Дописка владельца к передаче: `$ARGUMENTS`
8
+
9
+ Вызывается **последним действием захода** — после того, как работа закоммичена, а отчёт открыт
10
+ или влит. Команда ничего не мержит, не пушит и не открывает: закрытие захода — уборка, а не
11
+ поставка.
12
+
13
+ ## 1. Прочитай профиль дерева
14
+
15
+ Имя главной ветки, каталог папок задач и каталог передачи у каждого дерева свои:
16
+
17
+ ```bash
18
+ for profile in .claude/rt-kit/defaults/project.sh .claude/rt-kit/project.sh; do
19
+ [ -f "$profile" ] && . "$profile"
20
+ done
21
+ printf 'главная: %s · задачи: %s · передача: %s\n' \
22
+ "${RT_MAIN_BRANCH:-main}" "${RT_TASKS_DIR:-docs/tasks}" "${RT_HANDOFF_DIR:-.claude/handoff}"
23
+ ```
24
+
25
+ Зашивать эти имена в команду нельзя: в первом же дереве, которое зовёт главную ветку иначе,
26
+ уборка уедет не туда.
27
+
28
+ ## 2. Остановись, если в дереве есть незакоммиченное
29
+
30
+ ```bash
31
+ git status --short
32
+ ```
33
+
34
+ Непустой вывод — конец команды. Назови файлы владельцу и не трогай ни веток, ни главной: смена
35
+ ветки уносит правку за собой или отбивается на полпути, а решает, что с ней делать, владелец.
36
+
37
+ Неотслеживаемый файл — тоже незакоммиченное. Скажи о нём отдельной строкой: он мог остаться от
38
+ работы, которую бросили.
39
+
40
+ ## 3. Пойми, по правилу ли ведётся работа
41
+
42
+ ```bash
43
+ git fetch --prune --quiet
44
+ branch="$(git branch --show-current)"
45
+ ```
46
+
47
+ Работа идёт **по правилу**, если имя ветки несёт номер задачи — это `rt_task_branch_ok` из
48
+ профиля — или если в каталоге папок задач лежит папка с именем ветки. Отчёт **влит**, когда
49
+ коммиты ветки уже есть в удалённой главной:
50
+
51
+ ```bash
52
+ git merge-base --is-ancestor HEAD "origin/${RT_MAIN_BRANCH:-main}" && echo влит || echo 'не влит'
53
+ ```
54
+
55
+ ## 4. Приведи дерево к главной ветке
56
+
57
+ - **Работа по правилу и отчёт влит** — задача закрыта, ветка больше не нужна:
58
+
59
+ ```bash
60
+ git switch "${RT_MAIN_BRANCH:-main}" && git pull --ff-only
61
+ ```
62
+
63
+ - **Всё остальное** — работа не кончилась, и ветка остаётся местом, где она продолжится:
64
+
65
+ ```bash
66
+ git merge "origin/${RT_MAIN_BRANCH:-main}"
67
+ ```
68
+
69
+ Конфликт разбирается сейчас, а не в начале следующего захода: назови его владельцу и
70
+ останови команду до его решения.
71
+
72
+ ## 5. Убери ветки
73
+
74
+ Снимаются только влитые в главную: их коммиты есть в ней, и восстанавливать нечего.
75
+
76
+ ```bash
77
+ git branch --merged "${RT_MAIN_BRANCH:-main}" \
78
+ | grep -vE "^\*|^\s*${RT_MAIN_BRANCH:-main}$" \
79
+ | xargs -r git branch -d
80
+ ```
81
+
82
+ Невлитую ветку **не сноси**. Назови её владельцу вместе с числом коммитов, которых нет в
83
+ главной, — по ним видно, что именно потеряется, если её снести:
84
+
85
+ ```bash
86
+ for b in $(git branch --no-merged "${RT_MAIN_BRANCH:-main}" --format='%(refname:short)'); do
87
+ printf '%s: %s коммитов мимо главной\n' "$b" "$(git rev-list --count "${RT_MAIN_BRANCH:-main}..$b")"
88
+ done
89
+ ```
90
+
91
+ Мёртвые ссылки на удалённые ветки снял `git fetch --prune` шагом 3.
92
+
93
+ ## 6. Напиши передачу
94
+
95
+ Что в ней стоит и в какой форме — паттерн `task-flow-handoff`; здесь только место и порядок.
96
+ Файл один на ветку и лежит вне истории дерева:
97
+
98
+ ```bash
99
+ mkdir -p "${RT_HANDOFF_DIR:-.claude/handoff}"
100
+ # файл — ${RT_HANDOFF_DIR:-.claude/handoff}/<ветка>.md
101
+ ```
102
+
103
+ Имя берётся от той ветки, в которой шла работа, — не от той, куда команда перешла шагом 4.
104
+
105
+ К тому, что требует паттерн, эта команда добавляет своё: что она убрала — снятые ветки,
106
+ состояние главной, оставшееся невлитым. Следующий заход начинается ровно с этого.
107
+
108
+ Заход, кончившийся ничем, передачу пишет тоже: «пробовали так — не вышло, потому что» стоит
109
+ дороже пустого файла. Дописку владельца из `$ARGUMENTS` вставь своим разделом, не пересказывая.
110
+
111
+ ## 7. Отдай итог
112
+
113
+ Последней строкой — путь к передаче: владелец вставляет её в новый заход одной вставкой. Перед
114
+ ней: что стало с главной веткой, какие ветки сняты, какие остались невлитыми. Содержание
115
+ передачи не пересказывай — владелец её и так прочитает.
116
+
117
+ ## Чего команда не делает
118
+
119
+ - не мержит отчёт и не пушит: это поставка, и вслепую она не делается;
120
+ - не сносит невлитую ветку и не трогает папку задачи;
121
+ - не коммитит передачу — она лежит вне дерева намеренно, иначе рядом с ходом работы заводится
122
+ вторая запись об одном и том же.
@@ -47,7 +47,39 @@ ls -t "${TMPDIR}claude-skill-gate/"*.loaded | head -5
47
47
  Инструментом `Agent`, `subagent_type: 'skill-curator'`. В промпт — путь к `.loaded` и сводку
48
48
  целиком.
49
49
 
50
- ## 4. Отдай результат
50
+ ## 4. Выгрузи предложения файлом
51
+
52
+ Ответ роли живёт в переписке и умирает вместе с ней, а правки в пакет идут из другого дерева и
53
+ в другой день. Поэтому предложения ложатся на диск — их пишешь ты, не роль: файлов она не
54
+ пишет вовсе.
55
+
56
+ ```bash
57
+ mkdir -p .claude/rt-kit/proposals
58
+ cp .claude/rt-kit/templates/proposal.md .claude/rt-kit/proposals/$(date +%F)-<ветка>.md
59
+ ```
60
+
61
+ Дальше — по блоку на предложение, заголовком `## <адрес> · <ресурс>`. Адрес роль уже поставила,
62
+ твоё дело — не потерять его и не переписать текст своими словами.
63
+
64
+ Файл читает `agent-kit propose`: по заголовку он отбирает то, что уезжает в репозиторий пакета.
65
+ Блок без адреса в заголовке не уедет никуда и останется лежать молча.
66
+
67
+ ## 5. Отправь то, что адресовано пакету
68
+
69
+ ```bash
70
+ npx agent-kit propose --dry-run # что уехало бы
71
+ npx agent-kit propose # завести запись в очереди работ пакета
72
+ ```
73
+
74
+ Уезжают только блоки с адресом «пакет», и вместе с ними — сводка наблюдений: без цифр
75
+ предложение читается как мнение. Отправленное помечается ссылкой в том же файле и второй раз
76
+ не уезжает.
77
+
78
+ Отправка отказывает, если в тексте предложения нашёлся адрес этого дерева — путь, имя корня,
79
+ чужой репозиторий. Это не придирка: файл уезжает в чужой репозиторий целиком. Правь текст, а не
80
+ обходи проверку.
81
+
82
+ ## 6. Отдай результат владельцу
51
83
 
52
84
  Покажи предложения агента **как есть**: он пишет готовый текст для вставки, и пересказ его
53
85
  портит. По каждому скажи своё — согласен или нет и почему; правило, с которым ты не согласен,
@@ -51,7 +51,15 @@ skill_for_default() {
51
51
  case "$kind" in
52
52
  edit)
53
53
  case "$target" in
54
- # Файлы самого агента правятся без правила: правило на них это оно само.
54
+ # Правило и паттерн такая же договорённость, как спек: обязательные разделы,
55
+ # утверждение с привязкой, граница между статьёй закона и утверждением правила.
56
+ # Ветка стоит раньше общего исключения и раньше `*.md`: под исключением текст
57
+ # правила переписывался без единого требования, а `*.md` увёл бы его в правило
58
+ # формулировок — оно про слова, не про устройство.
59
+ */.claude/skills/*.md) printf '%s\n' 'spec-driven' ;;
60
+
61
+ # Остальные файлы самого агента правятся без правила: правило на них — это оно
62
+ # само.
55
63
  */.claude/skills/* | */.claude/agents/* | */.claude/commands/* | */.claude/workflows/*) return 0 ;;
56
64
 
57
65
  # Тексты проекта. Спек держит устройство домена, закон — договорённость,
@@ -61,6 +69,18 @@ skill_for_default() {
61
69
  */docs/constitution/*) printf '%s\n' 'spec-driven' ;;
62
70
  *.md) printf '%s\n' 'doc-style' ;;
63
71
 
72
+ # Конфиги линтеров — то же самое, только запреты в них исполняемые: они и есть
73
+ # исполнение правил про типы и про оформление, а комментарии в них пересказывают
74
+ # эти правила поимённо. Правились без единого правила под рукой.
75
+ */eslint.config.mjs | */eslint.config.js) printf '%s\n' 'typescript-conventions' ;;
76
+ */stylelint.config.js | */stylelint.config.mjs) printf '%s\n' 'styling-bem' ;;
77
+
78
+ # Проверка повторов исполняет утверждения правила об общем коде и требуется
79
+ # только им: правило о раскладке либ говорит про неё одной строкой с отсылкой,
80
+ # а привязки её признаков стоят при общем коде. Два отказа подряд на правку двух
81
+ # строк комментария стоят захода, а второе прочитанное правило не пригождается.
82
+ */tools/check-dupes.mjs | */tools/dupes-allowlist.json) printf '%s\n' 'shared-code' ;;
83
+
64
84
  # Поставка: состав зависимостей — это то, что приезжает на прод. Правка
65
85
  # скриптов зависимостью не является, и правило про версии на неё не вступает.
66
86
  # Оговорка: удаление зависимости приходит правкой без номера версии и сюда не
@@ -91,6 +111,8 @@ skill_for_default() {
91
111
  case "$target" in
92
112
  *git\ commit* | *git\ push* | *git\ merge* | *git\ rebase* | *git\ cherry-pick* | *gh\ pr\ * | *glab\ mr\ * | *az\ repos\ *)
93
113
  printf '%s\n' 'git-workflow' ;;
114
+ *git\ worktree\ add* | *git\ worktree\ remove*)
115
+ printf '%s\n' 'git-workflow' ;;
94
116
  *prisma\ migrate* | *prisma\ db\ *) printf '%s\n' 'git-workflow' ;;
95
117
  *curl\ *localhost* | *wget\ *localhost*) printf '%s\n' 'browser-verification' ;;
96
118
  esac
@@ -77,6 +77,30 @@ RT_TASKS_DIR="${RT_TASKS_DIR:-docs/tasks}"
77
77
  # удалить проще, чем разобрать, а слова владельца больше нигде не записаны.
78
78
  RT_ARCHIVE_DIR="${RT_ARCHIVE_DIR:-docs/archive}"
79
79
 
80
+ # Размер окна захода в токенах и пороги стража. Пусто — стража нет: считать долю не от чего, а
81
+ # выведенный из записи захода размер врал бы — модель записана там без пометки о расширенном
82
+ # окне. Дерево задаёт его в настройке агента, переменной окружения того же имени.
83
+ RT_WINDOW_TOKENS="${RT_WINDOW_TOKENS:-}"
84
+ RT_WINDOW_WARN_PCT="${RT_WINDOW_WARN_PCT:-40}"
85
+ RT_WINDOW_STOP_PCT="${RT_WINDOW_STOP_PCT:-50}"
86
+
87
+ # Куда кладётся передача захода. Вне дерева: состояние работы живёт в ходе работы и коммитится,
88
+ # а передача его пересказывает для вставки в новый заход и в историю не едет.
89
+ RT_HANDOFF_DIR="${RT_HANDOFF_DIR:-.claude/handoff}"
90
+
91
+ # Команды, которые проходят после порога остановки: ими заход закрывается. Отбить их значило бы
92
+ # отобрать у него единственный способ закончиться. Вызов считается по началу строки или сразу за
93
+ # разделителем — упоминание команды в тексте командой не является.
94
+ rt_handoff_allowed_cmd_default() {
95
+ case "$1" in
96
+ git\ * | *[\;\&\|]\ *git\ * | *\$\(git\ *) return 0 ;;
97
+ gh\ * | */gh\ * | glab\ * | */glab\ * | az\ * | */az\ *) return 0 ;;
98
+ *task:move* | *check:* | mkdir\ -p\ * | cat\ * | ls\ *) return 0 ;;
99
+ esac
100
+
101
+ return 1
102
+ }
103
+
80
104
  # Где лежат тексты, которые читают до вопроса владельцу: законы, правила и договорённости о
81
105
  # продукте. По ним гард разговора судит, читалось ли за ход хоть что-то, и их же называет в
82
106
  # подсказке. Пусто у законов и правил разом — дерево этого требования не получает: читать
@@ -101,9 +125,20 @@ rt_report_body_default() {
101
125
  # Признак — путь, а не оценка на глаз: оценку назначает тот, кому она мешает, и порог плывёт.
102
126
  # Правила, тексты, обвязка и зависимости под требование не попадают — иначе разбор задачи
103
127
  # нельзя было бы вести до заведения ветки.
128
+ #
129
+ # Судится путь относительно корня дерева: каталог со словом `projects` в имени встречается и
130
+ # вне репозитория, а правка файла вне корня замыслом этой ветки не распоряжается вовсе. Гарды
131
+ # отдают сюда абсолютный путь целиком, и образец по подстроке совпадал бы с домашним каталогом
132
+ # агента ровно так же, как с кодом дерева.
104
133
  rt_is_app_code_default() {
134
+ root="${CLAUDE_PROJECT_DIR:-$PWD}"
105
135
  case "$1" in
106
- */apps/* | */libs/* | */projects/*) return 0 ;;
136
+ "$root"/*) rel="${1#"$root"/}" ;;
137
+ /*) return 1 ;;
138
+ *) rel="$1" ;;
139
+ esac
140
+ case "$rel" in
141
+ apps/* | libs/* | projects/*) return 0 ;;
107
142
  *) return 1 ;;
108
143
  esac
109
144
  }
@@ -202,3 +237,4 @@ rt_is_app_code() { rt_is_app_code_default "$@"; }
202
237
  rt_qa_decorative() { rt_qa_decorative_default "$@"; }
203
238
  rt_task_state() { rt_task_state_default "$@"; }
204
239
  rt_report_body() { rt_report_body_default "$@"; }
240
+ rt_handoff_allowed_cmd() { rt_handoff_allowed_cmd_default "$@"; }
@@ -0,0 +1,77 @@
1
+ # Словарь проекта
2
+
3
+ Слова, которые в этом дереве значат что-то определённое. Читается перед тем, как написать спек,
4
+ правило, комментарий, тело коммита или описание отчёта: слово отсюда употребляется в том
5
+ значении, что здесь, а слово не отсюда либо заводится здесь же, либо заменяется простым.
6
+
7
+ Термины одного домена живут в разделе «Терминология» его спека — здесь только те, что проходят
8
+ сквозь весь проект.
9
+
10
+ Разделы ниже везёт пакет: это слова слоя правил, и значат они одно и то же везде, где он стоит.
11
+ Предметные слова дерево дописывает своими разделами через надстройку — они сливаются сюда по
12
+ заголовкам, и правка пакета их не трогает.
13
+
14
+ ## Слой правил
15
+
16
+ | Термин | Что это |
17
+ | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
18
+ | Закон | Файл в каталоге конституции. Говорит, что должно быть верно, и не знает ни путей, ни имён файлов. Верен для любого приложения этого класса |
19
+ | Законы приложения | Слой законов, верных только для этого приложения: деньги, локали, доступ. Предметность в них законна — она их предмет |
20
+ | Правило | Скил с `kind: rule`. Привязывает закон к этому дереву: чем это здесь названо и где лежит |
21
+ | Паттерн | Скил с `kind: pattern`. Готовый код и порядок действий; стоит при правиле |
22
+ | Компаньон | Файл `implementation.md` рядом с правилом: имена и пути этого дерева. Пакет знает приём, но не знает имён — их пишет проект |
23
+ | Спек | Описание домена: как он работает. Говорит об установившемся, а не о предстоящем |
24
+ | Домен | Предмет, у которого свой спек. Выросший домен делится на поддомены, а не на соседние домены |
25
+ | Сценарий | Наблюдаемое поведение под номером `SC-<ПРЕФИКС>-<НОМЕР>`. Номер стоит в заголовке теста |
26
+ | Привязка | Строка `` `файл:символ` `` в компаньоне или в спутнике спека — место, где утверждение исполняется |
27
+ | Спутник | Файл рядом со спеком или правилом: компаньон, перечень сценариев |
28
+ | Договорённость о продукте | Как продукт себя поведёт, записанное до кода. Единственное место, где спек говорит о будущем; после выкатки вливается в спек домена, а директория удаляется |
29
+ | Ресурс | Единица того, что везёт пакет правил: закон, правило, паттерн, гард, проверка, роль, команда, конвейер, шаблон, умолчание, документ |
30
+ | Раскладка | Перенос ресурса из пакета в дерево по его роду и настройке слоя |
31
+ | Разложенный файл | Файл в дереве с шапкой пакета. Правится не на месте, а надстройкой: правка на месте теряется на следующей раскладке |
32
+ | Надстройка | Файл дерева, который сливается с разложенным по заголовкам разделов |
33
+ | Наблюдение | Строка о событии слоя правил: правило загружено, гейт отбил, гард отказал. Пишет гард, живёт в дереве, наружу уезжает счётчиками. Журналом не называется |
34
+ | Сводка | Что наблюдения говорят за отрезок дней: чем пользовались, чем ни разу, обо что спотыкались |
35
+ | Предложение | Готовая формулировка правки правил с адресом: пакет, компаньон или дерево. Приносит разбор закрытой задачи, отправляет человек командой |
36
+
37
+ ## Работа
38
+
39
+ | Термин | Что это |
40
+ | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
41
+ | Задача | Единица работы в очереди работ. Заводится до ветки, и номер её стоит в имени ветки и в заголовке отчёта |
42
+ | Отчёт | Заявка на слияние: то же название, что у задачи, переведённое в сделанное |
43
+ | Очередь работ | Доска, на которой видно состояние каждой задачи. Ветки она не видит |
44
+ | Папка задачи | Одна работа от разбора до слияния: разбор просьбы, замысел, ход работы. Умирает со слиянием — разбирается, и объясняющее решение уезжает в архив |
45
+ | Разбор | Расспрос владельца до первой правки. Записывается его словами и задним числом не переписывается |
46
+ | Замысел | Файл папки задачи: след задачи и этапы с признаками готовности. После написания не правится — с ним сверяют результат при приёмке |
47
+ | Ход работы | Файл папки задачи: «Где стоим», решения по ходу с причинами, записи заходов. Единственное место, где отмечается сделанное. Журналом не называется |
48
+ | След задачи | Раздел замысла: какие спеки, законы, правила и части кода работа задевает |
49
+ | Заход | Одна сессия работы над задачей. Работа живёт дольше одного захода, и между ними её состояние держит только ход работы |
50
+ | Заполнение окна | Доля места захода, которую он уже занял: вход, запись в кэш, прочитанное из кэша и вывод последнего ответа, делённые на размер окна. Не «расход» и не «бюджет»: речь о месте, а не о деньгах |
51
+ | Передача | Текст, которым заход закрывается: рабочее дерево, ветка, задача, где лежит ход работы, что сделано, следующий шаг, особенности захода. Кладётся вне дерева и не коммитится |
52
+ | Линия работ | Файл с порядком задач и зависимостями между ними, когда из одного разбора вышло несколько задач. Шире одной ветки |
53
+ | Архив | Записи о состоявшемся: что объясняет закрытое решение. После выкатки не правится |
54
+
55
+ ## Проверки
56
+
57
+ | Термин | Что это |
58
+ | --------------------- | --------------------------------------------------------------------------------------------------------------------------- |
59
+ | Гард | Хук агента, который отбивает действие до того, как оно сделано, и говорит, чем отказ снимается |
60
+ | Гейт | Требование, которое пропускает действие один раз за сессию после того, как выполнено: загружено правило, пройдены проверки |
61
+ | Отказ в пользу работы | Устройство гарда, при котором любая его поломка пропускает действие. Сломанный гард не имеет права остановить работу совсем |
62
+ | Прогон | Запуск набора сценариев. «Тесты гоняются», а не «запускаются в работу» |
63
+ | Сверка | Проверка, которая ничего не правит, а называет расхождения: раскладки с пакетом, спеков с кодом, очереди работ с ветками |
64
+ | Замер | Число, снятое с работающего приложения. Взгляд на экран замером не является |
65
+
66
+ ## Так не пишем
67
+
68
+ | Так не пишем | Пишем так |
69
+ | -------------------------- | ---------------------------------------------------------------------------------------------- |
70
+ | спека (о тесте) | тест — файл рядом с исходником; спек — документ. Одна буква разницы, а значения противоположны |
71
+ | таска, тикет | задача |
72
+ | пул-реквест, мёрдж-реквест | отчёт, а действие — слияние |
73
+ | джоба, пайплайн | конвейер и его шаг |
74
+ | хендофф | передача |
75
+ | бэклог | очередь работ |
76
+ | контекст-виндоу | окно захода, а его доля — заполнение окна |
77
+ | скилл, скилы | правило, паттерн или скил без закона — по тому, что это на самом деле |
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  # rt-hook: PreToolUse Edit|Write|MultiEdit|Bash|mcp__webstorm__create_new_file|mcp__webstorm__execute_terminal_command|mcp__webstorm__execute_tool
3
+ # Требует: hooks/profile-check.sh
3
4
  # Гард пары «правка и её документ». PreToolUse.
4
5
  #
5
6
  # Расхождение кода с текстом беззвучно. Ни линтер, ни сборка, ни тесты не читают правила,
@@ -32,6 +33,16 @@ command -v jq >/dev/null 2>&1 || exit 0
32
33
  tool="$(printf '%s' "$input" | jq -r '.tool_name // empty' 2>/dev/null)"
33
34
 
34
35
  decide() {
36
+ # Наблюдение пишется только на отказе: подсказку гард раздаёт и там, где всё в порядке, и
37
+ # счёт, в котором они смешаны, не значит ничего.
38
+ if [ "$1" = "deny" ]; then
39
+ # shellcheck disable=SC1090
40
+ [ -f "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/observe.sh" ] \
41
+ && . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/observe.sh" 2>/dev/null
42
+ command -v rt_note >/dev/null 2>&1 \
43
+ && rt_note guard-deny res=docs-guard "sid=$(printf '%s' "$input" | jq -r '.session_id // "nosession"' 2>/dev/null)"
44
+ fi
45
+
35
46
  jq -n --arg d "$1" --arg r "$2" \
36
47
  '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:$d,permissionDecisionReason:$r}}' 2>/dev/null \
37
48
  || printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Документ едет тем же коммитом."}}\n'
@@ -47,6 +58,12 @@ for profile in "$rt_hooks_dir/../rt-kit/defaults/project.sh" "$rt_hooks_dir/../d
47
58
  [ -f "$profile" ] && . "$profile" 2>/dev/null
48
59
  done
49
60
 
61
+ # Слово о нехватке функции профиля: хук, вышедший молча, неотличим от работающего. Файл может
62
+ # быть не разложен — тогда остаётся прежнее поведение, молчаливое.
63
+ # shellcheck disable=SC1090
64
+ [ -f "$rt_hooks_dir/profile-check.sh" ] && . "$rt_hooks_dir/profile-check.sh"
65
+ command -v rt_needs >/dev/null 2>&1 || rt_needs() { command -v "$1" >/dev/null 2>&1; }
66
+
50
67
  laws_dir="${RT_LAWS_DIR:-docs/constitution}"
51
68
  lib_marker="${RT_LIB_MARKER:-project.json}"
52
69
 
@@ -170,7 +187,7 @@ done
170
187
  #
171
188
  # Контракт и спек домена, гард и его сценарии — что именно, знает профиль: связь у каждого
172
189
  # дерева своя, а механика одна.
173
- if command -v rt_docs_pair_for >/dev/null 2>&1; then
190
+ if rt_needs rt_docs_pair_for docs-guard; then
174
191
  while IFS= read -r file; do
175
192
  [ -z "$file" ] && continue
176
193
  want="$(rt_docs_pair_for "$file" 2>/dev/null)"
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  # rt-hook: PreToolUse Bash|mcp__webstorm__execute_terminal_command|mcp__webstorm__execute_tool
3
+ # Требует: hooks/profile-check.sh
3
4
  # Гард поставки. PreToolUse на заведении ветки и открытии заявки на слияние.
4
5
  #
5
6
  # Закон о поставке требует трёх вещей, которых обычно не проверяет ничто: правка начинается с
@@ -37,6 +38,7 @@ input="$(cat 2>/dev/null)"
37
38
  command -v jq >/dev/null 2>&1 || exit 0
38
39
 
39
40
  tool="$(printf '%s' "$input" | jq -r '.tool_name // empty' 2>/dev/null)"
41
+ sid="$(printf '%s' "$input" | jq -r '.session_id // "nosession"' 2>/dev/null)"
40
42
  case "$tool" in
41
43
  # Терминал среды и универсальный исполнитель кладут команду в то же поле.
42
44
  Bash | mcp__webstorm__execute_terminal_command | mcp__webstorm__execute_tool) ;;
@@ -73,7 +75,13 @@ for profile in "$rt_hooks_dir/../rt-kit/defaults/project.sh" "$rt_hooks_dir/../d
73
75
  # shellcheck disable=SC1090
74
76
  [ -f "$profile" ] && . "$profile" 2>/dev/null
75
77
  done
76
- command -v rt_task_branch_ok >/dev/null 2>&1 || exit 0
78
+
79
+ # Слово о нехватке функции профиля: хук, вышедший молча, неотличим от работающего. Файл может
80
+ # быть не разложен — тогда остаётся прежнее поведение, молчаливое.
81
+ # shellcheck disable=SC1090
82
+ [ -f "$rt_hooks_dir/profile-check.sh" ] && . "$rt_hooks_dir/profile-check.sh"
83
+ command -v rt_needs >/dev/null 2>&1 || rt_needs() { command -v "$1" >/dev/null 2>&1; }
84
+ rt_needs rt_task_branch_ok git-guard-delivery || exit 0
77
85
 
78
86
  title_re="${RT_TASK_TITLE_RE:-^\[[A-Za-z]+-[0-9]+\][[:space:]]+[^[:space:]]}"
79
87
  task_new="${RT_TASK_NEW_CMD:-npm run task:new}"
@@ -89,6 +97,13 @@ main_branch="${RT_MAIN_BRANCH:-main}"
89
97
  folder_skip_re='Task-folder-skip:[[:space:]]*[^[:space:]"'"'"']{3,}'
90
98
 
91
99
  deny() {
100
+ # Отказ гарда — наблюдение: гард, отбивающий чаще прочих, говорит, какое место поставки
101
+ # раз за разом делают не так. Текст отказа в наблюдение не идёт: в нём стоят номера задач
102
+ # и имена веток этого дерева.
103
+ # shellcheck disable=SC1090
104
+ [ -f "$rt_hooks_dir/observe.sh" ] && . "$rt_hooks_dir/observe.sh" 2>/dev/null
105
+ command -v rt_note >/dev/null 2>&1 && rt_note guard-deny res=git-guard-delivery "sid=$sid"
106
+
92
107
  jq -n --arg r "$1" '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:$r}}' 2>/dev/null \
93
108
  || printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Гард поставки."}}\n'
94
109
  exit 0
@@ -112,7 +127,7 @@ folder_in_branch() {
112
127
  check_task() {
113
128
  number="$1"
114
129
  where="$2"
115
- command -v rt_task_state >/dev/null 2>&1 || return 0
130
+ rt_needs rt_task_state git-guard-delivery || return 0
116
131
  state="$(cd "$root" && rt_task_state "$number" 2>/dev/null)" || return 0
117
132
  [ -z "$state" ] && return 0
118
133
 
@@ -173,7 +188,7 @@ if printf '%s' "$cmd" | grep -qE '(^|[;&|(]|&&|\|\|)[[:space:]]*(gh[[:space:]]+p
173
188
  printf '%s' "$cmd" | grep -qiE "$folder_skip_re" && exit 0
174
189
 
175
190
  merge_number="$(printf '%s' "$cmd" | sed -nE 's/.*(pr|mr)[[:space:]]+(merge|update)[[:space:]]+([0-9]+).*/\3/p' | head -1)"
176
- if [ -n "$merge_number" ] && command -v rt_report_body >/dev/null 2>&1; then
191
+ if [ -n "$merge_number" ] && rt_needs rt_report_body git-guard-delivery; then
177
192
  body="$(cd "$root" && rt_report_body "$merge_number" 2>/dev/null)"
178
193
  [ -n "$body" ] && printf '%s' "$body" | grep -qiE "$folder_skip_re" && exit 0
179
194
  fi
@@ -236,6 +251,18 @@ if [ -n "$title" ]; then
236
251
  fi
237
252
  fi
238
253
 
254
+ # Главная ветка влита до открытия отчёта. Отчёт от разошедшейся ветки показывает ревьюверу свою
255
+ # правку вперемешку с чужой, а проверки на нём гоняются от устаревшего основания.
256
+ #
257
+ # Судится локальная вершина главной ветки, без сети: сетевой вызов в разборе команды падал бы
258
+ # вместе со связью и отбивал бы работу вместо промаха. Отсюда и граница — гард ловит ветку,
259
+ # отставшую заведомо; свежесть самой вершины держит `git fetch`, и требует его чеклист.
260
+ if git rev-parse --verify --quiet "refs/remotes/origin/${main_branch}" >/dev/null 2>&1 \
261
+ && ! git merge-base --is-ancestor "origin/${main_branch}" HEAD 2>/dev/null; then
262
+ behind="$(git rev-list --count "HEAD..origin/${main_branch}" 2>/dev/null)"
263
+ deny "BLOCKED: «${main_branch}» ушла вперёд на ${behind:-несколько} коммитов, а в ветку не влита. Отчёт от разошедшейся ветки показывает ревьюверу правку вперемешку с чужой, а проверки на нём идут от устаревшего основания. Влей и повтори: git fetch origin && git merge origin/${main_branch} — порядок и разбор конфликта в паттерне git-workflow-merge."
264
+ fi
265
+
239
266
  check_task "$number" "заявка с ветки «${branch}»"
240
267
 
241
268
  # Сейчас папка ещё нужна: правки по замечаниям ревью идут в эту же ветку, а без plan.md их не
@@ -67,6 +67,14 @@ fi
67
67
 
68
68
  reason="Отбито: коммит прямо в «${default}». Работа едет через ветку и PR — правило git-workflow. Заведи ветку отдельным вызовом и коммить в неё: подготовленные изменения при этом сохранятся. Если коммит в ${default} действительно нужен — спроси владельца, сам не обходи."
69
69
 
70
+ # Отказ — наблюдение. Имя главной ветки в него не идёт: у деревьев оно своё, а счёт отказов
71
+ # одинаков везде.
72
+ rt_hooks_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
73
+ # shellcheck disable=SC1090
74
+ [ -f "$rt_hooks_dir/observe.sh" ] && . "$rt_hooks_dir/observe.sh" 2>/dev/null
75
+ command -v rt_note >/dev/null 2>&1 \
76
+ && rt_note guard-deny res=git-guard-main "sid=$(printf '%s' "$input" | jq -r '.session_id // "nosession"' 2>/dev/null)"
77
+
70
78
  jq -n --arg r "$reason" '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:$r}}' 2>/dev/null \
71
79
  || printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Коммит в главную ветку отбит. Заведи ветку."}}\n'
72
80
 
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  # rt-hook: PreToolUse Bash|mcp__webstorm__execute_terminal_command|mcp__webstorm__execute_tool
3
+ # Требует: hooks/profile-check.sh
3
4
  # Гард проверок перед пушем. PreToolUse на вызове пуша.
4
5
  #
5
6
  # Пуш — это вход в конвейер: слияние в главную ветку запускает выкатку, и всё, что не
@@ -60,7 +61,13 @@ for profile in "$rt_hooks_dir/../rt-kit/defaults/project.sh" "$rt_hooks_dir/../d
60
61
  # shellcheck disable=SC1090
61
62
  [ -f "$profile" ] && . "$profile" 2>/dev/null
62
63
  done
63
- command -v rt_push_checks >/dev/null 2>&1 || exit 0
64
+
65
+ # Слово о нехватке функции профиля: хук, вышедший молча, неотличим от работающего. Файл может
66
+ # быть не разложен — тогда остаётся прежнее поведение, молчаливое.
67
+ # shellcheck disable=SC1090
68
+ [ -f "$rt_hooks_dir/profile-check.sh" ] && . "$rt_hooks_dir/profile-check.sh"
69
+ command -v rt_needs >/dev/null 2>&1 || rt_needs() { command -v "$1" >/dev/null 2>&1; }
70
+ rt_needs rt_push_checks git-guard-push-tests || exit 0
64
71
 
65
72
  main_branch="${RT_MAIN_BRANCH:-main}"
66
73
  base=''