agent-quality-kit 0.11.0 → 0.13.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/README.md +28 -4
- package/README.ru.md +28 -4
- package/kit/gates/complexity-limit/gate.yml +9 -0
- package/kit/gates/dead-code/gate.yml +9 -0
- package/kit/gates/duplicate-code/gate.yml +5 -0
- package/kit/gates/env-secrets-not-committed/README.md +73 -0
- package/kit/gates/env-secrets-not-committed/check.sh +139 -0
- package/kit/gates/env-secrets-not-committed/gate.yml +21 -0
- package/kit/gates/env-secrets-not-committed/green/.aqk-tracked +10 -0
- package/kit/gates/env-secrets-not-committed/green/.env +10 -0
- package/kit/gates/env-secrets-not-committed/green/.env.production +5 -0
- package/kit/gates/env-secrets-not-committed/green/.env.test +2 -0
- package/kit/gates/env-secrets-not-committed/red/.aqk-tracked +5 -0
- package/kit/gates/env-secrets-not-committed/red/.env +7 -0
- package/kit/gates/no-print-in-prod/gate.yml +9 -0
- package/kit/gates/secrets-not-in-code/gate.yml +20 -0
- package/kit/gates/swallowed-error/gate.yml +9 -0
- package/kit/gates/todo-without-task/gate.yml +9 -0
- package/llms.txt +4 -2
- package/package.json +1 -1
- package/tool/commands/badge.mjs +1 -1
- package/tool/commands/context.mjs +59 -4
- package/tool/commands/doctor.mjs +171 -44
- package/tool/commands/gates.mjs +3 -2
- package/tool/commands/probe.mjs +86 -22
- package/tool/commands/project.mjs +6 -1
- package/tool/commands/report.mjs +1 -1
- package/tool/commands/vitals.mjs +21 -12
- package/tool/i18n/en-docs.mjs +24 -2
- package/tool/i18n/en-gates.mjs +10 -1
- package/tool/i18n/en.mjs +25 -0
- package/tool/i18n/ru-docs.mjs +26 -2
- package/tool/i18n/ru-gates.mjs +10 -1
- package/tool/i18n/ru.mjs +25 -0
- package/tool/lib/adopt.mjs +112 -0
- package/tool/lib/advice.mjs +115 -0
- package/tool/lib/brief.mjs +3 -1
- package/tool/lib/cadence.mjs +46 -1
- package/tool/lib/core.mjs +39 -1
- package/tool/lib/execution.mjs +50 -1
- package/tool/lib/gate-worker.mjs +18 -0
- package/tool/lib/history.mjs +45 -6
- package/tool/lib/manifest.mjs +65 -21
- package/tool/lib/prove.mjs +2 -2
- package/tool/lib/repo.mjs +18 -0
- package/tool/lib/run.mjs +108 -8
- package/tool/selfcheck/gates.sh +12 -2
- package/tool/selfcheck/smoke/first-run.test.mjs +105 -0
- package/tool/selfcheck/smoke/verdict.test.mjs +51 -2
- package/tool/selfcheck/smoke.sh +6 -2
- package/tool/selfcheck/units-cadence.mjs +51 -1
- package/tool/selfcheck/units-context.mjs +64 -1
- package/tool/selfcheck/units-execution.mjs +78 -1
- package/tool/selfcheck/units-level.mjs +99 -1
- package/tool/selfcheck/units-probe.mjs +104 -0
- package/tool/selfcheck/units-repo.mjs +146 -0
- package/tool/selfcheck/units-verdict.mjs +76 -0
- package/tool/selfcheck/units.mjs +34 -1
package/README.md
CHANGED
|
@@ -46,6 +46,8 @@ Go, Rust, Java, Ruby, PHP, C#, Kotlin, Swift, Scala. Where the project already h
|
|
|
46
46
|
says so out loud when it falls back.
|
|
47
47
|
|
|
48
48
|
**Requirements:** Node 18+ and an `sh` shell. Present on macOS, Linux and WSL; Git Bash on Windows.
|
|
49
|
+
On Windows the gates run through Git Bash even when `bash` on your PATH is the WSL stub in
|
|
50
|
+
`System32` — it is found next to `git.exe`; set `AQK_BASH` to point elsewhere.
|
|
49
51
|
|
|
50
52
|
### One movement, and everything follows from it
|
|
51
53
|
|
|
@@ -100,11 +102,12 @@ aqk badge write the level badge into the README
|
|
|
100
102
|
aqk badge --check fail if the badge disagrees with a run
|
|
101
103
|
|
|
102
104
|
aqk context the repository state in one block, for an agent's context:
|
|
103
|
-
level, what is red now, rules nobody enforces, ratchets
|
|
105
|
+
level, what is red now, rules nobody enforces, ratchets,
|
|
106
|
+
the next three steps with commands, and when to run what
|
|
104
107
|
aqk vitals is what the kit runs on wired up: gate tools, hooks, freshness
|
|
105
108
|
aqk doctor --run --brief one line on success, the whole run on failure — for hooks
|
|
106
109
|
aqk context --full the same plus the command map and the rulebook verbatim (~7000
|
|
107
|
-
tokens against ~
|
|
110
|
+
tokens against ~500: the price of an agent that does not guess)
|
|
108
111
|
aqk context --install put a SessionStart hook into .claude/settings.json
|
|
109
112
|
(add --full to install the full block)
|
|
110
113
|
|
|
@@ -345,7 +348,7 @@ Already using [pre-commit](https://pre-commit.com)? Three lines in the file you
|
|
|
345
348
|
```yaml
|
|
346
349
|
repos:
|
|
347
350
|
- repo: https://github.com/arsen-ask-lx/Agent_Quality_Kit
|
|
348
|
-
rev: v0.
|
|
351
|
+
rev: v0.13.0
|
|
349
352
|
hooks:
|
|
350
353
|
- id: aqk # runs what the repository declares; blocks below AQK-1
|
|
351
354
|
# - id: aqk-doctor # read-only: the level and what is missing, blocks nothing
|
|
@@ -366,7 +369,7 @@ layer AQK adds.
|
|
|
366
369
|
[](https://github.com/marketplace/actions/agent-quality-kit-aqk)
|
|
367
370
|
|
|
368
371
|
```yaml
|
|
369
|
-
- uses: arsen-ask-lx/Agent_Quality_Kit@v0.
|
|
372
|
+
- uses: arsen-ask-lx/Agent_Quality_Kit@v0.13.0
|
|
370
373
|
with:
|
|
371
374
|
min: 1 # the build fails below AQK-1, or if any declared gate failed
|
|
372
375
|
```
|
|
@@ -454,6 +457,27 @@ Otherwise a gate that cannot fail the build looks exactly like one that can, and
|
|
|
454
457
|
is in `advisory:` only on the day it goes red. Red ones are additionally named by name in the
|
|
455
458
|
summary: an advisory gate everyone forgot about is a switched-off check.
|
|
456
459
|
|
|
460
|
+
**A gate that needs a live stack** (a cost check against a seeded database, e2e) goes into a group,
|
|
461
|
+
and a fast run skips the group:
|
|
462
|
+
|
|
463
|
+
```yaml
|
|
464
|
+
groups:
|
|
465
|
+
stack: [cost, e2e]
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
```bash
|
|
469
|
+
aqk doctor --run --skip stack # everything except the stack
|
|
470
|
+
aqk doctor --run --only stack # only the stack, on the stand
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
Skipped gates are named in the output and written to the run report as "not run" — not as green.
|
|
474
|
+
An unknown name after `--skip` is a refusal: a typo must not quietly mean "skipped nothing".
|
|
475
|
+
|
|
476
|
+
**Independent gates can run in parallel:** `aqk doctor --run --jobs 4`. Off by default, on
|
|
477
|
+
purpose: two gates writing into the same folder (`npm run build` twice into `dist/`) would fail
|
|
478
|
+
at random when run together, and a flaky red is worse than a slow one. Output keeps the declared
|
|
479
|
+
order. On this repository, 30 gates: about 110 s one by one, 68 s with `--jobs 3`.
|
|
480
|
+
|
|
457
481
|
## When a bug slips past the guards
|
|
458
482
|
|
|
459
483
|
```bash
|
package/README.ru.md
CHANGED
|
@@ -48,6 +48,8 @@ TypeScript, Go, Rust, Java, Ruby, PHP, C#, Kotlin, Swift, Scala. Там, где
|
|
|
48
48
|
сообщает, когда откатилась на переносимый.
|
|
49
49
|
|
|
50
50
|
**Требуется:** Node 18+ и оболочка `sh`. Есть на macOS, Linux и в WSL; на Windows — Git Bash.
|
|
51
|
+
На Windows гейты идут через Git Bash, даже если `bash` в PATH — заглушка WSL из `System32`:
|
|
52
|
+
он ищется рядом с `git.exe`; другой путь задаётся `AQK_BASH`.
|
|
51
53
|
|
|
52
54
|
### Одно движение, из которого следует всё остальное
|
|
53
55
|
|
|
@@ -102,11 +104,12 @@ aqk badge вписать значок уровня в README
|
|
|
102
104
|
aqk badge --check упасть, если значок расходится с прогоном
|
|
103
105
|
|
|
104
106
|
aqk context состояние репозитория одним блоком, для контекста агента:
|
|
105
|
-
уровень, что красное сейчас, правила без арбитра,
|
|
107
|
+
уровень, что красное сейчас, правила без арбитра, храповики,
|
|
108
|
+
три следующих шага с командами и когда что запускать
|
|
106
109
|
aqk vitals подключено ли то, чем комплект работает: инструменты, хуки, свежесть
|
|
107
110
|
aqk doctor --run --brief одна строка на успехе, весь прогон при провале — для хуков
|
|
108
111
|
aqk context --full то же плюс карта команд и свод правил дословно (≈7000 токенов
|
|
109
|
-
против ≈
|
|
112
|
+
против ≈500 — плата за то, чтобы агент не догадывался)
|
|
110
113
|
aqk context --install поставить хук SessionStart в .claude/settings.json
|
|
111
114
|
(с --full ставится полный блок)
|
|
112
115
|
|
|
@@ -348,7 +351,7 @@ aqk badge --check # в конвейере: код 1 в тот день, ког
|
|
|
348
351
|
```yaml
|
|
349
352
|
repos:
|
|
350
353
|
- repo: https://github.com/arsen-ask-lx/Agent_Quality_Kit
|
|
351
|
-
rev: v0.
|
|
354
|
+
rev: v0.13.0
|
|
352
355
|
hooks:
|
|
353
356
|
- id: aqk # запускает объявленное; роняет коммит ниже AQK-1
|
|
354
357
|
# - id: aqk-doctor # только осмотр: уровень и чего не хватает, ничего не роняет
|
|
@@ -367,7 +370,7 @@ repos:
|
|
|
367
370
|
[](https://github.com/marketplace/actions/agent-quality-kit-aqk)
|
|
368
371
|
|
|
369
372
|
```yaml
|
|
370
|
-
- uses: arsen-ask-lx/Agent_Quality_Kit@v0.
|
|
373
|
+
- uses: arsen-ask-lx/Agent_Quality_Kit@v0.13.0
|
|
371
374
|
with:
|
|
372
375
|
min: 1 # сборка падает ниже AQK-1 или если упал любой объявленный гейт
|
|
373
376
|
```
|
|
@@ -453,6 +456,27 @@ advisory:
|
|
|
453
456
|
списке `advisory:` человек узнаёт только в день, когда гейт покраснел. Покрасневшие вдобавок
|
|
454
457
|
названы в сводке поимённо: совещательный гейт, о котором забыли, — это выключенная проверка.
|
|
455
458
|
|
|
459
|
+
**Гейт, которому нужен живой стенд** (цена на засеянной базе, e2e), кладётся в группу, и быстрый
|
|
460
|
+
прогон группу пропускает:
|
|
461
|
+
|
|
462
|
+
```yaml
|
|
463
|
+
groups:
|
|
464
|
+
stack: [cost, e2e]
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
aqk doctor --run --skip stack # всё, кроме стенда
|
|
469
|
+
aqk doctor --run --only stack # только стенд, на стенде
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
Пропущенные названы в выводе и записаны в отчёт прогона как «не запускались» — не как зелёные.
|
|
473
|
+
Неизвестное имя после `--skip` — отказ: опечатка не должна тихо означать «пропустили ничего».
|
|
474
|
+
|
|
475
|
+
**Независимые гейты можно гнать параллельно:** `aqk doctor --run --jobs 4`. По умолчанию
|
|
476
|
+
выключено намеренно: два гейта, пишущие в одну папку (`npm run build` дважды в `dist/`), вместе
|
|
477
|
+
падали бы через раз, а плавающее красное хуже медленного. Вывод — в порядке объявления. На этом
|
|
478
|
+
репозитории, 30 гейтов: около 110 с по одному, 68 с с `--jobs 3`.
|
|
479
|
+
|
|
456
480
|
|
|
457
481
|
## Поймал ошибку, которую не поймал сторож
|
|
458
482
|
|
|
@@ -4,6 +4,11 @@ intent_en: a function does not grow past the complexity you can hold in your hea
|
|
|
4
4
|
trigger:
|
|
5
5
|
always: true
|
|
6
6
|
|
|
7
|
+
# Домашние страницы готовых инструментов, которые зовут рецепты ниже. Адреса сверены
|
|
8
|
+
# по реестру github 2026-09-10, а не написаны по памяти: три выдуманных адреса чужих
|
|
9
|
+
# репозиториев — записанная шишка комплекта.
|
|
10
|
+
tool: https://github.com/astral-sh/ruff · https://github.com/eslint/eslint
|
|
11
|
+
|
|
7
12
|
recipes:
|
|
8
13
|
# Переносимая мера грубая — глубина вложенности. Готовые правила считают ветвления и
|
|
9
14
|
# инструкции, это точнее; см. kit/docs/ready-made-rules.md
|
|
@@ -12,3 +17,7 @@ recipes:
|
|
|
12
17
|
typescript: eslint --no-config-lookup --ignore-pattern 'gates/*/red/**' --ignore-pattern 'gates/*/green/**' --rule '{"complexity":["error",10],"max-depth":["error",4]}' {dir}
|
|
13
18
|
|
|
14
19
|
proof: incidents/README.md — «2026-08-27 ревизия гейтов», п. 6: 75 функций сложнее нормы, худшая с 29 ветвлениями
|
|
20
|
+
|
|
21
|
+
# Правило Biome под эту запись — для сверки `covers:` (рецептов под Biome нет). Имя сверено по
|
|
22
|
+
# схеме конфигурации Biome 2.5.12, а не по памяти.
|
|
23
|
+
biome_rules: complexity/noExcessiveCognitiveComplexity
|
|
@@ -10,6 +10,11 @@ trigger:
|
|
|
10
10
|
# Переносимого рецепта здесь нет намеренно: чтобы понять, вызывают ли функцию, нужен граф
|
|
11
11
|
# вызовов, а не поиск по тексту. Это тот случай, когда без готового инструмента не обойтись —
|
|
12
12
|
# и он есть под каждый распространённый стек.
|
|
13
|
+
# Домашние страницы готовых инструментов, которые зовут рецепты ниже. Адреса сверены
|
|
14
|
+
# по реестру github 2026-09-10, а не написаны по памяти: три выдуманных адреса чужих
|
|
15
|
+
# репозиториев — записанная шишка комплекта.
|
|
16
|
+
tool: https://github.com/jendrikseipp/vulture · https://github.com/webpro-nl/knip
|
|
17
|
+
|
|
13
18
|
recipes:
|
|
14
19
|
python: vulture --min-confidence 60 {dir}
|
|
15
20
|
typescript: npx --yes knip@6 --directory {dir}
|
|
@@ -22,3 +27,7 @@ recipes:
|
|
|
22
27
|
samples_for: python
|
|
23
28
|
|
|
24
29
|
proof: incidents/README.md — «2026-08-27 ревизия гейтов», п. 5: мёртвые функции бэкенда не искал никто, реестр на 148 записей
|
|
30
|
+
|
|
31
|
+
# Правило Biome под эту запись — для сверки `covers:` (рецептов под Biome нет). Имя сверено по
|
|
32
|
+
# схеме конфигурации Biome 2.5.12, а не по памяти.
|
|
33
|
+
biome_rules: correctness/noUnusedVariables, correctness/noUnusedImports
|
|
@@ -4,6 +4,11 @@ intent_en: the same code does not multiply into copies across the project
|
|
|
4
4
|
trigger:
|
|
5
5
|
files_gt: 20
|
|
6
6
|
|
|
7
|
+
# Домашние страницы готовых инструментов, которые зовут рецепты ниже. Адреса сверены
|
|
8
|
+
# по реестру github 2026-09-10, а не написаны по памяти: три выдуманных адреса чужих
|
|
9
|
+
# репозиториев — записанная шишка комплекта.
|
|
10
|
+
tool: https://github.com/kucherenko/jscpd · https://github.com/pylint-dev/pylint
|
|
11
|
+
|
|
7
12
|
recipes:
|
|
8
13
|
any: bash {gate}/check.sh {dir}
|
|
9
14
|
# jscpd умеет и Python, и TypeScript одним прогоном и считает похожесть, а не совпадение.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Пароли из .env не лежат в git
|
|
2
|
+
|
|
3
|
+
**Намерение.** Файл окружения с настоящими значениями, попав в git, раздаёт пароли и ключи
|
|
4
|
+
каждому, кто склонировал репозиторий, — и из истории их уже не вычистить: ключ считается
|
|
5
|
+
скомпрометированным, его остаётся только сменить.
|
|
6
|
+
|
|
7
|
+
**Почему не «любой `.env` в git — брак».** Так было бы проще, и так было бы неверно. Сверено по
|
|
8
|
+
первоисточникам 2026-09-11:
|
|
9
|
+
|
|
10
|
+
- dotenv: «Should I commit my `.env` file? No. Unless you encrypt it with dotenvx. Then we
|
|
11
|
+
recommend you do» — зашифрованный файл коммитят намеренно;
|
|
12
|
+
- Next.js: «`.env.test` file should be included in your repository, but `.env.test.local`
|
|
13
|
+
shouldn't, as `.env*.local` are intended to be ignored through `.gitignore`»;
|
|
14
|
+
- Vite: «`.env.*.local` files are local-only and can contain sensitive variables. You should add
|
|
15
|
+
`*.local` to your `.gitignore`» — про обычный `.env` не сказано ничего, и во многих проектах
|
|
16
|
+
его коммитят с публичными настройками.
|
|
17
|
+
|
|
18
|
+
Поэтому правил два:
|
|
19
|
+
|
|
20
|
+
1. **`.env.local` и `.env.*.local` в git — находка всегда.** Здесь все сходятся: «local» — только
|
|
21
|
+
для этой машины, что бы в файле ни лежало.
|
|
22
|
+
2. **Остальные `.env*` в git — находка, если внутри ключ-секрет с настоящим значением:** имя
|
|
23
|
+
содержит `PASSWORD`, `SECRET`, `TOKEN`, `API_KEY`, `PRIVATE_KEY`, `ACCESS_KEY`, `CREDENTIAL`,
|
|
24
|
+
либо значение — адрес с паролем (`схема://пользователь:пароль@хост`).
|
|
25
|
+
|
|
26
|
+
**Значение секрета проверка не печатает.** Вывод гейта попадает в лог конвейера; напечатать
|
|
27
|
+
пароль значило бы завести вторую утечку ради сообщения о первой. Называются файл, строка, ключ.
|
|
28
|
+
|
|
29
|
+
**Готовый аналог — есть частично, и мы его замерили.** Закоммиченный `.env` с тремя секретами и
|
|
30
|
+
`.env.local` с токеном (2026-09-11):
|
|
31
|
+
|
|
32
|
+
| | пароль, придуманный человеком | случайная строка | ключ с форматом поставщика | `.env.local` |
|
|
33
|
+
|---|---|---|---|---|
|
|
34
|
+
| наш `secrets-not-in-code` | не видит | не видит | видит | не видит |
|
|
35
|
+
| `gitleaks` 8.30.1 | не видит | видит (по энтропии) | видит | не видит |
|
|
36
|
+
| [`detect-secrets`](https://github.com/Yelp/detect-secrets) 1.5.0 | видит | видит | видит | не видит |
|
|
37
|
+
| эта запись | видит | видит | видит, если имя ключа говорящее | видит |
|
|
38
|
+
|
|
39
|
+
`detect-secrets` сильнее нас во втором правиле: он читает имена ключей во всех файлах, а не
|
|
40
|
+
только в `.env`. Если он у вас уже стоит в pre-commit с файлом-базой — второе правило закрыто
|
|
41
|
+
им, первое остаётся за этой записью. Родным рецептом он не стал: в конвейере ему нужна база
|
|
42
|
+
(`.secrets.baseline`), а запись ставится одной командой.
|
|
43
|
+
|
|
44
|
+
**Чего НЕ ловит.**
|
|
45
|
+
|
|
46
|
+
- **Секрет под неговорящим именем.** `STRIPE=…`, `DB=…`, `X_CONF=…` — ключ не называет себя
|
|
47
|
+
секретом. Такое ловит только сканер по формату значения (`secrets-not-in-code`, `gitleaks`).
|
|
48
|
+
- **Короткий пароль.** Значения короче восьми знаков пропускаются: `dev`, `x`, `1234` в `.env`
|
|
49
|
+
для локального стенда встречаются куда чаще настоящих паролей такой длины.
|
|
50
|
+
- **Пароль, совпавший со значением по умолчанию.** `postgres`, `root`, `admin`, `guest`,
|
|
51
|
+
`minioadmin` — стоят в каждом docker-compose для разработки, и считать их утечкой значит
|
|
52
|
+
краснеть на каждом втором проекте. Если такой пароль стоит на проде, это другая беда.
|
|
53
|
+
- **Публичные переменные сборки.** `NEXT_PUBLIC_`, `VITE_`, `REACT_APP_`, `EXPO_PUBLIC_`,
|
|
54
|
+
`NUXT_PUBLIC_`, `GATSBY_` уезжают в браузер по устройству — секретом они быть не могут.
|
|
55
|
+
- **Шаблоны и тестовые данные.** `.env.example`, `.env.sample`, `.env.template`, `.env.dist`,
|
|
56
|
+
`.env.test`, а также каталоги `templates/`, `examples/`, `fixtures/`, `testdata/`.
|
|
57
|
+
- **`.env.local` в каталоге тестов и демо.** `tests/`, `test/`, `__tests__/`, `spec/`,
|
|
58
|
+
`playgrounds/`, `demos/` — там это фикстура: библиотека показывает или проверяет, что умеет
|
|
59
|
+
читать такой файл. Замер: оба ложных срабатывания были ровно такими. Пароль внутри тестового
|
|
60
|
+
`.env` при этом ловится — утечка в тестах остаётся утечкой.
|
|
61
|
+
- **Историю.** Проверяется то, что лежит в git сейчас. Файл, удалённый из индекса, из истории не
|
|
62
|
+
исчез — это лечится сменой ключа, а не проверкой.
|
|
63
|
+
- **Файл, который лежит на диске и в `.gitignore`.** Это норма, а не находка: проверка спрашивает
|
|
64
|
+
git, а не диск. (Ошибка соседа, agentlint, ровно в этом: он проверяет диск и кричит
|
|
65
|
+
«закоммичен» про файл из `.gitignore`.)
|
|
66
|
+
|
|
67
|
+
**Образцы.** Настоящего git внутри каталога комплекта взять неоткуда, поэтому образцы кладут
|
|
68
|
+
список отслеживаемых файлов в `.aqk-tracked`; в живом проекте его не бывает и спрашивается git.
|
|
69
|
+
`red/` — `.env` с человеческими паролями (без формата и энтропии — ровно то, что сканеры
|
|
70
|
+
пропускают) и `.env.local` в индексе. `green/` — `.env` с публичными переменными сборки,
|
|
71
|
+
заглушками, ссылками `${…}`, паролями по умолчанию для локального стенда; `.env.production`,
|
|
72
|
+
зашифрованный `dotenvx`; `.env.test` с тестовым паролем; `.env.local` в `tests/`; `.env` в
|
|
73
|
+
`tests/fixtures/` и `templates/`. На всём этом арбитр обязан молчать.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
# Файл окружения с настоящими значениями лежит в git: пароль, токен или ключ разошёлся со всеми
|
|
3
|
+
# клонами — и из истории его уже не вычистить.
|
|
4
|
+
#
|
|
5
|
+
# ЗАЧЕМ СВОЯ ПРОВЕРКА, если есть сканеры секретов. Замер 2026-09-11 на закоммиченном `.env`
|
|
6
|
+
# (research/competitors/agentlint.md): наш `secrets-not-in-code` узнаёт секрет только по формату
|
|
7
|
+
# поставщика (`sk_live_…`); `gitleaks` 8.30.1 — по формату и по ЭНТРОПИИ: случайную строку
|
|
8
|
+
# (`wN7rQ2vLx9Tp`, энтропия 3,6) он ловит, а пароль, придуманный человеком (`Sup3rS3cret2024`,
|
|
9
|
+
# `hunter2hunter2`), пропускает. У такого пароля нет ни формата, ни энтропии — его узнают
|
|
10
|
+
# только по ИМЕНИ ключа. `detect-secrets` 1.5.0 имя ключа читает и оба ловит, но `.env.local`
|
|
11
|
+
# не видит вовсе: такой файл личный по определению, что бы в нём ни лежало.
|
|
12
|
+
#
|
|
13
|
+
# ДВА ПРАВИЛА, и второе не шире, чем нужно.
|
|
14
|
+
# 1. `.env.local` и `.env.*.local` в git — находка всегда. Документация Vite: «.env.*.local
|
|
15
|
+
# files are local-only and can contain sensitive variables. You should add `*.local` to your
|
|
16
|
+
# `.gitignore`»; Next.js: «.env*.local are intended to be ignored through .gitignore».
|
|
17
|
+
# 2. Остальные `.env*` в git — находка, только если внутри ключ-секрет с НАСТОЯЩИМ значением.
|
|
18
|
+
# Не любой `.env`: Next.js велит коммитить `.env.test`, во многих проектах на Vite
|
|
19
|
+
# коммитят `.env` с публичными настройками, а зашифрованный `dotenvx` файл коммитить
|
|
20
|
+
# рекомендует сам dotenv («Should I commit my .env file? No. Unless you encrypt it with
|
|
21
|
+
# dotenvx»). Проверка «файл есть в git» без чтения значений врала бы на всех троих.
|
|
22
|
+
#
|
|
23
|
+
# ЗНАЧЕНИЕ НЕ ПЕЧАТАЕТСЯ. Вывод гейта попадает в лог конвейера; напечатать пароль значило бы
|
|
24
|
+
# завести вторую утечку ради сообщения о первой. Называются файл, строка и имя ключа.
|
|
25
|
+
DIR="${1:-.}"
|
|
26
|
+
# Существование файла проверяется ДО `.`: под dash неудачный `.` завершает скрипт немедленно, и
|
|
27
|
+
# запасная ветка не выполняется никогда (замерено 2026-09-08 на соседней записи).
|
|
28
|
+
SKIP_LIB="$(dirname "$0")/../_skip.sh"
|
|
29
|
+
if [ ! -f "$SKIP_LIB" ]; then
|
|
30
|
+
echo "рядом с проверкой нет _skip.sh — обход не собран, проверка не состоялась"
|
|
31
|
+
echo " почини: скопируй гейт вместе с файлом kit/gates/_skip.sh, он общий на весь каталог"
|
|
32
|
+
exit 2
|
|
33
|
+
fi
|
|
34
|
+
. "$SKIP_LIB"
|
|
35
|
+
if [ "${AQK_SKIP_READY:-}" != 1 ]; then
|
|
36
|
+
echo "_skip.sh есть, но обход не собрался — проверка НЕ СОСТОЯЛАСЬ, а не прошла"
|
|
37
|
+
echo " почини: замени kit/gates/_skip.sh целым файлом из каталога"
|
|
38
|
+
exit 2
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Образцы (gates.sh) несут список отслеживаемых файлов в `.aqk-tracked`: вложенный .git внутри
|
|
42
|
+
# каталога комплекта создал бы embedded-репозиторий. В настоящем проекте спрашиваем git.
|
|
43
|
+
if [ -f "$DIR/.aqk-tracked" ]; then
|
|
44
|
+
LIST=$(cat "$DIR/.aqk-tracked")
|
|
45
|
+
else
|
|
46
|
+
if ! (cd "$DIR" 2>/dev/null && git rev-parse --git-dir >/dev/null 2>&1); then
|
|
47
|
+
echo "не git-репозиторий — закоммитить сюда нечего"
|
|
48
|
+
exit 0
|
|
49
|
+
fi
|
|
50
|
+
# core.quotePath=false: иначе путь с не-ASCII приходит в кавычках, и якорь «$» не совпадает.
|
|
51
|
+
LIST=$(cd "$DIR" && git -c core.quotePath=false ls-files 2>/dev/null)
|
|
52
|
+
fi
|
|
53
|
+
LIST=$(printf '%s\n' "$LIST" | tr -d '\r')
|
|
54
|
+
|
|
55
|
+
# Кандидаты — файлы окружения по имени. Шаблоны (`.example`, `.sample`, `.template`, `.dist`) и
|
|
56
|
+
# каталоги заготовок и тестовых данных — не утечка: их кладут намеренно, чтобы скопировать.
|
|
57
|
+
# Замер на соседней записи: больше половины совпадений по имени были такими.
|
|
58
|
+
ENVS=$(printf '%s\n' "$LIST" \
|
|
59
|
+
| grep -E '(^|/)\.env(\.[A-Za-z0-9_.-]+)?$' \
|
|
60
|
+
| grep -vE '\.(example|sample|template|dist|schema)$' \
|
|
61
|
+
| grep -vE '(^|/)(templates?|examples?|samples?|fixtures?|__fixtures__|testdata|test_data|dist)/' \
|
|
62
|
+
| grep -vE '(^|/)kit/gates/[^/]+/(red|green)(/|$)' \
|
|
63
|
+
|| true)
|
|
64
|
+
if command -v own_samples_filter >/dev/null 2>&1 || type own_samples_filter >/dev/null 2>&1; then
|
|
65
|
+
ENVS=$(printf '%s\n' "$ENVS" | own_samples_filter "$DIR" | grep -v '^$' || true)
|
|
66
|
+
fi
|
|
67
|
+
[ -z "$ENVS" ] && exit 0
|
|
68
|
+
|
|
69
|
+
# Секрет узнаётся по имени ключа и по значению. Ложные — это то, что в git класть МОЖНО:
|
|
70
|
+
# · публичные по устройству переменные сборки (`NEXT_PUBLIC_`, `VITE_`, `REACT_APP_`, …) —
|
|
71
|
+
# они и так уезжают в браузер;
|
|
72
|
+
# · пустое значение, ссылка `${VAR}`/`$VAR`, заглушка (`changeme`, `<…>`, `your_…`);
|
|
73
|
+
# · значения по умолчанию для локального стенда (`postgres`, `root`, `admin`, `guest`…) —
|
|
74
|
+
# тот же пароль стоит в каждом docker-compose для разработки;
|
|
75
|
+
# · зашифрованное `dotenvx` (`encrypted:…`); числа и логические (`TOKEN_TTL=86400`);
|
|
76
|
+
# · короче восьми знаков — настоящий пароль короче почти не бывает, а «dev» и «x» бывают.
|
|
77
|
+
# Интервалы `{n}` в регулярках НЕ использовать: mawk из node:22-slim их не понимает, и две
|
|
78
|
+
# записи каталога однажды молчали на своих красных образцах ровно из-за этого.
|
|
79
|
+
# Построчно, а не `for F in $ENVS`: путь с пробелом развалился бы на части. Цикл в конвейере
|
|
80
|
+
# идёт в подоболочке, поэтому находки собираются в вывод, а не в переменную-флаг.
|
|
81
|
+
OUT=$(printf '%s\n' "$ENVS" | while IFS= read -r F; do
|
|
82
|
+
BASE=${F##*/}
|
|
83
|
+
case "$BASE" in
|
|
84
|
+
.env.local|.env.*.local)
|
|
85
|
+
# В каталоге тестов и демо-приложений `.env.local` — фикстура: библиотека показывает или
|
|
86
|
+
# проверяет, что умеет его читать. Замер 2026-09-11: `tests/.env.local` у motdotla/dotenv
|
|
87
|
+
# и `playgrounds/*/.env.local` у инструментов для Next.js — обе находки ложные. Правило 2
|
|
88
|
+
# (настоящий пароль внутри) там действует: утечка в тестах остаётся утечкой.
|
|
89
|
+
if printf '%s\n' "$F" | grep -qE '(^|/)(tests?|__tests__|specs?|playgrounds?|demos?)/'; then :; else
|
|
90
|
+
echo "$F: личный файл окружения отслеживается git — «.local» по определению только для этой машины"
|
|
91
|
+
continue
|
|
92
|
+
fi ;;
|
|
93
|
+
# Next.js: «.env.test file should be included in your repository».
|
|
94
|
+
.env.test|.env.testing) continue ;;
|
|
95
|
+
esac
|
|
96
|
+
[ -f "$DIR/$F" ] || continue
|
|
97
|
+
HITS=$(tr -d '\r' < "$DIR/$F" | awk '
|
|
98
|
+
function placeholder(v, l) {
|
|
99
|
+
l = tolower(v)
|
|
100
|
+
if (l == "") return 1
|
|
101
|
+
if (l ~ /^encrypted:/) return 1
|
|
102
|
+
if (l ~ /^\$/) return 1
|
|
103
|
+
if (l ~ /^<.*>$/) return 1
|
|
104
|
+
if (l ~ /^(your|example|dummy|placeholder|replace|change)[-_ a-z0-9]*$/) return 1
|
|
105
|
+
if (l ~ /^(changeme|todo|none|null|secret|password|test|dev|development|local|localhost)$/) return 1
|
|
106
|
+
if (l ~ /^(postgres|mysql|root|admin|guest|redis|minioadmin|user|pass)$/) return 1
|
|
107
|
+
if (l ~ /^x+$/ || l ~ /^\*+$/ || l ~ /^\.+$/) return 1
|
|
108
|
+
if (l ~ /^[0-9]+$/ || l ~ /^(true|false|yes|no|on|off)$/) return 1
|
|
109
|
+
return 0
|
|
110
|
+
}
|
|
111
|
+
/^[ \t]*#/ || /^[ \t]*$/ { next }
|
|
112
|
+
{
|
|
113
|
+
line = $0
|
|
114
|
+
sub(/^[ \t]*export[ \t]+/, "", line)
|
|
115
|
+
eq = index(line, "=")
|
|
116
|
+
if (eq == 0) next
|
|
117
|
+
key = substr(line, 1, eq - 1); val = substr(line, eq + 1)
|
|
118
|
+
gsub(/^[ \t]+|[ \t]+$/, "", key); gsub(/^[ \t]+|[ \t]+$/, "", val)
|
|
119
|
+
sub(/[ \t]+#.*$/, "", val)
|
|
120
|
+
if (val ~ /^".*"$/ || val ~ /^\047.*\047$/) val = substr(val, 2, length(val) - 2)
|
|
121
|
+
k = toupper(key)
|
|
122
|
+
if (k ~ /^(NEXT_PUBLIC_|VITE_|REACT_APP_|PUBLIC_|EXPO_PUBLIC_|NUXT_PUBLIC_|GATSBY_)/) next
|
|
123
|
+
# Пароль внутри адреса: схема://пользователь:пароль@хост.
|
|
124
|
+
if (match(val, /:\/\/[^\/:@ ]+:[^\/@ ]+@/)) {
|
|
125
|
+
cred = substr(val, RSTART + 3, RLENGTH - 4); pw = substr(cred, index(cred, ":") + 1)
|
|
126
|
+
if (!placeholder(pw)) { print NR ": " key " — пароль внутри адреса"; next }
|
|
127
|
+
}
|
|
128
|
+
if (k !~ /(PASSWORD|PASSWD|SECRET|TOKEN|API_?KEY|PRIVATE_?KEY|ACCESS_?KEY|CREDENTIAL)/) next
|
|
129
|
+
if (placeholder(val) || length(val) < 8) next
|
|
130
|
+
print NR ": " key " — похоже на настоящее значение"
|
|
131
|
+
}')
|
|
132
|
+
[ -n "$HITS" ] && printf '%s\n' "$HITS" | sed "s|^|$F:|; s|\$| (само значение не печатаю)|"
|
|
133
|
+
done)
|
|
134
|
+
|
|
135
|
+
[ -z "$OUT" ] && exit 0
|
|
136
|
+
printf '%s\n' "$OUT"
|
|
137
|
+
echo " почини: git rm --cached <файл> и добавь его в .gitignore; в git оставь .env.example с пустыми"
|
|
138
|
+
echo " значениями. И смени эти пароли и ключи: из истории git их уже не вычистить."
|
|
139
|
+
exit 1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
intent: личные файлы окружения и настоящие пароли из .env не попадают в git
|
|
2
|
+
intent_en: personal environment files and real passwords from .env do not end up in git
|
|
3
|
+
|
|
4
|
+
# Только там, где файлом окружения пользуются: признак считает `.env`, `.env.example`,
|
|
5
|
+
# `.env.sample` в корне. Проекту без них запись показывать незачем.
|
|
6
|
+
trigger:
|
|
7
|
+
has_env: true
|
|
8
|
+
|
|
9
|
+
# Переносимая проверка — единственный рецепт, и это решение, а не пробел. Готовый `detect-secrets`
|
|
10
|
+
# ловит пароли по имени ключа, но не видит `.env.local` (личный по определению, что бы в нём ни
|
|
11
|
+
# лежало) и требует файла-базы для работы в конвейере; `gitleaks` пропускает человеческие пароли.
|
|
12
|
+
# Замер и таблица — в README записи.
|
|
13
|
+
recipes:
|
|
14
|
+
any: bash {gate}/check.sh {dir}
|
|
15
|
+
|
|
16
|
+
tool: https://github.com/Yelp/detect-secrets
|
|
17
|
+
|
|
18
|
+
proof: incidents/README.md, 2026-09-11 «пароль из .env, которого не видит ни один сканер» —
|
|
19
|
+
пять репозиториев с закоммиченным .env.production.local из поиска кода GitHub: гейт краснеет на
|
|
20
|
+
всех пяти, четыре находки настоящие; контрольные — одиннадцать репозиториев, ложных после
|
|
21
|
+
исключения ноль
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Значения по умолчанию, которые коммитят намеренно: секретов здесь нет
|
|
2
|
+
APP_ENV=development
|
|
3
|
+
VITE_API_KEY=pk_demo_public_1234567
|
|
4
|
+
DB_PASSWORD=
|
|
5
|
+
API_TOKEN=changeme
|
|
6
|
+
SECRET_KEY=${APP_SECRET}
|
|
7
|
+
REDIS_PASSWORD=<set-in-vault>
|
|
8
|
+
TOKEN_TTL_SECONDS=86400000
|
|
9
|
+
DATABASE_URL=postgres://postgres:postgres@localhost:5432/app
|
|
10
|
+
RABBITMQ_URL=amqp://guest:guest@localhost:5672/
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
|
|
2
|
+
#/ public-key encryption for .env files /
|
|
3
|
+
#/----------------------------------------------------------/
|
|
4
|
+
DOTENV_PUBLIC_KEY_PRODUCTION="03f8b0c2d1e4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1"
|
|
5
|
+
DB_PASSWORD="encrypted:BG8M6U+GKJGwpGA42ml2erb9+T2NBX6Z2JkBLynDy21poz0UfF5aPxCgRbIyhnQFdWKd0C9GZ7lM5PeL86xghoMcWvvPpkyQ0yaD2pZ64RzoxFGB1lTZYlEgQOxTDJnWxODHfuQcFY10uA=="
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Настройки прода, закоммиченные по ошибке. Пароли — человеческие, без узнаваемого формата:
|
|
2
|
+
# ровно то, что сканеры по форме и энтропии пропускают.
|
|
3
|
+
APP_ENV=production
|
|
4
|
+
DB_HOST=db.internal
|
|
5
|
+
DB_PASSWORD=Sup3rS3cret2024
|
|
6
|
+
DATABASE_URL=postgres://app:Winter2024Pass@db.internal:5432/app
|
|
7
|
+
export JWT_SECRET="hunter2hunter2"
|
|
@@ -11,6 +11,11 @@ trigger:
|
|
|
11
11
|
# печать в примерах документации, в тестах, в бенчмарках и в выводе командной строки.
|
|
12
12
|
# Отличить их от отладки можно только разбором кода, а не поиском по тексту, — и это
|
|
13
13
|
# ровно то, что уже делают ruff и eslint.
|
|
14
|
+
# Домашние страницы готовых инструментов, которые зовут рецепты ниже. Адреса сверены
|
|
15
|
+
# по реестру github 2026-09-10, а не написаны по памяти: три выдуманных адреса чужих
|
|
16
|
+
# репозиториев — записанная шишка комплекта.
|
|
17
|
+
tool: https://github.com/astral-sh/ruff · https://github.com/eslint/eslint
|
|
18
|
+
|
|
14
19
|
recipes:
|
|
15
20
|
python: ruff check -q --output-format=concise --select T20 {dir}
|
|
16
21
|
typescript: eslint --no-config-lookup --ignore-pattern 'gates/*/red/**' --ignore-pattern 'gates/*/green/**' --rule '{"no-console":"error"}' {dir}
|
|
@@ -22,3 +27,7 @@ samples_for: python
|
|
|
22
27
|
|
|
23
28
|
proof: incidents/README.md, 2026-09-07 «замер по пяти стекам вынес приговор пяти записям» —
|
|
24
29
|
434 находки переносимой проверки на пяти чужих репозиториях, настоящих ноль
|
|
30
|
+
|
|
31
|
+
# Правило Biome под эту запись — для сверки `covers:` (рецептов под Biome нет). Имя сверено по
|
|
32
|
+
# схеме конфигурации Biome 2.5.12, а не по памяти.
|
|
33
|
+
biome_rules: suspicious/noConsole
|
|
@@ -4,7 +4,27 @@ intent_en: keys, passwords and private keys do not end up in the code
|
|
|
4
4
|
trigger:
|
|
5
5
|
always: true
|
|
6
6
|
|
|
7
|
+
# Домашние страницы готовых инструментов, которые зовут рецепты ниже. Адреса сверены
|
|
8
|
+
# по реестру github 2026-09-10, а не написаны по памяти: три выдуманных адреса чужих
|
|
9
|
+
# репозиториев — записанная шишка комплекта.
|
|
10
|
+
tool: https://github.com/gitleaks/gitleaks
|
|
11
|
+
|
|
7
12
|
recipes:
|
|
13
|
+
# Готовый аналог сильнее нашего, и об этом прямо сказано в README записи: `gitleaks` знает
|
|
14
|
+
# сотни форматов токенов и умеет читать историю, а не только рабочее дерево. Наша проверка —
|
|
15
|
+
# запасная, для тех, кто не хочет ставить лишний бинарь.
|
|
16
|
+
#
|
|
17
|
+
# Ключ `native`, а не язык: секреты ищутся в любом файле, и раскладывать одну команду по
|
|
18
|
+
# восьми языкам значило бы завести ровно тот повтор, против которого у нас есть гейт.
|
|
19
|
+
#
|
|
20
|
+
# `dir` — рабочее дерево; чтение истории (`gitleaks git`) сюда не берём: гейт обязан отвечать
|
|
21
|
+
# про ТЕКУЩЕЕ состояние, а прошлое лечится не проверкой, а ротацией ключа.
|
|
22
|
+
#
|
|
23
|
+
# ОСТОРОЖНО С КОДОМ ВОЗВРАТА: у `gitleaks` 1 означает «нашёл ЛИБО сломался» — по документации,
|
|
24
|
+
# проверено 2026-09-10. То есть для него «находка» и «сбой инструмента» неразличимы, и это
|
|
25
|
+
# ровно тот класс, который мы разделяем у себя. Пока принимаем как есть и говорим об этом
|
|
26
|
+
# вслух, а не делаем вид, что кодов три.
|
|
27
|
+
native: gitleaks dir --no-banner {dir}
|
|
8
28
|
any: bash {gate}/check.sh {dir}
|
|
9
29
|
|
|
10
30
|
proof: kit/docs/ai/project-baseline.md, пункт 5 — «секреты не хранятся в коде вообще: ни в истории, ни в примерах, ни в тестах»
|
|
@@ -7,6 +7,11 @@ intent_en: an error is not silently swallowed — it is handled and logged, or r
|
|
|
7
7
|
trigger:
|
|
8
8
|
langs: python, javascript, typescript, go
|
|
9
9
|
|
|
10
|
+
# Домашние страницы готовых инструментов, которые зовут рецепты ниже. Адреса сверены
|
|
11
|
+
# по реестру github 2026-09-10, а не написаны по памяти: три выдуманных адреса чужих
|
|
12
|
+
# репозиториев — записанная шишка комплекта.
|
|
13
|
+
tool: https://github.com/astral-sh/ruff · https://github.com/eslint/eslint · https://github.com/kisielk/errcheck
|
|
14
|
+
|
|
10
15
|
recipes:
|
|
11
16
|
# BLE — ловля голого исключения, TRY400 — запись без трейса, SIM105 — перехват ради тишины.
|
|
12
17
|
python: ruff check -q --output-format=concise --select BLE,TRY400,SIM105 {dir}
|
|
@@ -21,3 +26,7 @@ proof: incidents/README.md, 2026-09-07 «замер по пяти стекам
|
|
|
21
26
|
переносимая версия дала 46 находок на пяти чужих репозиториях, настоящими оказались только
|
|
22
27
|
пустые `catch {}` в zod; весь питоновский класс `except ImportError: pass` и растовый
|
|
23
28
|
`let _ = write(..)?` были ложными
|
|
29
|
+
|
|
30
|
+
# Правило Biome под эту запись — для сверки `covers:` (рецептов под Biome нет). Имя сверено по
|
|
31
|
+
# схеме конфигурации Biome 2.5.12, а не по памяти.
|
|
32
|
+
biome_rules: suspicious/noEmptyBlockStatements
|
|
@@ -4,6 +4,11 @@ intent_en: "fix later" markers are absent from finished code — a filed task re
|
|
|
4
4
|
trigger:
|
|
5
5
|
always: true
|
|
6
6
|
|
|
7
|
+
# Домашние страницы готовых инструментов, которые зовут рецепты ниже. Адреса сверены
|
|
8
|
+
# по реестру github 2026-09-10, а не написаны по памяти: три выдуманных адреса чужих
|
|
9
|
+
# репозиториев — записанная шишка комплекта.
|
|
10
|
+
tool: https://github.com/astral-sh/ruff · https://github.com/eslint/eslint
|
|
11
|
+
|
|
7
12
|
recipes:
|
|
8
13
|
any: bash {gate}/check.sh {dir}
|
|
9
14
|
# Готовое правило точнее самописного и не требует поддержки.
|
|
@@ -11,3 +16,7 @@ recipes:
|
|
|
11
16
|
typescript: eslint --no-config-lookup --ignore-pattern 'gates/*/red/**' --ignore-pattern 'gates/*/green/**' --rule '{"no-warning-comments":["error",{"terms":["todo","fixme","hack","xxx"]}]}' {dir}
|
|
12
17
|
|
|
13
18
|
proof: incidents/README.md — «2026-08-25 разбор 1069 коммитов»: 416 коммитов из 1069 оказались стабилизацией уже выкаченного
|
|
19
|
+
|
|
20
|
+
# У Biome правила под TODO нет вовсе — сверено по схеме конфигурации Biome 2.5.12. Заявка
|
|
21
|
+
# «lint (biome) держит эту запись» поэтому заведомо неверна, и doctor так и говорит.
|
|
22
|
+
biome_rules: none
|
package/llms.txt
CHANGED
|
@@ -40,7 +40,9 @@ Zero runtime dependencies. Node 18+ and an `sh` shell. MIT.
|
|
|
40
40
|
enforces, what the ratchets hold. Where it does not know, it says so: a run that never happened
|
|
41
41
|
is reported as unknown, never as clean. `context --install` writes a `SessionStart` hook into
|
|
42
42
|
`.claude/settings.json` (Claude Code only; the rest of the kit stays vendor-neutral). Measured:
|
|
43
|
-
the block is ~
|
|
43
|
+
the block is ~500 tokens, and carries what a file cannot — what changed today, the next three
|
|
44
|
+
steps with ready commands, and when to run which command (before a commit, after changing a
|
|
45
|
+
check, when a check is in the way). `context --full`
|
|
44
46
|
adds the command map and the rulebook verbatim (~7000 tokens): a deliberate trade, chosen by
|
|
45
47
|
the owner after the objection about long inputs, on the grounds that an agent reads files
|
|
46
48
|
poorly and the tokens are the price of it not guessing
|
|
@@ -70,7 +72,7 @@ Zero runtime dependencies. Node 18+ and an `sh` shell. MIT.
|
|
|
70
72
|
the files `init` writes are owned by root, so you cannot edit your own manifest. Debian-based
|
|
71
73
|
on purpose: the gates are `sh`, `grep`, `awk`, `find` — under alpine's busybox they behave
|
|
72
74
|
differently, and an image where the gates behave differently is worse than no image
|
|
73
|
-
- As a GitHub Action: `uses: arsen-ask-lx/Agent_Quality_Kit@v0.
|
|
75
|
+
- As a GitHub Action: `uses: arsen-ask-lx/Agent_Quality_Kit@v0.13.0` with `min: 1`
|
|
74
76
|
(https://github.com/marketplace/actions/agent-quality-kit-aqk)
|
|
75
77
|
|
|
76
78
|
## What makes it different
|
package/package.json
CHANGED
package/tool/commands/badge.mjs
CHANGED
|
@@ -45,7 +45,7 @@ async function cmdBadge(args = []) {
|
|
|
45
45
|
// Прогон, а не манифест. Значок при красном гейте — это и есть недоказанное утверждение.
|
|
46
46
|
const gates = declaredGates(man);
|
|
47
47
|
if (gates.length) {
|
|
48
|
-
const run = runGates(man);
|
|
48
|
+
const run = await runGates(man);
|
|
49
49
|
if (run.failed) {
|
|
50
50
|
const red = run.results.filter((r) => !r.ok).map((r) => r.name).join(", ");
|
|
51
51
|
die(`\n ${L.badge.redGates(run.failed, red)}\n`);
|