agent-quality-kit 0.6.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.
- package/README.md +18 -2
- package/README.ru.md +16 -0
- package/kit/docs/ai/agent-harness-playbook.md +1 -1
- package/kit/docs/ready-made-rules.md +29 -4
- package/kit/gates/README.md +40 -0
- package/kit/gates/ci-actually-fails/README.md +12 -0
- package/kit/gates/ci-actually-fails/check.sh +26 -3
- package/kit/gates/ci-actually-fails/green/.github/workflows/ci.yml +16 -0
- package/kit/gates/ci-actually-fails/red/.github/workflows/soft.yml +15 -0
- package/kit/gates/color-from-token/check.sh +13 -1
- package/kit/gates/commit-explains-itself/README.md +13 -3
- package/kit/gates/commit-explains-itself/check.sh +8 -4
- package/kit/gates/complexity-limit/README.md +5 -0
- package/kit/gates/complexity-limit/check.sh +21 -2
- package/kit/gates/complexity-limit/green/test_fixtures.py +14 -0
- package/kit/gates/deps-are-pinned/README.md +14 -1
- package/kit/gates/deps-are-pinned/check.sh +6 -1
- package/kit/gates/deps-are-pinned/green/pyproject-with-requirements/pyproject.toml +12 -0
- package/kit/gates/deps-are-pinned/green/pyproject-with-requirements/requirements.txt +3 -0
- package/kit/gates/deps-are-pinned/red/pyproject-loose/pyproject.toml +12 -0
- package/kit/gates/deps-are-pinned/red/pyproject-loose/requirements.txt +3 -0
- package/kit/gates/duplicate-code/README.md +11 -2
- package/kit/gates/duplicate-code/check.sh +31 -4
- package/kit/gates/duplicate-code/gate.yml +8 -0
- package/kit/gates/duplicate-code/green/imports_a.go +20 -0
- package/kit/gates/duplicate-code/green/imports_b.go +19 -0
- package/kit/gates/entry-links-exist/README.md +5 -0
- package/kit/gates/entry-links-exist/check.sh +6 -0
- package/kit/gates/entry-links-exist/green/AGENTS.md +3 -0
- package/kit/gates/file-size-limit/README.md +9 -2
- package/kit/gates/file-size-limit/check.sh +13 -1
- package/kit/gates/gate-not-weakened/check.sh +13 -1
- package/kit/gates/hook-actually-fires/README.md +74 -0
- package/kit/gates/hook-actually-fires/check.sh +183 -0
- package/kit/gates/hook-actually-fires/gate.yml +15 -0
- package/kit/gates/hook-actually-fires/green/.claude/hooks/hooks.json +3 -0
- package/kit/gates/hook-actually-fires/green/.claude/settings.json +74 -0
- package/kit/gates/hook-actually-fires/green/.claude/settings.local.json +74 -0
- package/kit/gates/hook-actually-fires/red/.claude/hooks/hooks.json +4 -0
- package/kit/gates/hook-actually-fires/red/.claude/settings.json +53 -0
- package/kit/gates/no-phantom-package/README.md +84 -0
- package/kit/gates/no-phantom-package/check.sh +161 -0
- package/kit/gates/no-phantom-package/gate.yml +20 -0
- package/kit/gates/no-phantom-package/green/AGENTS.md +15 -0
- package/kit/gates/no-phantom-package/red/AGENTS.md +15 -0
- package/kit/gates/no-print-in-prod/README.md +33 -39
- package/kit/gates/no-print-in-prod/gate.yml +14 -6
- package/kit/gates/personal-config-not-shared/README.md +66 -0
- package/kit/gates/personal-config-not-shared/check.sh +103 -0
- package/kit/gates/personal-config-not-shared/gate.yml +16 -0
- package/kit/gates/personal-config-not-shared/green/.aqk-tracked +9 -0
- package/kit/gates/personal-config-not-shared/red/.aqk-tracked +6 -0
- package/kit/gates/secrets-not-in-code/check.sh +13 -1
- package/kit/gates/swallowed-error/README.md +36 -18
- package/kit/gates/swallowed-error/gate.yml +13 -3
- package/kit/gates/test-has-assertion/check.sh +13 -1
- package/kit/gates/test-not-adjusted/README.md +79 -0
- package/kit/gates/test-not-adjusted/check.sh +136 -0
- package/kit/gates/test-not-adjusted/gate.yml +19 -0
- package/kit/gates/test-not-adjusted/green/after/calc.py +6 -0
- package/kit/gates/test-not-adjusted/green/after/tests/test_calc.py +9 -0
- package/kit/gates/test-not-adjusted/green/before/calc.py +2 -0
- package/kit/gates/test-not-adjusted/green/before/tests/test_calc.py +5 -0
- package/kit/gates/test-not-adjusted/red/after/calc.py +2 -0
- package/kit/gates/test-not-adjusted/red/after/tests/test_calc.py +5 -0
- package/kit/gates/test-not-adjusted/red/before/calc.py +2 -0
- package/kit/gates/test-not-adjusted/red/before/tests/test_calc.py +7 -0
- package/kit/gates/todo-without-task/README.md +6 -0
- package/kit/gates/todo-without-task/check.sh +13 -1
- package/kit/ratchet/ratchet.sh +70 -2
- package/kit/rules/general.md +9 -0
- package/kit/rules-en/general.md +82 -0
- package/kit/rules-en/security.md +33 -0
- package/kit/rules-en/testing.md +48 -0
- package/llms.txt +1 -1
- package/package.json +3 -2
- package/tool/commands/badge.mjs +7 -1
- package/tool/commands/doctor.mjs +49 -9
- package/tool/commands/gates.mjs +10 -4
- package/tool/commands/project.mjs +8 -1
- package/tool/commands/prove.mjs +67 -0
- package/tool/commands/report.mjs +4 -1
- package/tool/i18n/en-docs.mjs +70 -0
- package/tool/i18n/en.mjs +49 -54
- package/tool/i18n/ru-docs.mjs +70 -0
- package/tool/i18n/ru.mjs +48 -54
- package/tool/i18n/templates-en.mjs +1 -1
- package/tool/i18n/templates-ru.mjs +1 -1
- package/tool/lib/core.mjs +7 -1
- package/tool/lib/manifest.mjs +36 -5
- package/tool/lib/prove.mjs +160 -0
- package/tool/lib/repo.mjs +31 -2
- package/tool/lib/scope.mjs +37 -2
- package/tool/lib/templates.mjs +2 -0
- package/tool/program.mjs +5 -0
- package/tool/selfcheck/gates.sh +66 -0
- package/tool/selfcheck/mutation.sh +21 -1
- package/tool/selfcheck/smoke.sh +279 -39
- package/tool/selfcheck/units-level.mjs +60 -0
- package/tool/selfcheck/units.mjs +113 -2
- package/kit/gates/no-print-in-prod/check.sh +0 -38
- package/kit/gates/no-print-in-prod/green/docs.ts +0 -15
- package/kit/gates/no-print-in-prod/green/main.go +0 -8
- package/kit/gates/no-print-in-prod/green/main.rs +0 -4
- package/kit/gates/no-print-in-prod/red/main.go +0 -8
- package/kit/gates/no-print-in-prod/red/main.rs +0 -4
- package/kit/gates/swallowed-error/check.sh +0 -54
- package/kit/gates/swallowed-error/green/run.js +0 -8
- package/kit/gates/swallowed-error/red/run.js +0 -3
- /package/kit/gates/commit-explains-itself/green/{COMMIT_MSG → .aqk-commit-msg} +0 -0
- /package/kit/gates/commit-explains-itself/red/{COMMIT_MSG → .aqk-commit-msg} +0 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// tool/i18n/en-docs.mjs — text that ends up IN A FILE, not in the terminal.
|
|
2
|
+
//
|
|
3
|
+
// WHY SEPARATE. The string catalogue outgrew its own 500-line limit — caught by our own
|
|
4
|
+
// file-size-limit gate. The seam follows meaning rather than the midpoint: here is what the
|
|
5
|
+
// program WRITES (comments in .aqk.yml, level names, the report form), and in en.mjs what it
|
|
6
|
+
// SAYS. These texts live differently: the first is read months later in someone's repository,
|
|
7
|
+
// the second for one second in a terminal.
|
|
8
|
+
|
|
9
|
+
const enDocs = {
|
|
10
|
+
manifestDoc: {
|
|
11
|
+
head: [
|
|
12
|
+
"# .aqk.yml — the Agent Quality Kit manifest",
|
|
13
|
+
"# What this is: a machine-readable description of how agents live in this repository.",
|
|
14
|
+
"# `aqk doctor` computes the compliance level. An empty field = the level is not reached,",
|
|
15
|
+
"# and that is honest: filling it with placeholders is pointless, files are checked, not words.",
|
|
16
|
+
],
|
|
17
|
+
entry: "# AQK-0 — what the agent reads first.",
|
|
18
|
+
rules: "# AQK-1 — where the standards are and which checks are mandatory.",
|
|
19
|
+
gates: [
|
|
20
|
+
" # name: a command returning 0 or non-zero. An empty declaration protects nothing and is",
|
|
21
|
+
' # rejected by the "a declared gate runs" check — hence examples here, not placeholders.',
|
|
22
|
+
' # lint: "ruff check ."',
|
|
23
|
+
' # test: "pytest -q"',
|
|
24
|
+
" # To install a ready entry from the catalogue together with its samples: aqk add <name>",
|
|
25
|
+
],
|
|
26
|
+
samples: [
|
|
27
|
+
"# AQK-2 — what proves the gates work, and where the debt registries are.",
|
|
28
|
+
"# samples: the directory with red and green samples (a gate must go red on the first and",
|
|
29
|
+
"# stay quiet on the second). ratchets: lists of known violations that may only get",
|
|
30
|
+
"# shorter.",
|
|
31
|
+
],
|
|
32
|
+
lessons: "# AQK-3 — where lessons accumulate. A path or an address.",
|
|
33
|
+
advisory: [
|
|
34
|
+
"# Advisory gates: they show findings but do not fail the run. The third way to introduce",
|
|
35
|
+
"# a rule, next to the ratchet and the big clean-up. The list is named on every run:",
|
|
36
|
+
"# an advisory gate everyone forgot about is a switched-off check.",
|
|
37
|
+
"# advisory:",
|
|
38
|
+
"# - complexity-limit",
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
levels: [
|
|
42
|
+
{
|
|
43
|
+
title: "a manifest and an entry point",
|
|
44
|
+
need: "create .aqk.yml and point entry at the file an agent reads first (AGENTS.md)",
|
|
45
|
+
gives: "any tool understands what to read in this repository",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
title: "rules and working gates",
|
|
49
|
+
need: "set rules (the standards directory) and fill at least one gate in gates with a real command",
|
|
50
|
+
gives: "checks are declared as commands, not described in prose",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
title: "gates are proven, debt is under a ratchet",
|
|
54
|
+
need: "set samples (red and green gate samples) and ratchets (debt registries)",
|
|
55
|
+
gives: "the gate has proven it catches defects and stays quiet on correct code",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
title: "lessons come back into the work",
|
|
59
|
+
need: "set lessons — the path or address of a journal where every incident yields a conclusion",
|
|
60
|
+
gives: "the project learns: the same bruise is not collected twice",
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
report: {
|
|
64
|
+
title: "aqk doctor --run",
|
|
65
|
+
version: "version",
|
|
66
|
+
level: "level",
|
|
67
|
+
summary: (ok, all) => `total: ${ok} of ${all} green`,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
export { enDocs };
|
package/tool/i18n/en.mjs
CHANGED
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
// Правка здесь обязана иметь пару в ru.mjs с тем же ключом: расхождение ловит модульная
|
|
4
4
|
// проверка «оба каталога несут одни и те же ключи».
|
|
5
5
|
|
|
6
|
+
import { enDocs } from "./en-docs.mjs";
|
|
7
|
+
|
|
6
8
|
import { templates } from "./templates-en.mjs";
|
|
7
9
|
|
|
8
10
|
export const en = {
|
|
11
|
+
...enDocs,
|
|
9
12
|
templates,
|
|
10
13
|
help: {
|
|
11
14
|
tagline: "tooling for building software with agents",
|
|
@@ -13,6 +16,7 @@ export const en = {
|
|
|
13
16
|
init: "lay the rules and guides into the current project",
|
|
14
17
|
initForce: "overwrite files that already exist",
|
|
15
18
|
start: "no code yet: day-zero guards and the order of work",
|
|
19
|
+
prove: "prove the gates catch a defect: each against its own red and green sample",
|
|
16
20
|
doctor: "check what is laid out and what is missing",
|
|
17
21
|
doctorRun: "and also run the declared gates",
|
|
18
22
|
doctorSince: "the same, but show only what the diff against a ref introduced",
|
|
@@ -41,6 +45,8 @@ export const en = {
|
|
|
41
45
|
emptyCommands: (n) => `AGENTS.md has ${n} unfilled commands.`,
|
|
42
46
|
emptyCommandsWhy: "An agent cannot execute an empty line.",
|
|
43
47
|
|
|
48
|
+
levelUnproven: (cmd) => `not proven: ${cmd}`,
|
|
49
|
+
gatesDoNotCatch: (n, cmd) => `gates that do not catch a defect: ${n}. Details: ${cmd}`,
|
|
44
50
|
levelHeading: "AQK compliance level",
|
|
45
51
|
levelNone: "none",
|
|
46
52
|
levelNotSet: "Level: the standard is not set up in this repository.",
|
|
@@ -71,6 +77,10 @@ export const en = {
|
|
|
71
77
|
sinceBadRef: (ref) => `cannot compare against "${ref}": no such ref, or this is not a git repository`,
|
|
72
78
|
notScopable: "output carries no paths — cannot be narrowed by diff, left red",
|
|
73
79
|
outsideDiff: (n) => `findings exist, but outside the diff (${n})`,
|
|
80
|
+
advisoryMark: "advisory — shown, the run was not failed",
|
|
81
|
+
advisorySummary: (names) =>
|
|
82
|
+
`advisory and red: ${names.join(", ")}. These are switched-off checks: ` +
|
|
83
|
+
`either fix them and drop them from advisory, or admit the rule does not exist.`,
|
|
74
84
|
runHeading: "Running the declared gates",
|
|
75
85
|
timeout: "did not finish within 5 minutes",
|
|
76
86
|
exitCode: (code) => `exit ${code}`,
|
|
@@ -132,6 +142,8 @@ export const en = {
|
|
|
132
142
|
has_deps: ["no dependency file in sight", "dependencies are declared"],
|
|
133
143
|
has_tests: ["no tests in sight", "tests exist"],
|
|
134
144
|
has_env: ["no environment file", "an environment file exists"],
|
|
145
|
+
has_agent_config: ["the agent was never configured here", "agent settings exist"],
|
|
146
|
+
has_agent_entry: ["no entry point for an agent here", "an entry point for an agent exists"],
|
|
135
147
|
has_ui: ["no stylesheets or UI components in sight", "a UI exists: stylesheets or components"],
|
|
136
148
|
},
|
|
137
149
|
},
|
|
@@ -160,7 +172,13 @@ export const en = {
|
|
|
160
172
|
|
|
161
173
|
add: {
|
|
162
174
|
noSuchGate: (slug, cmd) => `No such gate: ${slug}\nThe applicable ones — ${cmd}`,
|
|
163
|
-
|
|
175
|
+
toolMissing: (slug, missing) =>
|
|
176
|
+
`Entry ${slug} needs ${missing.join(" and ")}, which is not on this machine.\n fix: install ${missing.join(" and ")} and try again — this entry delegates to a ready-made tool by design and has no check of its own.`,
|
|
177
|
+
noRecipe: (slug, stack, missing) =>
|
|
178
|
+
`Entry ${slug} has no command for ${stack} and no portable one.` +
|
|
179
|
+
(Array.isArray(missing) && missing.length
|
|
180
|
+
? `\n fix: install ${missing.join(" or ")} and try again — this entry delegates to a ready-made tool by design and has no check of its own.`
|
|
181
|
+
: `\n fix: add a recipe for your stack to this entry's gate.yml, or pick another entry.`),
|
|
164
182
|
thisStack: "this stack",
|
|
165
183
|
needName: (cmd, doctor) => `Name the gate: ${cmd}. The list — ${doctor}`,
|
|
166
184
|
noManifest: (cmd) => `No .aqk.yml — run ${cmd} first`,
|
|
@@ -213,7 +231,14 @@ export const en = {
|
|
|
213
231
|
registryHead: (slug, stamp) =>
|
|
214
232
|
`# Debt registry: ${slug}\n` +
|
|
215
233
|
`# Captured ${stamp}. This list may ONLY get shorter.\n` +
|
|
216
|
-
`# A new violation turns the gate red; a fixed one is struck out automatically.\n
|
|
234
|
+
`# A new violation turns the gate red; a fixed one is struck out automatically.\n` +
|
|
235
|
+
`#\n` +
|
|
236
|
+
`# Debt with no goal and no deadline never ends. The goal is how many violations count as\n` +
|
|
237
|
+
`# paid off; on reaching it the ratchet tells you to remove the wrapper. The deadline is\n` +
|
|
238
|
+
`# optional, but if set, debt still open past that date turns the gate red — a deadline\n` +
|
|
239
|
+
`# without a consequence is not a deadline.\n` +
|
|
240
|
+
`# aqk-goal: 0\n` +
|
|
241
|
+
`# aqk-deadline:\n`,
|
|
217
242
|
recorded: (n) => `${n} violations recorded as debt`,
|
|
218
243
|
libCopied: "wrapper copied into the project",
|
|
219
244
|
wrapped: "command wrapped in the ratchet",
|
|
@@ -295,6 +320,8 @@ export const en = {
|
|
|
295
320
|
|
|
296
321
|
init: {
|
|
297
322
|
noDocs: (dir) => `Guides not found: ${dir}\nLooks like the package is not fully installed.`,
|
|
323
|
+
docsRu:
|
|
324
|
+
"the guides in .aqk/docs/ are in Russian — a deliberate decision, not a broken install.\n The rules in .aqk/rules/ are in English; the guides are prose an agent may ignore anyway,\n and what a machine holds lives in .aqk.yml and the gates. Translation waits for someone who needs it.",
|
|
298
325
|
created: (n) => `created (${n}):`,
|
|
299
326
|
andMore: (n) => `… and ${n} more`,
|
|
300
327
|
kept: (n) => `already there, left untouched (${n}):`,
|
|
@@ -386,31 +413,27 @@ export const en = {
|
|
|
386
413
|
nextWhy: "— run everything that is declared",
|
|
387
414
|
},
|
|
388
415
|
|
|
389
|
-
manifestDoc: {
|
|
390
|
-
head: [
|
|
391
|
-
"# .aqk.yml — the Agent Quality Kit manifest",
|
|
392
|
-
"# What this is: a machine-readable description of how agents live in this repository.",
|
|
393
|
-
"# `aqk doctor` computes the compliance level. An empty field = the level is not reached,",
|
|
394
|
-
"# and that is honest: filling it with placeholders is pointless, files are checked, not words.",
|
|
395
|
-
],
|
|
396
|
-
entry: "# AQK-0 — what the agent reads first.",
|
|
397
|
-
rules: "# AQK-1 — where the standards are and which checks are mandatory.",
|
|
398
|
-
gates: [
|
|
399
|
-
" # name: a command returning 0 or non-zero. An empty declaration protects nothing and is",
|
|
400
|
-
' # rejected by the "a declared gate runs" check — hence examples here, not placeholders.',
|
|
401
|
-
' # lint: "ruff check ."',
|
|
402
|
-
' # test: "pytest -q"',
|
|
403
|
-
" # To install a ready entry from the catalogue together with its samples: aqk add <name>",
|
|
404
|
-
],
|
|
405
|
-
samples: [
|
|
406
|
-
"# AQK-2 — what proves the gates work, and where the debt registries are.",
|
|
407
|
-
"# samples: the directory with red and green samples (a gate must go red on the first and",
|
|
408
|
-
"# stay quiet on the second). ratchets: lists of known violations that may only get",
|
|
409
|
-
"# shorter.",
|
|
410
|
-
],
|
|
411
|
-
lessons: "# AQK-3 — where lessons accumulate. A path or an address.",
|
|
412
|
-
},
|
|
413
416
|
|
|
417
|
+
prove: {
|
|
418
|
+
title: "aqk prove — proving the gates",
|
|
419
|
+
running: "proving the gates against their samples…",
|
|
420
|
+
proven: (n) => `proven: ${n}`,
|
|
421
|
+
broken: (n) => `do not catch: ${n}`,
|
|
422
|
+
unprovable: (n) => `nothing to prove with: ${n}`,
|
|
423
|
+
okRed: "red on the red sample, silent on the green one",
|
|
424
|
+
redPassed: "stayed silent on the RED sample — the gate does not catch the defect",
|
|
425
|
+
greenFailed: "went red on the GREEN sample — the gate complains about working code",
|
|
426
|
+
empty: "the command is empty — a declaration without a command protects nothing",
|
|
427
|
+
noSamples: "no samples — nothing to prove with",
|
|
428
|
+
noTarget: "no place to substitute the sample directory — the command was written by hand",
|
|
429
|
+
otherRecipe: (lang) => `the samples are written for the "${lang}" recipe, another one is installed — nothing to prove with`,
|
|
430
|
+
noGates: "no gates declared — nothing to prove",
|
|
431
|
+
noSamplesDir: "the samples field in .aqk.yml is empty — nowhere to look for samples",
|
|
432
|
+
nothingProven:
|
|
433
|
+
"not a single gate is proven. A level above AQK-1 would mean trust in the author, not a fact:\n a project whose gate is `true` would pass it exactly like a project with real protection.",
|
|
434
|
+
fix: (cmd) => `fix: install a catalogue entry together with its samples — ${cmd}`,
|
|
435
|
+
heading: "Proving the gates",
|
|
436
|
+
},
|
|
414
437
|
badge: {
|
|
415
438
|
noManifest: (cmd) => `No .aqk.yml — there is no level yet. Start with ${cmd}`,
|
|
416
439
|
notReached: (cmd) => `AQK-0 is not reached — there is nothing to put on a badge. What is missing: ${cmd}`,
|
|
@@ -459,33 +482,5 @@ export const en = {
|
|
|
459
482
|
},
|
|
460
483
|
},
|
|
461
484
|
|
|
462
|
-
levels: [
|
|
463
|
-
{
|
|
464
|
-
title: "a manifest and an entry point",
|
|
465
|
-
need: "create .aqk.yml and point entry at the file an agent reads first (AGENTS.md)",
|
|
466
|
-
gives: "any tool understands what to read in this repository",
|
|
467
|
-
},
|
|
468
|
-
{
|
|
469
|
-
title: "rules and working gates",
|
|
470
|
-
need: "set rules (the standards directory) and fill at least one gate in gates with a real command",
|
|
471
|
-
gives: "checks are declared as commands, not described in prose",
|
|
472
|
-
},
|
|
473
|
-
{
|
|
474
|
-
title: "gates are proven, debt is under a ratchet",
|
|
475
|
-
need: "set samples (red and green gate samples) and ratchets (debt registries)",
|
|
476
|
-
gives: "the gate has proven it catches defects and stays quiet on correct code",
|
|
477
|
-
},
|
|
478
|
-
{
|
|
479
|
-
title: "lessons come back into the work",
|
|
480
|
-
need: "set lessons — the path or address of a journal where every incident yields a conclusion",
|
|
481
|
-
gives: "the project learns: the same bruise is not collected twice",
|
|
482
|
-
},
|
|
483
|
-
],
|
|
484
485
|
|
|
485
|
-
report: {
|
|
486
|
-
title: "aqk doctor --run",
|
|
487
|
-
version: "version",
|
|
488
|
-
level: "level",
|
|
489
|
-
summary: (ok, all) => `total: ${ok} of ${all} green`,
|
|
490
|
-
},
|
|
491
486
|
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// tool/i18n/ru-docs.mjs — тексты, которые уходят В ФАЙЛ, а не в терминал.
|
|
2
|
+
//
|
|
3
|
+
// ЗАЧЕМ ОТДЕЛЬНО. Каталог строк перерос собственный предел в 500 строк — его поймал наш же
|
|
4
|
+
// гейт file-size-limit. Шов выбран по смыслу, а не пополам: здесь то, что программа ПИШЕТ
|
|
5
|
+
// (комментарии в .aqk.yml, названия ступеней, форма отчёта), а в ru.mjs — то, что она ГОВОРИТ.
|
|
6
|
+
// У этих текстов разная жизнь: первые читает человек в своём репозитории месяцы спустя,
|
|
7
|
+
// вторые — он же в терминале, одну секунду.
|
|
8
|
+
|
|
9
|
+
const ruDocs = {
|
|
10
|
+
manifestDoc: {
|
|
11
|
+
head: [
|
|
12
|
+
"# .aqk.yml — манифест Agent Quality Kit",
|
|
13
|
+
"# Что это: машиночитаемое описание того, как в этом репозитории живут агенты.",
|
|
14
|
+
"# Уровень соответствия считает `aqk doctor`. Пустое поле = ступень не пройдена,",
|
|
15
|
+
"# и это честно: заполнять заглушками бессмысленно, проверяются файлы, а не слова.",
|
|
16
|
+
],
|
|
17
|
+
entry: "# AQK-0 — что агент читает первым.",
|
|
18
|
+
rules: "# AQK-1 — где стандарты и какие проверки обязательны.",
|
|
19
|
+
gates: [
|
|
20
|
+
" # Имя: команда, возвращающая 0 или не 0. Пустое объявление защиты не даёт и бракуется",
|
|
21
|
+
" # проверкой «объявленный гейт запускается» — поэтому здесь примеры, а не заготовки.",
|
|
22
|
+
' # lint: "ruff check ."',
|
|
23
|
+
' # test: "pytest -q"',
|
|
24
|
+
" # Поставить готовую запись из каталога вместе с образцами: aqk add <имя>",
|
|
25
|
+
],
|
|
26
|
+
samples: [
|
|
27
|
+
"# AQK-2 — чем доказано, что гейты работают, и где реестры долга.",
|
|
28
|
+
"# samples: каталог с красными и зелёными образцами (гейт обязан краснеть на первом",
|
|
29
|
+
"# и молчать на втором). ratchets: списки известных нарушений, которые могут только",
|
|
30
|
+
"# укорачиваться.",
|
|
31
|
+
],
|
|
32
|
+
lessons: "# AQK-3 — где копятся уроки. Путь или адрес.",
|
|
33
|
+
advisory: [
|
|
34
|
+
"# Совещательные гейты: показывают находки, но не роняют прогон. Третий способ ввести",
|
|
35
|
+
"# правило — рядом с храповиком и с большой чисткой. Список называется в каждом прогоне:",
|
|
36
|
+
"# совещательный гейт, о котором забыли, — это выключенная проверка.",
|
|
37
|
+
"# advisory:",
|
|
38
|
+
"# - complexity-limit",
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
levels: [
|
|
42
|
+
{
|
|
43
|
+
title: "манифест и точка входа",
|
|
44
|
+
need: "создай .aqk.yml и укажи в entry файл, который агент читает первым (AGENTS.md)",
|
|
45
|
+
gives: "любой инструмент понимает, что читать в этом репозитории",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
title: "правила и работающие гейты",
|
|
49
|
+
need: "укажи rules (каталог стандартов) и заполни хотя бы один гейт в gates реальной командой",
|
|
50
|
+
gives: "проверки объявлены командами, а не описаны словами",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
title: "гейты доказаны, долг под храповиком",
|
|
54
|
+
need: "заведи samples (красные и зелёные образцы гейтов) и ratchets (реестры долга)",
|
|
55
|
+
gives: "гейт доказал, что ловит брак и молчит на исправном коде",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
title: "уроки возвращаются в работу",
|
|
59
|
+
need: "укажи lessons — путь или адрес журнала, где каждый инцидент даёт вывод",
|
|
60
|
+
gives: "проект учится: одна и та же шишка не набивается дважды",
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
report: {
|
|
64
|
+
title: "aqk doctor --run",
|
|
65
|
+
version: "версия",
|
|
66
|
+
level: "уровень",
|
|
67
|
+
summary: (ok, all) => `итого: ${ok} из ${all} зелёных`,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
export { ruDocs };
|
package/tool/i18n/ru.mjs
CHANGED
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
// проверка «оба каталога несут одни и те же ключи». Иначе один язык молча отстаёт, а
|
|
5
5
|
// обещание «два языка» превращается в слова.
|
|
6
6
|
|
|
7
|
+
import { ruDocs } from "./ru-docs.mjs";
|
|
8
|
+
|
|
7
9
|
import { templates } from "./templates-ru.mjs";
|
|
8
10
|
|
|
9
11
|
export const ru = {
|
|
12
|
+
...ruDocs,
|
|
10
13
|
templates,
|
|
11
14
|
help: {
|
|
12
15
|
tagline: "оснастка для разработки с агентами",
|
|
@@ -14,6 +17,7 @@ export const ru = {
|
|
|
14
17
|
init: "разложить правила и методички в текущий проект",
|
|
15
18
|
initForce: "перезаписать уже существующие файлы",
|
|
16
19
|
start: "кода ещё нет: сторожа дня 0 и порядок работы",
|
|
20
|
+
prove: "доказать, что гейты ловят брак: каждый по своему красному и зелёному образцу",
|
|
17
21
|
doctor: "проверить, что разложено и чего не хватает",
|
|
18
22
|
doctorRun: "ещё и запустить объявленные гейты",
|
|
19
23
|
doctorSince: "то же, но показать только то, что внёс диф относительно ссылки",
|
|
@@ -42,6 +46,8 @@ export const ru = {
|
|
|
42
46
|
emptyCommands: (n) => `В AGENTS.md ${n} незаполненных команд.`,
|
|
43
47
|
emptyCommandsWhy: "Агент не может выполнить пустую строку.",
|
|
44
48
|
|
|
49
|
+
levelUnproven: (cmd) => `не доказано: ${cmd}`,
|
|
50
|
+
gatesDoNotCatch: (n, cmd) => `гейтов, не ловящих брак: ${n}. Подробности: ${cmd}`,
|
|
45
51
|
levelHeading: "Уровень соответствия AQK",
|
|
46
52
|
levelNone: "нет",
|
|
47
53
|
levelNotSet: "Уровень: стандарт в этом репозитории не заведён.",
|
|
@@ -72,6 +78,10 @@ export const ru = {
|
|
|
72
78
|
sinceBadRef: (ref) => `не могу сравнить с «${ref}»: такой ссылки нет или это не репозиторий git`,
|
|
73
79
|
notScopable: "вывод без путей — дифом не сужается, оставлен красным",
|
|
74
80
|
outsideDiff: (n) => `находки есть, но вне дифа (${n})`,
|
|
81
|
+
advisoryMark: "совещательный — показано, прогон не уронен",
|
|
82
|
+
advisorySummary: (names) =>
|
|
83
|
+
`совещательные и красные: ${names.join(", ")}. Это выключенные проверки: ` +
|
|
84
|
+
`либо почини и убери из advisory, либо признай, что правила нет.`,
|
|
75
85
|
runHeading: "Прогон объявленных гейтов",
|
|
76
86
|
timeout: "не уложился в 5 минут",
|
|
77
87
|
exitCode: (code) => `код ${code}`,
|
|
@@ -138,6 +148,8 @@ export const ru = {
|
|
|
138
148
|
has_deps: ["не видно файла зависимостей", "зависимости объявлены"],
|
|
139
149
|
has_tests: ["не видно тестов", "тесты есть"],
|
|
140
150
|
has_env: ["нет файла окружения", "файл окружения есть"],
|
|
151
|
+
has_agent_config: ["агента здесь не настраивали", "настройки агента есть"],
|
|
152
|
+
has_agent_entry: ["свода для агента здесь нет", "свод для агента есть"],
|
|
141
153
|
has_ui: ["не видно стилей и компонентов интерфейса", "интерфейс есть: стили или компоненты"],
|
|
142
154
|
},
|
|
143
155
|
},
|
|
@@ -167,7 +179,13 @@ export const ru = {
|
|
|
167
179
|
|
|
168
180
|
add: {
|
|
169
181
|
noSuchGate: (slug, cmd) => `Нет такого гейта: ${slug}\nСписок применимых — ${cmd}`,
|
|
170
|
-
|
|
182
|
+
toolMissing: (slug, missing) =>
|
|
183
|
+
`Записи ${slug} нужен ${missing.join(" и ")}, а его нет на этой машине.\n почини: поставь ${missing.join(" и ")} и повтори — запись делегирует готовому инструменту по устройству, своей проверки у неё нет.`,
|
|
184
|
+
noRecipe: (slug, stack, missing) =>
|
|
185
|
+
`У записи ${slug} нет команды ни под ${stack}, ни общей.` +
|
|
186
|
+
(Array.isArray(missing) && missing.length
|
|
187
|
+
? `\n почини: поставь ${missing.join(" или ")} и повтори — запись делегирует готовому инструменту по устройству, своей проверки у неё нет.`
|
|
188
|
+
: `\n почини: добавь рецепт под свой стек в gate.yml этой записи либо возьми другую.`),
|
|
171
189
|
thisStack: "этот стек",
|
|
172
190
|
needName: (cmd, doctor) => `Укажи имя гейта: ${cmd}. Список — ${doctor}`,
|
|
173
191
|
noManifest: (cmd) => `Нет .aqk.yml — сначала ${cmd}`,
|
|
@@ -220,7 +238,13 @@ export const ru = {
|
|
|
220
238
|
registryHead: (slug, stamp) =>
|
|
221
239
|
`# Реестр долга: ${slug}\n` +
|
|
222
240
|
`# Снят ${stamp}. Список разрешается ТОЛЬКО укорачивать.\n` +
|
|
223
|
-
`# Новое нарушение красит гейт; исправленное вычёркивается автоматически.\n
|
|
241
|
+
`# Новое нарушение красит гейт; исправленное вычёркивается автоматически.\n` +
|
|
242
|
+
`#\n` +
|
|
243
|
+
`# Долг без цели и срока не кончается. Цель — сколько нарушений считать погашенным долгом;\n` +
|
|
244
|
+
`# по достижении храповик скажет убрать обёртку. Срок необязателен, но если он есть,\n` +
|
|
245
|
+
`# после этой даты непогашенный долг красит гейт — срок без последствия не срок.\n` +
|
|
246
|
+
`# aqk-goal: 0\n` +
|
|
247
|
+
`# aqk-deadline:\n`,
|
|
224
248
|
recorded: (n) => `${n} нарушений записано долгом`,
|
|
225
249
|
libCopied: "обёртка скопирована в проект",
|
|
226
250
|
wrapped: "команда завёрнута в храповик",
|
|
@@ -302,6 +326,8 @@ export const ru = {
|
|
|
302
326
|
|
|
303
327
|
init: {
|
|
304
328
|
noDocs: (dir) => `Не найден корпус методичек: ${dir}\nПохоже, пакет установлен не полностью.`,
|
|
329
|
+
docsRu:
|
|
330
|
+
"методички в .aqk/docs/ остаются на русском — решение, а не недоделка.",
|
|
305
331
|
created: (n) => `создано (${n}):`,
|
|
306
332
|
andMore: (n) => `… и ещё ${n}`,
|
|
307
333
|
kept: (n) => `уже были на месте, не тронуты (${n}):`,
|
|
@@ -393,31 +419,27 @@ export const ru = {
|
|
|
393
419
|
nextWhy: "— прогнать всё, что объявлено",
|
|
394
420
|
},
|
|
395
421
|
|
|
396
|
-
manifestDoc: {
|
|
397
|
-
head: [
|
|
398
|
-
"# .aqk.yml — манифест Agent Quality Kit",
|
|
399
|
-
"# Что это: машиночитаемое описание того, как в этом репозитории живут агенты.",
|
|
400
|
-
"# Уровень соответствия считает `aqk doctor`. Пустое поле = ступень не пройдена,",
|
|
401
|
-
"# и это честно: заполнять заглушками бессмысленно, проверяются файлы, а не слова.",
|
|
402
|
-
],
|
|
403
|
-
entry: "# AQK-0 — что агент читает первым.",
|
|
404
|
-
rules: "# AQK-1 — где стандарты и какие проверки обязательны.",
|
|
405
|
-
gates: [
|
|
406
|
-
" # Имя: команда, возвращающая 0 или не 0. Пустое объявление защиты не даёт и бракуется",
|
|
407
|
-
" # проверкой «объявленный гейт запускается» — поэтому здесь примеры, а не заготовки.",
|
|
408
|
-
' # lint: "ruff check ."',
|
|
409
|
-
' # test: "pytest -q"',
|
|
410
|
-
" # Поставить готовую запись из каталога вместе с образцами: aqk add <имя>",
|
|
411
|
-
],
|
|
412
|
-
samples: [
|
|
413
|
-
"# AQK-2 — чем доказано, что гейты работают, и где реестры долга.",
|
|
414
|
-
"# samples: каталог с красными и зелёными образцами (гейт обязан краснеть на первом",
|
|
415
|
-
"# и молчать на втором). ratchets: списки известных нарушений, которые могут только",
|
|
416
|
-
"# укорачиваться.",
|
|
417
|
-
],
|
|
418
|
-
lessons: "# AQK-3 — где копятся уроки. Путь или адрес.",
|
|
419
|
-
},
|
|
420
422
|
|
|
423
|
+
prove: {
|
|
424
|
+
title: "aqk prove — доказательство гейтов",
|
|
425
|
+
running: "доказываю гейты по их образцам…",
|
|
426
|
+
proven: (n) => `доказано: ${n}`,
|
|
427
|
+
broken: (n) => `не ловят: ${n}`,
|
|
428
|
+
unprovable: (n) => `нечем доказать: ${n}`,
|
|
429
|
+
okRed: "краснеет на красном, молчит на зелёном",
|
|
430
|
+
redPassed: "промолчал на КРАСНОМ образце — гейт не ловит брак",
|
|
431
|
+
greenFailed: "покраснел на ЗЕЛЁНОМ образце — гейт ругается на исправный код",
|
|
432
|
+
empty: "команда пустая — объявление без команды защиты не даёт",
|
|
433
|
+
noSamples: "нет образцов — доказать нечем",
|
|
434
|
+
noTarget: "не видно, куда подставить каталог образца — команда написана руками",
|
|
435
|
+
otherRecipe: (lang) => `образцы написаны под рецепт «${lang}», а стоит другой — доказать нечем`,
|
|
436
|
+
noGates: "гейтов не объявлено — доказывать нечего",
|
|
437
|
+
noSamplesDir: "в .aqk.yml не заполнено поле samples — образцы искать негде",
|
|
438
|
+
nothingProven:
|
|
439
|
+
"ни один гейт не доказан. Уровень выше AQK-1 означал бы доверие к автору, а не факт:\n проект с гейтом «true» прошёл бы его так же, как проект с настоящей защитой.",
|
|
440
|
+
fix: (cmd) => `почини: поставь запись каталога вместе с образцами — ${cmd}`,
|
|
441
|
+
heading: "Доказательство гейтов",
|
|
442
|
+
},
|
|
421
443
|
badge: {
|
|
422
444
|
noManifest: (cmd) => `Нет .aqk.yml — уровня ещё нет. Начни с ${cmd}`,
|
|
423
445
|
notReached: (cmd) => `AQK-0 не достигнут — значок выдавать не за что. Чего не хватает: ${cmd}`,
|
|
@@ -466,33 +488,5 @@ export const ru = {
|
|
|
466
488
|
},
|
|
467
489
|
},
|
|
468
490
|
|
|
469
|
-
levels: [
|
|
470
|
-
{
|
|
471
|
-
title: "манифест и точка входа",
|
|
472
|
-
need: "создай .aqk.yml и укажи в entry файл, который агент читает первым (AGENTS.md)",
|
|
473
|
-
gives: "любой инструмент понимает, что читать в этом репозитории",
|
|
474
|
-
},
|
|
475
|
-
{
|
|
476
|
-
title: "правила и работающие гейты",
|
|
477
|
-
need: "укажи rules (каталог стандартов) и заполни хотя бы один гейт в gates реальной командой",
|
|
478
|
-
gives: "проверки объявлены командами, а не описаны словами",
|
|
479
|
-
},
|
|
480
|
-
{
|
|
481
|
-
title: "гейты доказаны, долг под храповиком",
|
|
482
|
-
need: "заведи samples (красные и зелёные образцы гейтов) и ratchets (реестры долга)",
|
|
483
|
-
gives: "гейт доказал, что ловит брак и молчит на исправном коде",
|
|
484
|
-
},
|
|
485
|
-
{
|
|
486
|
-
title: "уроки возвращаются в работу",
|
|
487
|
-
need: "укажи lessons — путь или адрес журнала, где каждый инцидент даёт вывод",
|
|
488
|
-
gives: "проект учится: одна и та же шишка не набивается дважды",
|
|
489
|
-
},
|
|
490
|
-
],
|
|
491
491
|
|
|
492
|
-
report: {
|
|
493
|
-
title: "aqk doctor --run",
|
|
494
|
-
version: "версия",
|
|
495
|
-
level: "уровень",
|
|
496
|
-
summary: (ok, all) => `итого: ${ok} из ${all} зелёных`,
|
|
497
|
-
},
|
|
498
492
|
};
|
|
@@ -18,7 +18,7 @@ const AGENTS_MD = `# AGENTS.md
|
|
|
18
18
|
- **Only the files the task is about.** No fixing things "while we are here". <!-- aqk: human -->
|
|
19
19
|
- **Done = proven.** Name the arbiter: a test, a live run, a check against the source. <!-- aqk: human -->
|
|
20
20
|
"Looks like it works" is not done.
|
|
21
|
-
- **Never swallow an error.** Either handled and logged, or re-raised. <!-- aqk:
|
|
21
|
+
- **Never swallow an error.** Either handled and logged, or re-raised. <!-- aqk: human -->
|
|
22
22
|
- **A fork in the road is a question for a human.** Departing from an agreed decision is not <!-- aqk: human -->
|
|
23
23
|
documented with a code comment.
|
|
24
24
|
- **Report on your work with the kit with a command, not with words.** When you are done, run <!-- aqk: human -->
|
|
@@ -23,7 +23,7 @@ const AGENTS_MD = `# AGENTS.md
|
|
|
23
23
|
- **Только файлы из задачи.** Заодно ничего не чиним. <!-- aqk: человек -->
|
|
24
24
|
- **Готово = доказано.** Назови арбитра: тест, живой прогон, сверка с источником. «Выглядит <!-- aqk: человек -->
|
|
25
25
|
рабочим» — не готово.
|
|
26
|
-
- **Ошибку не глотать.** Либо обработана и залогирована, либо проброшена. <!-- aqk:
|
|
26
|
+
- **Ошибку не глотать.** Либо обработана и залогирована, либо проброшена. <!-- aqk: человек -->
|
|
27
27
|
- **Развилка — вопрос человеку.** Отступление от принятого решения не оформляется комментарием <!-- aqk: человек -->
|
|
28
28
|
в коде.
|
|
29
29
|
- **Отчёт о работе с комплектом — командой, а не словами.** Закончил — выполни \`aqk report\`. <!-- aqk: человек -->
|
package/tool/lib/core.mjs
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// пределе в 500. Разделена по назначению, а не пополам — так требует наше же правило про
|
|
5
5
|
// размер файла. Зависимостей по-прежнему нет ни одной: только встроенные модули Node.
|
|
6
6
|
|
|
7
|
+
import { LANG } from "../i18n/index.mjs";
|
|
7
8
|
import { access, readdir, mkdir, copyFile, writeFile } from "node:fs/promises";
|
|
8
9
|
import { constants } from "node:fs";
|
|
9
10
|
import { fileURLToPath } from "node:url";
|
|
@@ -17,7 +18,12 @@ const PKG_ROOT = resolve(HERE, "..", "..");
|
|
|
17
18
|
const CWD = process.cwd();
|
|
18
19
|
|
|
19
20
|
const DOCS_SRC = join(PKG_ROOT, "kit", "docs");
|
|
20
|
-
|
|
21
|
+
// Правила переносятся в проект НА ЯЗЫКЕ ВЫВОДА. Русский текст в англоязычном проекте — не
|
|
22
|
+
// мелочь: это первое, что там откроет человек, и первое, чего он не прочитает. Русская версия
|
|
23
|
+
// остаётся источником истины, английская — переводом; расходиться им нельзя, и совпадение
|
|
24
|
+
// НАБОРА ФАЙЛОВ сторожит модульная проверка. Совпадение содержания машина не сторожит — это
|
|
25
|
+
// названо в AGENTS.md, а не спрятано.
|
|
26
|
+
const RULES_SRC = join(PKG_ROOT, "kit", LANG === "en" ? "rules-en" : "rules");
|
|
21
27
|
const TARGET_DIR = ".aqk";
|
|
22
28
|
|
|
23
29
|
const c = {
|
package/tool/lib/manifest.mjs
CHANGED
|
@@ -79,7 +79,7 @@ function parseManifest(text) {
|
|
|
79
79
|
// Список обязан совпадать с тем, что программа РЕАЛЬНО читает (`man?.<поле>` в tool/):
|
|
80
80
|
// лишнее имя здесь молча узаконивает поле, которое ни на что не влияет, — та же тишина,
|
|
81
81
|
// только с другой стороны. Сверено обходом: aqk, entry, rules, gates, samples, ratchets, lessons.
|
|
82
|
-
const KNOWN_KEYS = ["aqk", "entry", "rules", "gates", "samples", "ratchets", "lessons"];
|
|
82
|
+
const KNOWN_KEYS = ["aqk", "entry", "rules", "gates", "samples", "ratchets", "lessons", "advisory"];
|
|
83
83
|
|
|
84
84
|
function unknownKeys(man) {
|
|
85
85
|
if (!man || typeof man !== "object" || Array.isArray(man)) return [];
|
|
@@ -119,6 +119,22 @@ function entryLifecycle(rec) {
|
|
|
119
119
|
return { state, why, supersededBy: supersededBy || null, problem };
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
// СОВЕЩАТЕЛЬНЫЕ ГЕЙТЫ. Правило вводят в проект, где старый код ему не соответствует. Храповик
|
|
123
|
+
// отвечает на это одним способом: старое становится долгом, новое блокируется. Второй способ —
|
|
124
|
+
// показывать, не роняя, пока команда договаривается о правиле. Без него у человека остаётся
|
|
125
|
+
// выбор из двух крайностей: включить и сломать сборку либо не включать вовсе.
|
|
126
|
+
//
|
|
127
|
+
// ПОЧЕМУ СПИСКОМ В МАНИФЕСТЕ, А НЕ ФЛАГОМ ПРОГОНА. Флаг «не роняй ничего» — это тот самый
|
|
128
|
+
// `continue-on-error`, против которого написана наша запись ci-actually-fails: он понижает всё
|
|
129
|
+
// разом, не виден в дифе и не назван в сводке. Список виден в манифесте, называется поимённо и
|
|
130
|
+
// печатается КАЖДЫЙ прогон: совещательный гейт, о котором забыли, — это выключенная проверка,
|
|
131
|
+
// и молчать о нём нельзя.
|
|
132
|
+
function advisorySet(man) {
|
|
133
|
+
const v = man?.advisory;
|
|
134
|
+
if (Array.isArray(v)) return new Set(v.map((x) => String(x).trim()).filter(Boolean));
|
|
135
|
+
return new Set();
|
|
136
|
+
}
|
|
137
|
+
|
|
122
138
|
async function readManifest() {
|
|
123
139
|
const p = join(CWD, MANIFEST);
|
|
124
140
|
if (!(await exists(p))) return null;
|
|
@@ -130,7 +146,11 @@ async function readManifest() {
|
|
|
130
146
|
}
|
|
131
147
|
|
|
132
148
|
// Каждая ступень: что требуется, как проверяется, и что это даёт человеку.
|
|
133
|
-
|
|
149
|
+
// `proof` — результат `proveGates`: { ok } либо null, если доказательства не было. Ступень
|
|
150
|
+
// AQK-2 требует его прямо: до 2026-09-07 она проверяла, что папки образцов и храповиков
|
|
151
|
+
// СУЩЕСТВУЮТ, и проект с тремя гейтами `true` проходил порог AQK-3 с зелёным значком.
|
|
152
|
+
// Проверено прогоном на пустой папке. Наличие папки — не защита; уровень обязан означать факт.
|
|
153
|
+
async function assessLevel(man, proof = null) {
|
|
134
154
|
const has = async (rel) => Boolean(rel) && (await exists(join(CWD, String(rel))));
|
|
135
155
|
const isUrl = (v) => typeof v === "string" && /^https?:\/\//.test(v);
|
|
136
156
|
|
|
@@ -146,10 +166,21 @@ async function assessLevel(man) {
|
|
|
146
166
|
const conditions = [
|
|
147
167
|
Boolean(man?.aqk) && entriesExist,
|
|
148
168
|
(await has(man?.rules)) && filledGates.length > 0,
|
|
149
|
-
(await has(man?.samples)) && (await has(man?.ratchets)),
|
|
169
|
+
(await has(man?.samples)) && (await has(man?.ratchets)) && proof?.ok === true,
|
|
150
170
|
isUrl(man?.lessons) || (await has(man?.lessons)),
|
|
151
171
|
];
|
|
152
|
-
|
|
172
|
+
// Ступени, которым нужно доказательство, помечаются отдельно: «не выполнено» и «не проверяли»
|
|
173
|
+
// — разные состояния, и печатать их одинаково значит врать ровно тем способом, против
|
|
174
|
+
// которого весь комплект.
|
|
175
|
+
const NEEDS_PROOF = 2;
|
|
176
|
+
const steps = conditions.map((ok, level) => ({
|
|
177
|
+
level,
|
|
178
|
+
ok,
|
|
179
|
+
// Ровно вторая: третья ступень проверяет журнал, и посылать за доказательством там
|
|
180
|
+
// значит указать не на ту недостачу. Найдено код-ревью 2026-09-07.
|
|
181
|
+
needsProof: level === NEEDS_PROOF && proof === null,
|
|
182
|
+
...L.levels[level],
|
|
183
|
+
}));
|
|
153
184
|
|
|
154
185
|
let reached = -1;
|
|
155
186
|
for (const s of steps) {
|
|
@@ -184,5 +215,5 @@ function manifestWithGate(text, slug, cmd) {
|
|
|
184
215
|
|
|
185
216
|
export {
|
|
186
217
|
parseManifest, readManifest, assessLevel, manifestWithGate, unknownKeys, KNOWN_KEYS,
|
|
187
|
-
entryLifecycle,
|
|
218
|
+
entryLifecycle, advisorySet,
|
|
188
219
|
};
|