agent-quality-kit 0.5.0 → 0.7.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.
Files changed (135) hide show
  1. package/README.md +53 -2
  2. package/README.ru.md +35 -1
  3. package/kit/docs/ai/agent-harness-playbook.md +1 -1
  4. package/kit/docs/ready-made-rules.md +65 -0
  5. package/kit/gates/README.md +60 -0
  6. package/kit/gates/ci-actually-fails/README.md +54 -0
  7. package/kit/gates/ci-actually-fails/check.sh +116 -0
  8. package/kit/gates/ci-actually-fails/gate.yml +14 -0
  9. package/kit/gates/ci-actually-fails/green/.github/workflows/ci.yml +30 -0
  10. package/kit/gates/ci-actually-fails/red/.github/workflows/ci.yml +12 -0
  11. package/kit/gates/ci-actually-fails/red/.github/workflows/soft.yml +15 -0
  12. package/kit/gates/color-from-token/check.sh +13 -1
  13. package/kit/gates/commit-explains-itself/README.md +13 -3
  14. package/kit/gates/commit-explains-itself/check.sh +8 -4
  15. package/kit/gates/complexity-limit/README.md +5 -0
  16. package/kit/gates/complexity-limit/check.sh +21 -2
  17. package/kit/gates/complexity-limit/green/test_fixtures.py +14 -0
  18. package/kit/gates/deps-are-pinned/README.md +14 -1
  19. package/kit/gates/deps-are-pinned/check.sh +6 -1
  20. package/kit/gates/deps-are-pinned/green/pyproject-with-requirements/pyproject.toml +12 -0
  21. package/kit/gates/deps-are-pinned/green/pyproject-with-requirements/requirements.txt +3 -0
  22. package/kit/gates/deps-are-pinned/red/pyproject-loose/pyproject.toml +12 -0
  23. package/kit/gates/deps-are-pinned/red/pyproject-loose/requirements.txt +3 -0
  24. package/kit/gates/duplicate-code/README.md +11 -2
  25. package/kit/gates/duplicate-code/check.sh +31 -4
  26. package/kit/gates/duplicate-code/gate.yml +8 -0
  27. package/kit/gates/duplicate-code/green/imports_a.go +20 -0
  28. package/kit/gates/duplicate-code/green/imports_b.go +19 -0
  29. package/kit/gates/entry-links-exist/README.md +5 -0
  30. package/kit/gates/entry-links-exist/check.sh +6 -0
  31. package/kit/gates/entry-links-exist/green/AGENTS.md +3 -0
  32. package/kit/gates/file-size-limit/README.md +9 -2
  33. package/kit/gates/file-size-limit/check.sh +13 -1
  34. package/kit/gates/gate-not-weakened/README.md +54 -0
  35. package/kit/gates/gate-not-weakened/check.sh +84 -0
  36. package/kit/gates/gate-not-weakened/gate.yml +15 -0
  37. package/kit/gates/gate-not-weakened/green/checkout.ts +8 -0
  38. package/kit/gates/gate-not-weakened/green/payments.py +6 -0
  39. package/kit/gates/gate-not-weakened/green/release.sh +2 -0
  40. package/kit/gates/gate-not-weakened/red/checkout.ts +9 -0
  41. package/kit/gates/gate-not-weakened/red/payments.py +6 -0
  42. package/kit/gates/gate-not-weakened/red/release.sh +2 -0
  43. package/kit/gates/hook-actually-fires/README.md +74 -0
  44. package/kit/gates/hook-actually-fires/check.sh +183 -0
  45. package/kit/gates/hook-actually-fires/gate.yml +15 -0
  46. package/kit/gates/hook-actually-fires/green/.claude/hooks/hooks.json +3 -0
  47. package/kit/gates/hook-actually-fires/green/.claude/settings.json +74 -0
  48. package/kit/gates/hook-actually-fires/green/.claude/settings.local.json +74 -0
  49. package/kit/gates/hook-actually-fires/red/.claude/hooks/hooks.json +4 -0
  50. package/kit/gates/hook-actually-fires/red/.claude/settings.json +53 -0
  51. package/kit/gates/no-phantom-package/README.md +84 -0
  52. package/kit/gates/no-phantom-package/check.sh +161 -0
  53. package/kit/gates/no-phantom-package/gate.yml +20 -0
  54. package/kit/gates/no-phantom-package/green/AGENTS.md +15 -0
  55. package/kit/gates/no-phantom-package/red/AGENTS.md +15 -0
  56. package/kit/gates/no-print-in-prod/README.md +33 -39
  57. package/kit/gates/no-print-in-prod/gate.yml +14 -6
  58. package/kit/gates/personal-config-not-shared/README.md +66 -0
  59. package/kit/gates/personal-config-not-shared/check.sh +103 -0
  60. package/kit/gates/personal-config-not-shared/gate.yml +16 -0
  61. package/kit/gates/personal-config-not-shared/green/.aqk-tracked +9 -0
  62. package/kit/gates/personal-config-not-shared/red/.aqk-tracked +6 -0
  63. package/kit/gates/promise-has-gate/README.md +50 -0
  64. package/kit/gates/promise-has-gate/check.sh +88 -0
  65. package/kit/gates/promise-has-gate/gate.yml +14 -0
  66. package/kit/gates/promise-has-gate/green/.aqk.yml +6 -0
  67. package/kit/gates/promise-has-gate/green/AGENTS.md +7 -0
  68. package/kit/gates/promise-has-gate/red/.aqk.yml +6 -0
  69. package/kit/gates/promise-has-gate/red/AGENTS.md +7 -0
  70. package/kit/gates/secrets-not-in-code/check.sh +13 -1
  71. package/kit/gates/swallowed-error/README.md +36 -18
  72. package/kit/gates/swallowed-error/gate.yml +13 -3
  73. package/kit/gates/test-has-assertion/README.md +47 -0
  74. package/kit/gates/test-has-assertion/check.sh +206 -0
  75. package/kit/gates/test-has-assertion/gate.yml +15 -0
  76. package/kit/gates/test-has-assertion/green/checkout.test.ts +9 -0
  77. package/kit/gates/test-has-assertion/green/test_billing.py +17 -0
  78. package/kit/gates/test-has-assertion/red/checkout.test.ts +8 -0
  79. package/kit/gates/test-has-assertion/red/test_billing.py +14 -0
  80. package/kit/gates/test-not-adjusted/README.md +79 -0
  81. package/kit/gates/test-not-adjusted/check.sh +136 -0
  82. package/kit/gates/test-not-adjusted/gate.yml +19 -0
  83. package/kit/gates/test-not-adjusted/green/after/calc.py +6 -0
  84. package/kit/gates/test-not-adjusted/green/after/tests/test_calc.py +9 -0
  85. package/kit/gates/test-not-adjusted/green/before/calc.py +2 -0
  86. package/kit/gates/test-not-adjusted/green/before/tests/test_calc.py +5 -0
  87. package/kit/gates/test-not-adjusted/red/after/calc.py +2 -0
  88. package/kit/gates/test-not-adjusted/red/after/tests/test_calc.py +5 -0
  89. package/kit/gates/test-not-adjusted/red/before/calc.py +2 -0
  90. package/kit/gates/test-not-adjusted/red/before/tests/test_calc.py +7 -0
  91. package/kit/gates/todo-without-task/README.md +6 -0
  92. package/kit/gates/todo-without-task/check.sh +13 -1
  93. package/kit/ratchet/ratchet.sh +70 -2
  94. package/kit/rules/general.md +23 -0
  95. package/kit/rules-en/general.md +82 -0
  96. package/kit/rules-en/security.md +33 -0
  97. package/kit/rules-en/testing.md +48 -0
  98. package/llms.txt +2 -1
  99. package/package.json +4 -2
  100. package/tool/commands/badge.mjs +7 -1
  101. package/tool/commands/doctor.mjs +90 -10
  102. package/tool/commands/gates.mjs +19 -5
  103. package/tool/commands/project.mjs +15 -2
  104. package/tool/commands/prove.mjs +67 -0
  105. package/tool/commands/report.mjs +4 -1
  106. package/tool/i18n/en-docs.mjs +70 -0
  107. package/tool/i18n/en.mjs +66 -54
  108. package/tool/i18n/ru-docs.mjs +70 -0
  109. package/tool/i18n/ru.mjs +66 -54
  110. package/tool/i18n/templates-en.mjs +9 -9
  111. package/tool/i18n/templates-ru.mjs +9 -9
  112. package/tool/lib/core.mjs +7 -1
  113. package/tool/lib/manifest.mjs +72 -5
  114. package/tool/lib/prove.mjs +160 -0
  115. package/tool/lib/repo.mjs +31 -2
  116. package/tool/lib/scope.mjs +131 -0
  117. package/tool/lib/templates.mjs +2 -0
  118. package/tool/program.mjs +6 -0
  119. package/tool/selfcheck/gates.sh +86 -3
  120. package/tool/selfcheck/lifecycle.mjs +29 -0
  121. package/tool/selfcheck/mutation.sh +21 -1
  122. package/tool/selfcheck/smoke.sh +329 -36
  123. package/tool/selfcheck/units-level.mjs +60 -0
  124. package/tool/selfcheck/units.mjs +196 -1
  125. package/kit/gates/no-print-in-prod/check.sh +0 -38
  126. package/kit/gates/no-print-in-prod/green/docs.ts +0 -15
  127. package/kit/gates/no-print-in-prod/green/main.go +0 -8
  128. package/kit/gates/no-print-in-prod/green/main.rs +0 -4
  129. package/kit/gates/no-print-in-prod/red/main.go +0 -8
  130. package/kit/gates/no-print-in-prod/red/main.rs +0 -4
  131. package/kit/gates/swallowed-error/check.sh +0 -54
  132. package/kit/gates/swallowed-error/green/run.js +0 -8
  133. package/kit/gates/swallowed-error/red/run.js +0 -3
  134. /package/kit/gates/commit-explains-itself/green/{COMMIT_MSG → .aqk-commit-msg} +0 -0
  135. /package/kit/gates/commit-explains-itself/red/{COMMIT_MSG → .aqk-commit-msg} +0 -0
@@ -11,8 +11,9 @@
11
11
 
12
12
  import test from "node:test";
13
13
  import assert from "node:assert/strict";
14
- import { parseManifest, manifestWithGate, unknownKeys } from "../lib/manifest.mjs";
14
+ import { parseManifest, manifestWithGate, unknownKeys, entryLifecycle, advisorySet, KNOWN_KEYS } from "../lib/manifest.mjs";
15
15
  import { triggerVerdict, recipeFor, stems, overlap, EXT_LANG, whichSync } from "../lib/repo.mjs";
16
+ import { scopeOutput, splitAdvice } from "../lib/scope.mjs";
16
17
  import { assessBaseline, ITEMS, BASELINE_TOTAL } from "../lib/baseline.mjs";
17
18
  import { CATALOGS, pickLang, L } from "../i18n/index.mjs";
18
19
  import { badgeMarkdown, BADGE_RE, placesToCheck } from "../commands/badge.mjs";
@@ -274,3 +275,197 @@ test("триггер по интерфейсу отделяет фронтенд
274
275
  // Причина сокрытия называется, а не молчит: иначе «не показано» неотличимо от «нечего показать».
275
276
  assert.equal(typeof triggerVerdict(rec, { ...base, has_ui: false }).why, "string");
276
277
  });
278
+
279
+ // --- зрелость записи ---------------------------------------------------------
280
+ // ЗАЧЕМ. У всех трёх соседей поле зрелости есть, и у всех троих его ЗАПОЛНЯЕТ АВТОР: `lifecycle`
281
+ // у зондов Scorecard, `future`/`obsolete` у критериев значка OpenSSF. Поле, которое объявляет
282
+ // автор, означает доверие к автору, а не факт, — ровно то, против чего построен весь стандарт.
283
+ // Поэтому зрелость здесь ВЫЧИСЛЯЕТСЯ из доказательства, а объявить её нельзя.
284
+ test("зрелость записи считается по доказательству, а не по объявлению", () => {
285
+ const proven = entryLifecycle({ proof: "incidents/README.md, 2026-08-27 «печать в проде»" });
286
+ assert.equal(proven.state, "stable");
287
+ assert.equal(proven.problem, null);
288
+
289
+ const claimed = entryLifecycle({ proof: "это общепринятая хорошая практика" });
290
+ assert.equal(claimed.state, "experimental");
291
+ assert.equal(claimed.problem, null);
292
+ // Причина обязательна: «запись условная» без объяснения неотличимо от придирки.
293
+ assert.equal(typeof claimed.why, "string");
294
+ });
295
+
296
+ test("объявить себя зрелым нельзя — это самооценка", () => {
297
+ for (const claim of ["stable", "experimental"]) {
298
+ const r = entryLifecycle({ lifecycle: claim, proof: "incidents/README.md, 2026-01-01" });
299
+ assert.notEqual(r.problem, null);
300
+ // Вердикт всё равно считается сам: объявление не влияет ни на что, кроме отказа.
301
+ assert.equal(r.state, "stable");
302
+ }
303
+ assert.notEqual(entryLifecycle({ lifecycle: "beta", proof: "incidents/x" }).problem, null);
304
+ });
305
+
306
+ // Единственное состояние, которое ОБЪЯВЛЯЕТСЯ: из фактов записи «её больше не ставят» не
307
+ // выводится никак. Цена объявления — обязательная замена: запись, выведенная в никуда,
308
+ // оставляет человека без ответа на вопрос «а что теперь».
309
+ test("выведенная запись обязана назвать замену", () => {
310
+ const noReplacement = entryLifecycle({ lifecycle: "deprecated", proof: "incidents/x" });
311
+ assert.equal(noReplacement.state, "deprecated");
312
+ assert.notEqual(noReplacement.problem, null);
313
+
314
+ const ok = entryLifecycle({ lifecycle: "deprecated", superseded_by: "no-print-in-prod", proof: "incidents/x" });
315
+ assert.equal(ok.state, "deprecated");
316
+ assert.equal(ok.supersededBy, "no-print-in-prod");
317
+ assert.equal(ok.problem, null);
318
+ });
319
+
320
+
321
+ // --- сужение вывода до дифа --------------------------------------------------
322
+ // ЗАЧЕМ. Первый прогон на живом проекте даёт тысячи находок из кода, который писали годами.
323
+ // Человек видит стену красного и выключает инструмент целиком — это причина номер один, по
324
+ // которой такие проверки снимают. Три независимых проекта из нашего разбора умеют показывать
325
+ // только внесённое дифом (reviewdog, ratchets `--since`, четыре режима шума у react-doctor).
326
+ test("сужение по дифу: находка вне диапазона отбрасывается, внутри — остаётся", () => {
327
+ const files = new Set(["src/new.py"]);
328
+ const r = scopeOutput(["src/new.py:3: печать", "src/old.py:9: печать"], files);
329
+ assert.deepEqual(r.kept, ["src/new.py:3: печать"]);
330
+ assert.equal(r.findings, 1);
331
+ });
332
+
333
+ test("сужение по дифу: «./путь» и «путь» — один и тот же файл", () => {
334
+ const r = scopeOutput(["./src/new.py:3: печать", "src\\new.py:4: печать"], new Set(["src/new.py"]));
335
+ assert.equal(r.findings, 2);
336
+ });
337
+
338
+ // Тот же урок, что стоил починки в _native.sh: родные инструменты печатают путь ВНУТРИ
339
+ // escape-последовательности, и сравнение по границе пути его не видит. Тогда «вывод
340
+ // сократился с 5597 до 5505 строк» выглядело как работающая правка.
341
+ test("сужение по дифу: цвет снимается до сравнения путей", () => {
342
+ const esc = String.fromCharCode(27);
343
+ const line = esc + "[32m" + "src/new.py" + esc + "[0m" + ":3: печать";
344
+ assert.equal(scopeOutput([line], new Set(["src/new.py"])).findings, 1);
345
+ });
346
+
347
+ test("сужение по дифу: строка без пути остаётся, но находкой не считается", () => {
348
+ const r = scopeOutput(["Итого: 4 нарушения", "src/old.py:1: печать"], new Set(["src/new.py"]));
349
+ assert.equal(r.findings, 0);
350
+ assert.equal(r.kept.includes("Итого: 4 нарушения"), true);
351
+ });
352
+
353
+ // САМОЕ ВАЖНОЕ ЗДЕСЬ. Гейт, который печатает вердикт без путей (проверка коммита, проверка
354
+ // конфига конвейера), сузить дифом нельзя. Молча признать его успешным — это ровно та тишина,
355
+ // против которой построен весь стандарт, только теперь внутри нашего же флага.
356
+ test("сужение по дифу: гейт без путей в выводе не сужается и остаётся красным", () => {
357
+ const r = scopeOutput(["коммит не несёт раздела «Сделано:»"], new Set(["src/new.py"]));
358
+ assert.equal(r.scopable, false);
359
+ assert.equal(scopeOutput(["src/old.py:1: печать"], new Set(["src/new.py"])).scopable, true);
360
+ });
361
+
362
+
363
+ // --- совет по починке не теряется в обрезке ----------------------------------
364
+ // ЗАЧЕМ. Все девятнадцать записей каталога печатают строку «почини: …» последней. `doctor --run`
365
+ // показывает три первые строки вывода и обрезает остальное — то есть ровно ту строку, ради
366
+ // которой человек и смотрит на красное, он не видит никогда. Находка без действия — это повод
367
+ // закрыть окно, а не починить.
368
+ test("совет по починке отделяется от находок и не обрезается", () => {
369
+ const out = [
370
+ "src/a.py:3: печать",
371
+ "src/b.py:9: печать",
372
+ "src/c.py:1: печать",
373
+ "src/d.py:7: печать",
374
+ " почини: замени на вызов системы логов",
375
+ " тогда запись попадёт в общий журнал",
376
+ ];
377
+ const r = splitAdvice(out);
378
+ assert.equal(r.findings.length, 4);
379
+ assert.equal(r.advice.length, 2);
380
+ // Продолжение совета едет вместе с ним: без второй строки первая обрывается на полуслове.
381
+ assert.equal(r.advice[1].includes("общий журнал"), true);
382
+ });
383
+
384
+ test("совет по починке опознаётся на обоих языках", () => {
385
+ assert.equal(splitAdvice(["a.py:1: x", " fix: replace with a logger call"]).advice.length, 1);
386
+ assert.equal(splitAdvice(["a.py:1: x", " почини: замени на логгер"]).advice.length, 1);
387
+ });
388
+
389
+ // Вывод без совета — это не ошибка разбора, а признак записи, которая не говорит, что делать.
390
+ // Разбор обязан вернуть пустой совет, а не выдумать его из последней строки.
391
+ test("вывод без совета не превращается в совет", () => {
392
+ const r = splitAdvice(["src/a.py:3: печать", "src/b.py:9: печать"]);
393
+ assert.equal(r.advice.length, 0);
394
+ assert.equal(r.findings.length, 2);
395
+ });
396
+
397
+
398
+ // Совет бывает не только в конце: `deps-are-pinned` печатает его внутри вспомогательной
399
+ // функции, вызываемой шесть раз, и находки идут ПОСЛЕ. Пока советом считалось «всё от первой
400
+ // метки до конца», сорок находок уезжали в жёлтый список без обрезки — ровно та стена красного,
401
+ // против которой написан этот модуль. Найдено ревью 2026-09-06.
402
+ test("совет, вставленный посреди вывода, не проглатывает находки", () => {
403
+ const r = splitAdvice([
404
+ " почини: закрепи версии",
405
+ "src/a.py:1: x",
406
+ "src/b.py:2: x",
407
+ "src/c.py:3: x",
408
+ " почини: и вот это тоже",
409
+ " вторая строка совета",
410
+ ]);
411
+ assert.equal(r.findings.length, 3);
412
+ assert.equal(r.advice.length, 3);
413
+ });
414
+
415
+ // Продолжение совета не должно съедать находку, стоящую сразу за ним.
416
+ test("находка сразу после совета остаётся находкой", () => {
417
+ const r = splitAdvice([" почини: сделай так", " пояснение", "src/z.py:9: x"]);
418
+ assert.equal(r.findings.length, 1);
419
+ assert.equal(r.findings[0].includes("src/z.py"), true);
420
+ });
421
+
422
+
423
+ // --- правило, введённое совещательным ----------------------------------------
424
+ // ЗАЧЕМ. Правило вводят в проект, где старый код ему не соответствует. Храповик отвечает на
425
+ // это одним способом: старое становится долгом. Второй способ — показывать, не роняя, пока
426
+ // команда договаривается. Сегодня его нет вовсе: находка либо роняет сборку, либо не существует.
427
+ //
428
+ // ПОЧЕМУ ОБЪЯВЛЕНИЕМ, А НЕ ФЛАГОМ ПРОГОНА. Флаг «не роняй ничего» — это `continue-on-error`,
429
+ // против которого написана наша же запись ci-actually-fails: он понижает всё разом, не виден
430
+ // в дифе и не назван в сводке. Список в манифесте виден, именуется и считается всегда.
431
+ test("совещательные гейты читаются из манифеста списком", () => {
432
+ const man = parseManifest("aqk: 1\nadvisory:\n - complexity-limit\n - duplicate-code\n");
433
+ const a = advisorySet(man);
434
+ assert.equal(a.has("complexity-limit"), true);
435
+ assert.equal(a.has("duplicate-code"), true);
436
+ assert.equal(a.has("secrets-not-in-code"), false);
437
+ });
438
+
439
+ test("список в одну строку читается так же", () => {
440
+ const a = advisorySet(parseManifest("advisory: [complexity-limit, duplicate-code]\n"));
441
+ assert.equal(a.size, 2);
442
+ });
443
+
444
+ // Отсутствие блока — это ноль совещательных, а не «все совещательные».
445
+ test("без блока advisory совещательных нет", () => {
446
+ assert.equal(advisorySet(parseManifest("aqk: 1\n")).size, 0);
447
+ assert.equal(advisorySet(null).size, 0);
448
+ });
449
+
450
+ // Поле обязано быть известным манифесту: иначе опечатка «advisery:» молча означала бы
451
+ // «совещательных нет», и правило, которое человек считал введённым, роняло бы сборку.
452
+ test("advisory — известное поле манифеста", () => {
453
+ assert.equal(KNOWN_KEYS.includes("advisory"), true);
454
+ assert.deepEqual(unknownKeys({ aqk: 1, advisory: [] }), []);
455
+ });
456
+
457
+
458
+ // --- правила на языке проекта ------------------------------------------------
459
+ // ЗАЧЕМ. Русский текст правил в англоязычном проекте — это первое, что там откроет человек,
460
+ // и первое, чего он не прочитает. Русская версия остаётся источником истины, английская —
461
+ // переводом. Совпадение содержания машина не сторожит, а вот НАБОР ФАЙЛОВ обязана: добавили
462
+ // правило на одном языке и забыли про другой — половина мира получит комплект без него.
463
+ test("наборы файлов правил совпадают на обоих языках", async () => {
464
+ const { readdir } = await import("node:fs/promises");
465
+ const { fileURLToPath } = await import("node:url");
466
+ const at = (d) => fileURLToPath(new URL(`../../kit/${d}`, import.meta.url));
467
+ const ru = (await readdir(at("rules"))).filter((f) => f.endsWith(".md")).sort();
468
+ const en = (await readdir(at("rules-en"))).filter((f) => f.endsWith(".md")).sort();
469
+ assert.deepEqual(en, ru);
470
+ assert.equal(ru.length > 0, true);
471
+ });
@@ -1,38 +0,0 @@
1
- #!/usr/bin/env sh
2
- # Отладочная печать переживает задачу и уезжает в прод: она не ошибка сборки и
3
- # не видна в ревью диффа на 400 строк. В проде это либо шум, либо утечка данных
4
- # мимо системы логов.
5
- DIR="${1:-.}"
6
- . "$(dirname "$0")/../_skip.sh" 2>/dev/null || SKIP_NAMES=".git .aqk node_modules .venv"
7
- # Красный образец — намеренно сломанный код в репозитории. Сканирующий гейт обязан его
8
- # пропускать, иначе будет вечно краснеть на том, что сам же и положил. Исключение снимается,
9
- # когда проверяют сам образец: тогда каталог red и есть цель проверки.
10
-
11
- # Где печать законна и дефектом не является: вспомогательные скрипты, оснастка, примеры,
12
- # записные книжки. Это НЕ общий список исключений: секрет в scripts/ — такой же секрет,
13
- # а печать там — обычный способ говорить с человеком.
14
- #
15
- # На настоящем проекте без этого различия 285 находок из 330 пришли из scripts/ и оснастки.
16
- # Гейт, который на 86% состоит из ложных сработок, выключают целиком.
17
- TOOLING="--exclude-dir=scripts --exclude-dir=tools --exclude-dir=bin --exclude-dir=examples --exclude-dir=notebooks --exclude-dir=docs --exclude-dir=.claude --exclude-dir=gates"
18
- # `gates-reference` отсюда убран: это имя каталога ОДНОГО проекта, зашитое в общий
19
- # инструмент. Такому место в .aqkignore самого проекта — он появился позже этой строки.
20
-
21
- # Проект называет СВОИ каталоги, где печать — интерфейс, а не отладка: у программы командной
22
- # строки это её исходники целиком. Объявляется в манифесте, рядом с командой, и потому видно
23
- # глазами: AQK_PRINT_OK_DIRS="tool cli" bash .../check.sh .
24
- #
25
- # ЗАЧЕМ ОТДЕЛЬНО ОТ ХРАПОВИКА. Сначала такие места записали долгом — и каждая новая строка
26
- # вывода красила проверку, требуя переснять реестр. Но долг — это то, что собираются погасить;
27
- # печать из программы командной строки убирать никто не будет. Долг, который нельзя погасить
28
- # работой, — не долг, а неверная мера: журнал, 2026-09-03.
29
- for D in ${AQK_PRINT_OK_DIRS:-}; do
30
- TOOLING="$TOOLING --exclude-dir=$D"
31
- done
32
- HITS=$(grep -rnE $(skip_grep "$DIR") $(include_code) $TOOLING '(^|[^A-Za-z_.])(print\(|console\.log\(|fmt\.Print|println!|print!|dbg!|System\.out\.print|Console\.Write)' "$DIR" 2>/dev/null | drop_comments | own_samples_filter "$DIR")
33
- if [ -n "$HITS" ]; then
34
- echo "$HITS"
35
- echo " почини: замени на вызов системы логов — тогда запись попадёт в общий журнал и уровень можно приглушить."
36
- exit 1
37
- fi
38
- exit 0
@@ -1,15 +0,0 @@
1
- // Печать внутри комментария — не печать. Пример в документации к функции и закомментированная
2
- // строка отладки одинаково не выполняются: находка здесь означает, что проверка читает текст,
3
- // а не код. Найдено прогоном по audit_project — семь находок из JSDoc и закомментированных строк.
4
-
5
- /**
6
- * Скачивает файл.
7
- *
8
- * @example
9
- * const result = await download(id);
10
- * console.log('Done:', result.file_url);
11
- */
12
- export async function download(id: string): Promise<string> {
13
- // console.log('отладка', id);
14
- return `/files/${id}`;
15
- }
@@ -1,8 +0,0 @@
1
- package main
2
-
3
- import "log/slog"
4
-
5
- func handle(x int) int {
6
- slog.Debug("got", "x", x)
7
- return x + 1
8
- }
@@ -1,4 +0,0 @@
1
- fn handle(x: i32) -> i32 {
2
- log::debug!("got {}", x);
3
- x + 1
4
- }
@@ -1,8 +0,0 @@
1
- package main
2
-
3
- import "fmt"
4
-
5
- func handle(x int) int {
6
- fmt.Println("got", x)
7
- return x + 1
8
- }
@@ -1,4 +0,0 @@
1
- fn handle(x: i32) -> i32 {
2
- println!("got {}", x);
3
- x + 1
4
- }
@@ -1,54 +0,0 @@
1
- #!/usr/bin/env sh
2
- # Тихо проглоченная ошибка — отказ, о котором никто не узнал. Система продолжает работать
3
- # «как будто всё хорошо», а причина всплывает через недели и в другом месте.
4
- DIR="${1:-.}"
5
- . "$(dirname "$0")/../_skip.sh" 2>/dev/null || SKIP_NAMES=".git .aqk node_modules .venv"
6
-
7
- # Список файлов собираем заранее. awk без файловых аргументов читает поток ввода и ждёт его
8
- # вечно: на проекте без файлов этих языков проверка зависала навсегда — в конвейере и в хуке
9
- # коммита, где поток ввода открыт. Найдено прогоном по проекту на Go.
10
- FILES=$(find "$DIR" $(skip_find "$DIR") -type f -print 2>/dev/null | only_code | own_samples_filter "$DIR")
11
- [ -z "$FILES" ] && exit 0
12
-
13
- printf '%s\n' "$FILES" | xargs -r awk '
14
- FILENAME ~ /\/(\.git|\.aqk|node_modules|dist|build|vendor)\// { next }
15
-
16
- # python: except ...: с пустым телом
17
- prev ~ /^[[:space:]]*except([[:space:]]|:)/ && $0 ~ /^[[:space:]]*(pass|\.\.\.)[[:space:]]*$/ {
18
- print FILENAME ":" FNR ": перехват без обработки — " gensub(/^[[:space:]]+/, "", 1, prev)
19
- }
20
- # python в одну строку
21
- /^[[:space:]]*except[^:]*:[[:space:]]*(pass|\.\.\.)[[:space:]]*$/ {
22
- print FILENAME ":" FNR ": перехват без обработки — " gensub(/^[[:space:]]+/, "", 1, $0)
23
- }
24
- # js/java/go-подобные: catch (...) { } пустой
25
- /catch[[:space:]]*(\([^)]*\))?[[:space:]]*\{[[:space:]]*\}/ {
26
- print FILENAME ":" FNR ": перехват без обработки — " gensub(/^[[:space:]]+/, "", 1, $0)
27
- }
28
- # go: пустое тело после проверки ошибки, либо ошибка присвоена в пустоту
29
- prev ~ /if[[:space:]]+err[[:space:]]*!=[[:space:]]*nil/ && $0 ~ /^[[:space:]]*\}[[:space:]]*$/ {
30
- print FILENAME ":" FNR ": ошибка проверена и выброшена — " gensub(/^[[:space:]]+/, "", 1, prev)
31
- }
32
- /^[[:space:]]*_[[:space:]]*=[[:space:]]*err[[:space:]]*$/ {
33
- print FILENAME ":" FNR ": ошибка присвоена в пустоту — " gensub(/^[[:space:]]+/, "", 1, $0)
34
- }
35
- # rust: результат отброшен без разбора
36
- /\.ok\(\);[[:space:]]*$/ || /let[[:space:]]+_[[:space:]]*=[^;]*\?[[:space:]]*;/ {
37
- print FILENAME ":" FNR ": результат отброшен без разбора — " gensub(/^[[:space:]]+/, "", 1, $0)
38
- }
39
- # обещания: .catch(() => {})
40
- /\.catch\([^)]*=>[[:space:]]*\{[[:space:]]*\}\)/ {
41
- print FILENAME ":" FNR ": перехват без обработки — " gensub(/^[[:space:]]+/, "", 1, $0)
42
- }
43
- { prev = $0 }
44
- ' > /tmp/.swallowed.$$ 2>/dev/null
45
-
46
- if [ -s /tmp/.swallowed.$$ ]; then
47
- cat /tmp/.swallowed.$$
48
- echo " почини: либо обработай и запиши в лог, либо пробрось дальше."
49
- echo " тихий перехват — это отказ, о котором никто не узнает, пока не станет поздно."
50
- rm -f /tmp/.swallowed.$$
51
- exit 1
52
- fi
53
- rm -f /tmp/.swallowed.$$
54
- exit 0
@@ -1,8 +0,0 @@
1
- async function go() {
2
- try {
3
- await run();
4
- } catch (e) {
5
- logger.error("шаг не выполнен", e);
6
- throw e;
7
- }
8
- }
@@ -1,3 +0,0 @@
1
- async function go() {
2
- try { await run(); } catch (e) {}
3
- }