workflow-ai 1.0.68 → 1.1.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 (44) hide show
  1. package/package.json +10 -7
  2. package/src/lib/operations/plans.mjs +85 -0
  3. package/src/lib/operations/skills.mjs +124 -0
  4. package/src/lib/operations/tickets.mjs +332 -0
  5. package/src/scripts/get-next-id.js +39 -165
  6. package/src/scripts/move-ticket.js +68 -225
  7. package/src/scripts/pick-next-task.js +93 -759
  8. package/src/skills/analyze-report/tests/cases/TC-ANALYZE-REPORT-001/current/claude-sonnet/trial-1.md +4 -68
  9. package/src/skills/analyze-report/tests/cases/TC-ANALYZE-REPORT-001/current/claude-sonnet/trial-2.md +53 -58
  10. package/src/skills/analyze-report/tests/cases/TC-ANALYZE-REPORT-001/current/claude-sonnet/trial-3.md +48 -48
  11. package/src/skills/analyze-report/tests/cases/TC-ANALYZE-REPORT-001/current/judge.json +15 -15
  12. package/src/skills/analyze-report/tests/cases/TC-ANALYZE-REPORT-001/current/meta.json +16 -16
  13. package/src/skills/analyze-report/tests/cases/TC-ANALYZE-REPORT-002/current/claude-sonnet/trial-3.md +4 -76
  14. package/src/skills/coach/tests/cases/TC-COACH-001/current/meta.json +93 -93
  15. package/src/skills/coach/tests/cases/TC-COACH-002/current/meta.json +93 -93
  16. package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005/current/meta.json +113 -113
  17. package/src/skills/execute-task/tests/cases/TC-EXECUTE-TASK-001/current/meta.json +87 -87
  18. package/src/skills/execute-task/tests/cases/TC-EXECUTE-TASK-005/current/meta.json +87 -87
  19. package/src/skills/review-result/SKILL.md +1 -0
  20. package/src/skills/review-result/knowledge/baseline-snapshot-validation.md +67 -0
  21. package/src/skills/review-result/knowledge/dod-patterns.md +1 -0
  22. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-001/current/claude-sonnet/trial-1.md +2 -2
  23. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-001/current/claude-sonnet/trial-2.md +2 -2
  24. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-001/current/claude-sonnet/trial-3.md +2 -14
  25. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-001/current/judge.json +18 -18
  26. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-001/current/meta.json +20 -20
  27. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-002/current/claude-sonnet/trial-2.md +2 -34
  28. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-002/current/judge.json +19 -19
  29. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-002/current/meta.json +21 -21
  30. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/claude-sonnet/trial-1.md +36 -3
  31. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/claude-sonnet/trial-2.md +11 -3
  32. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/claude-sonnet/trial-3.md +3 -3
  33. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/judge.json +18 -18
  34. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/meta.json +20 -20
  35. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-004/current/claude-sonnet/trial-1.md +5 -0
  36. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-004/current/claude-sonnet/trial-2.md +5 -0
  37. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-004/current/claude-sonnet/trial-3.md +6 -0
  38. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-004/current/judge.json +46 -0
  39. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-004/current/meta.json +37 -0
  40. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-004-baseline-snapshot.yaml +50 -0
  41. package/src/skills/review-result/tests/fixtures/QA-905-baseline-regex-instead-of-snapshot/QA-905.md +62 -0
  42. package/src/skills/review-result/tests/fixtures/QA-905-baseline-regex-instead-of-snapshot/baseline.test.mjs +124 -0
  43. package/src/skills/review-result/tests/index.yaml +5 -0
  44. package/src/skills/review-result/tests/rubrics/baseline-snapshot.md +20 -0
@@ -1,114 +1,114 @@
1
- {
2
- "date": "2026-04-23T08:08:11.073Z",
3
- "skill_sha": "3f91270",
4
- "status": "passed",
5
- "duration_ms": 10,
6
- "per_model": {
7
- "claude-sonnet": {
8
- "passed": true,
9
- "errored": false,
10
- "pass_count": 3,
11
- "error_count": 0,
12
- "total": 3,
13
- "threshold": 2
14
- },
15
- "kilo-glm": {
16
- "passed": true,
17
- "errored": false,
18
- "pass_count": 3,
19
- "error_count": 0,
20
- "total": 3,
21
- "threshold": 2
22
- },
23
- "kilo-minimax": {
24
- "passed": true,
25
- "errored": false,
26
- "pass_count": 2,
27
- "error_count": 0,
28
- "total": 3,
29
- "threshold": 2
30
- },
31
- "kilo-deepseek": {
32
- "passed": true,
33
- "errored": false,
34
- "pass_count": 2,
35
- "error_count": 0,
36
- "total": 3,
37
- "threshold": 2
38
- }
39
- },
40
- "rubric_scores": [
41
- {
42
- "agentId": "claude-sonnet",
43
- "trial": 1,
44
- "score": 5,
45
- "errored": false
46
- },
47
- {
48
- "agentId": "claude-sonnet",
49
- "trial": 2,
50
- "score": 5,
51
- "errored": false
52
- },
53
- {
54
- "agentId": "claude-sonnet",
55
- "trial": 3,
56
- "score": 5,
57
- "errored": false
58
- },
59
- {
60
- "agentId": "kilo-deepseek",
61
- "trial": 1,
62
- "score": 1,
63
- "errored": false
64
- },
65
- {
66
- "agentId": "kilo-deepseek",
67
- "trial": 2,
68
- "score": 5,
69
- "errored": false
70
- },
71
- {
72
- "agentId": "kilo-deepseek",
73
- "trial": 3,
74
- "score": 5,
75
- "errored": false
76
- },
77
- {
78
- "agentId": "kilo-glm",
79
- "trial": 1,
80
- "score": 5,
81
- "errored": false
82
- },
83
- {
84
- "agentId": "kilo-glm",
85
- "trial": 2,
86
- "score": 5,
87
- "errored": false
88
- },
89
- {
90
- "agentId": "kilo-glm",
91
- "trial": 3,
92
- "score": 5,
93
- "errored": false
94
- },
95
- {
96
- "agentId": "kilo-minimax",
97
- "trial": 1,
98
- "score": 5,
99
- "errored": false
100
- },
101
- {
102
- "agentId": "kilo-minimax",
103
- "trial": 2,
104
- "score": 5,
105
- "errored": false
106
- },
107
- {
108
- "agentId": "kilo-minimax",
109
- "trial": 3,
110
- "score": 1,
111
- "errored": false
112
- }
113
- ]
1
+ {
2
+ "date": "2026-04-25T11:54:44.062Z",
3
+ "skill_sha": "3f91270",
4
+ "status": "passed",
5
+ "duration_ms": 4,
6
+ "per_model": {
7
+ "claude-sonnet": {
8
+ "passed": true,
9
+ "errored": false,
10
+ "pass_count": 3,
11
+ "error_count": 0,
12
+ "total": 3,
13
+ "threshold": 2
14
+ },
15
+ "kilo-glm": {
16
+ "passed": true,
17
+ "errored": false,
18
+ "pass_count": 3,
19
+ "error_count": 0,
20
+ "total": 3,
21
+ "threshold": 2
22
+ },
23
+ "kilo-minimax": {
24
+ "passed": true,
25
+ "errored": false,
26
+ "pass_count": 2,
27
+ "error_count": 0,
28
+ "total": 3,
29
+ "threshold": 2
30
+ },
31
+ "kilo-deepseek": {
32
+ "passed": true,
33
+ "errored": false,
34
+ "pass_count": 2,
35
+ "error_count": 0,
36
+ "total": 3,
37
+ "threshold": 2
38
+ }
39
+ },
40
+ "rubric_scores": [
41
+ {
42
+ "agentId": "claude-sonnet",
43
+ "trial": 1,
44
+ "score": 5,
45
+ "errored": false
46
+ },
47
+ {
48
+ "agentId": "claude-sonnet",
49
+ "trial": 2,
50
+ "score": 5,
51
+ "errored": false
52
+ },
53
+ {
54
+ "agentId": "claude-sonnet",
55
+ "trial": 3,
56
+ "score": 5,
57
+ "errored": false
58
+ },
59
+ {
60
+ "agentId": "kilo-deepseek",
61
+ "trial": 1,
62
+ "score": 1,
63
+ "errored": false
64
+ },
65
+ {
66
+ "agentId": "kilo-deepseek",
67
+ "trial": 2,
68
+ "score": 5,
69
+ "errored": false
70
+ },
71
+ {
72
+ "agentId": "kilo-deepseek",
73
+ "trial": 3,
74
+ "score": 5,
75
+ "errored": false
76
+ },
77
+ {
78
+ "agentId": "kilo-glm",
79
+ "trial": 1,
80
+ "score": 5,
81
+ "errored": false
82
+ },
83
+ {
84
+ "agentId": "kilo-glm",
85
+ "trial": 2,
86
+ "score": 5,
87
+ "errored": false
88
+ },
89
+ {
90
+ "agentId": "kilo-glm",
91
+ "trial": 3,
92
+ "score": 5,
93
+ "errored": false
94
+ },
95
+ {
96
+ "agentId": "kilo-minimax",
97
+ "trial": 1,
98
+ "score": 5,
99
+ "errored": false
100
+ },
101
+ {
102
+ "agentId": "kilo-minimax",
103
+ "trial": 2,
104
+ "score": 5,
105
+ "errored": false
106
+ },
107
+ {
108
+ "agentId": "kilo-minimax",
109
+ "trial": 3,
110
+ "score": 1,
111
+ "errored": false
112
+ }
113
+ ]
114
114
  }
@@ -1,88 +1,88 @@
1
- {
2
- "date": "2026-04-23T08:08:11.087Z",
3
- "skill_sha": "1503ea1",
4
- "status": "passed",
5
- "duration_ms": 2,
6
- "per_model": {
7
- "claude-haiku": {
8
- "passed": true,
9
- "errored": false,
10
- "pass_count": 3,
11
- "error_count": 0,
12
- "total": 3,
13
- "threshold": 2
14
- },
15
- "kilo-free": {
16
- "passed": true,
17
- "errored": false,
18
- "pass_count": 3,
19
- "error_count": 0,
20
- "total": 3,
21
- "threshold": 2
22
- },
23
- "kilo-glm-air": {
24
- "passed": true,
25
- "errored": false,
26
- "pass_count": 3,
27
- "error_count": 0,
28
- "total": 3,
29
- "threshold": 2
30
- }
31
- },
32
- "rubric_scores": [
33
- {
34
- "agentId": "claude-haiku",
35
- "trial": 1,
36
- "score": 4,
37
- "errored": false
38
- },
39
- {
40
- "agentId": "claude-haiku",
41
- "trial": 2,
42
- "score": 4,
43
- "errored": false
44
- },
45
- {
46
- "agentId": "claude-haiku",
47
- "trial": 3,
48
- "score": 4,
49
- "errored": false
50
- },
51
- {
52
- "agentId": "kilo-free",
53
- "trial": 1,
54
- "score": 4,
55
- "errored": false
56
- },
57
- {
58
- "agentId": "kilo-free",
59
- "trial": 2,
60
- "score": 4,
61
- "errored": false
62
- },
63
- {
64
- "agentId": "kilo-free",
65
- "trial": 3,
66
- "score": 4,
67
- "errored": false
68
- },
69
- {
70
- "agentId": "kilo-glm-air",
71
- "trial": 1,
72
- "score": 4,
73
- "errored": false
74
- },
75
- {
76
- "agentId": "kilo-glm-air",
77
- "trial": 2,
78
- "score": 4,
79
- "errored": false
80
- },
81
- {
82
- "agentId": "kilo-glm-air",
83
- "trial": 3,
84
- "score": 4,
85
- "errored": false
86
- }
87
- ]
1
+ {
2
+ "date": "2026-04-25T11:54:44.070Z",
3
+ "skill_sha": "1503ea1",
4
+ "status": "passed",
5
+ "duration_ms": 1,
6
+ "per_model": {
7
+ "claude-haiku": {
8
+ "passed": true,
9
+ "errored": false,
10
+ "pass_count": 3,
11
+ "error_count": 0,
12
+ "total": 3,
13
+ "threshold": 2
14
+ },
15
+ "kilo-free": {
16
+ "passed": true,
17
+ "errored": false,
18
+ "pass_count": 3,
19
+ "error_count": 0,
20
+ "total": 3,
21
+ "threshold": 2
22
+ },
23
+ "kilo-glm-air": {
24
+ "passed": true,
25
+ "errored": false,
26
+ "pass_count": 3,
27
+ "error_count": 0,
28
+ "total": 3,
29
+ "threshold": 2
30
+ }
31
+ },
32
+ "rubric_scores": [
33
+ {
34
+ "agentId": "claude-haiku",
35
+ "trial": 1,
36
+ "score": 4,
37
+ "errored": false
38
+ },
39
+ {
40
+ "agentId": "claude-haiku",
41
+ "trial": 2,
42
+ "score": 4,
43
+ "errored": false
44
+ },
45
+ {
46
+ "agentId": "claude-haiku",
47
+ "trial": 3,
48
+ "score": 4,
49
+ "errored": false
50
+ },
51
+ {
52
+ "agentId": "kilo-free",
53
+ "trial": 1,
54
+ "score": 4,
55
+ "errored": false
56
+ },
57
+ {
58
+ "agentId": "kilo-free",
59
+ "trial": 2,
60
+ "score": 4,
61
+ "errored": false
62
+ },
63
+ {
64
+ "agentId": "kilo-free",
65
+ "trial": 3,
66
+ "score": 4,
67
+ "errored": false
68
+ },
69
+ {
70
+ "agentId": "kilo-glm-air",
71
+ "trial": 1,
72
+ "score": 4,
73
+ "errored": false
74
+ },
75
+ {
76
+ "agentId": "kilo-glm-air",
77
+ "trial": 2,
78
+ "score": 4,
79
+ "errored": false
80
+ },
81
+ {
82
+ "agentId": "kilo-glm-air",
83
+ "trial": 3,
84
+ "score": 4,
85
+ "errored": false
86
+ }
87
+ ]
88
88
  }
@@ -1,88 +1,88 @@
1
- {
2
- "date": "2026-04-23T08:08:11.091Z",
3
- "skill_sha": "1503ea1",
4
- "status": "passed",
5
- "duration_ms": 1,
6
- "per_model": {
7
- "claude-haiku": {
8
- "passed": true,
9
- "errored": false,
10
- "pass_count": 3,
11
- "error_count": 0,
12
- "total": 3,
13
- "threshold": 2
14
- },
15
- "kilo-free": {
16
- "passed": true,
17
- "errored": false,
18
- "pass_count": 3,
19
- "error_count": 0,
20
- "total": 3,
21
- "threshold": 2
22
- },
23
- "kilo-glm-air": {
24
- "passed": true,
25
- "errored": false,
26
- "pass_count": 3,
27
- "error_count": 0,
28
- "total": 3,
29
- "threshold": 2
30
- }
31
- },
32
- "rubric_scores": [
33
- {
34
- "agentId": "claude-haiku",
35
- "trial": 1,
36
- "score": 5,
37
- "errored": false
38
- },
39
- {
40
- "agentId": "claude-haiku",
41
- "trial": 2,
42
- "score": 5,
43
- "errored": false
44
- },
45
- {
46
- "agentId": "claude-haiku",
47
- "trial": 3,
48
- "score": 5,
49
- "errored": false
50
- },
51
- {
52
- "agentId": "kilo-free",
53
- "trial": 1,
54
- "score": 5,
55
- "errored": false
56
- },
57
- {
58
- "agentId": "kilo-free",
59
- "trial": 2,
60
- "score": 5,
61
- "errored": false
62
- },
63
- {
64
- "agentId": "kilo-free",
65
- "trial": 3,
66
- "score": 5,
67
- "errored": false
68
- },
69
- {
70
- "agentId": "kilo-glm-air",
71
- "trial": 1,
72
- "score": 5,
73
- "errored": false
74
- },
75
- {
76
- "agentId": "kilo-glm-air",
77
- "trial": 2,
78
- "score": 5,
79
- "errored": false
80
- },
81
- {
82
- "agentId": "kilo-glm-air",
83
- "trial": 3,
84
- "score": 5,
85
- "errored": false
86
- }
87
- ]
1
+ {
2
+ "date": "2026-04-25T11:54:44.073Z",
3
+ "skill_sha": "1503ea1",
4
+ "status": "passed",
5
+ "duration_ms": 1,
6
+ "per_model": {
7
+ "claude-haiku": {
8
+ "passed": true,
9
+ "errored": false,
10
+ "pass_count": 3,
11
+ "error_count": 0,
12
+ "total": 3,
13
+ "threshold": 2
14
+ },
15
+ "kilo-free": {
16
+ "passed": true,
17
+ "errored": false,
18
+ "pass_count": 3,
19
+ "error_count": 0,
20
+ "total": 3,
21
+ "threshold": 2
22
+ },
23
+ "kilo-glm-air": {
24
+ "passed": true,
25
+ "errored": false,
26
+ "pass_count": 3,
27
+ "error_count": 0,
28
+ "total": 3,
29
+ "threshold": 2
30
+ }
31
+ },
32
+ "rubric_scores": [
33
+ {
34
+ "agentId": "claude-haiku",
35
+ "trial": 1,
36
+ "score": 5,
37
+ "errored": false
38
+ },
39
+ {
40
+ "agentId": "claude-haiku",
41
+ "trial": 2,
42
+ "score": 5,
43
+ "errored": false
44
+ },
45
+ {
46
+ "agentId": "claude-haiku",
47
+ "trial": 3,
48
+ "score": 5,
49
+ "errored": false
50
+ },
51
+ {
52
+ "agentId": "kilo-free",
53
+ "trial": 1,
54
+ "score": 5,
55
+ "errored": false
56
+ },
57
+ {
58
+ "agentId": "kilo-free",
59
+ "trial": 2,
60
+ "score": 5,
61
+ "errored": false
62
+ },
63
+ {
64
+ "agentId": "kilo-free",
65
+ "trial": 3,
66
+ "score": 5,
67
+ "errored": false
68
+ },
69
+ {
70
+ "agentId": "kilo-glm-air",
71
+ "trial": 1,
72
+ "score": 5,
73
+ "errored": false
74
+ },
75
+ {
76
+ "agentId": "kilo-glm-air",
77
+ "trial": 2,
78
+ "score": 5,
79
+ "errored": false
80
+ },
81
+ {
82
+ "agentId": "kilo-glm-air",
83
+ "trial": 3,
84
+ "score": 5,
85
+ "errored": false
86
+ }
87
+ ]
88
88
  }
@@ -36,6 +36,7 @@ description: >
36
36
  |--------|----------------|
37
37
  | `knowledge/dod-patterns.md` | При определении типа проверки для конкретного пункта DoD |
38
38
  | `knowledge/test-hygiene.md` | Когда DoD требует создать/изменить автотест — красные и зелёные флаги изоляции, что считать нарушением и как оформить issue |
39
+ | `knowledge/baseline-snapshot-validation.md` | **⛔ Обязательно**, когда DoD/план тикета содержит формулировки уровня эталона: baseline, snapshot, inline-snapshot, побайтное/точное совпадение, регрессионный эталон, «output не должен меняться». Проверяет способ реализации теста (точное сравнение с литералом-эталоном vs регекс/includes), а не только зелёный прогон |
39
40
  | `../shared/*` | **ВСЕГДА** перед началом работы — общие знания проекта |
40
41
 
41
42
  ## Загрузка шаблонов
@@ -0,0 +1,67 @@
1
+ # Проверка baseline/snapshot-тестов на ревью
2
+
3
+ Когда DoD тикета требует **baseline-теста**, **snapshot-сравнения**, **регрессионного эталона** или **побайтного/посимвольного совпадения** результата с зафиксированной версией, ревьюер обязан проверить **способ реализации теста**, а не только факт его существования и зелёный статус прогона.
4
+
5
+ ## Зачем ревьюеру проверять это отдельно
6
+
7
+ «Тесты зелёные» и «файл создан» — необходимые, но не достаточные признаки. Тест может быть зелёным и одновременно не выполнять заявленной функции baseline:
8
+
9
+ - ассерт по совпадению с регулярным выражением ловит только то, что регекс уже описывает; всё, что вне регекса, тест не отслеживает;
10
+ - ассерт «значение truthy» или равенство одного поля проверяют точечный признак, а не целостный эталон;
11
+ - проверка наличия подстроки в выводе — не сравнение с эталоном.
12
+
13
+ Такие тесты **тавтологичны**: они зафиксированы лишь в той части, где автор уже знал ответ. Регрессия в любой другой части (новые поля, изменённые ключи, изменённый формат, потерянные строки stdout) проходит мимо. Если назначение теста — поймать **любую** регрессию по сравнению с эталонной версией, ассерт по фрагментам не реализует это назначение.
14
+
15
+ ## Триггеры применения
16
+
17
+ Шаг применяется, если в DoD, секции «Детали задачи» или «Критериях успеха» родительского плана встречаются формулировки уровня семантического намерения:
18
+
19
+ - baseline / эталон / reference / зафиксированный output;
20
+ - snapshot / inline-snapshot / external-snapshot;
21
+ - побайтное / посимвольное / точное совпадение;
22
+ - регрессия (ловить регрессию, регрессионный тест на изменение формата);
23
+ - «output не должен меняться», «формат стабилизирован», «контракт фиксируется».
24
+
25
+ Если хотя бы одна формулировка присутствует — это не обычный «тест проходит», а заявление о наличии **эталона**, и проверка способа реализации обязательна.
26
+
27
+ ## Как проверять
28
+
29
+ 1. **Открой файл теста физически** (Read), не доверяй секции Result. Найди ассерты, относящиеся к baseline-проверке.
30
+ 2. **Сверь способ ассерта со способом, заявленным в DoD/плане:**
31
+
32
+ | Заявлено в DoD/плане | Что обязано быть в тесте | Что недопустимо |
33
+ |---|---|---|
34
+ | inline-snapshot / точное совпадение / эталон | Точное равенство (deep-equality для структур, побайтное равенство для строк) нормализованного output с зафиксированным литералом-эталоном. Эталон лежит **в файле теста как литерал** (или как сериализованный snapshot, на который тест ссылается). | Ассерт по регулярному выражению, проверка наличия подстроки, ассерт по одному полю без сверки остального вывода. |
35
+ | regression / not-changed / стабилизирован | Точное сравнение всего нормализованного output с эталоном. Любое отклонение должно ронять тест. | Набор фрагментарных ассертов (несколько регекс-проверок подряд) — не отвергает структурные изменения вне зон проверки. |
36
+ | побайтное совпадение | Бинарное/посимвольное равенство (буфер/строка целиком) после задокументированной нормализации. Нормализация описана в коде теста. | Любая «приблизительная» проверка. |
37
+
38
+ 3. **Проверь, что эталон существует физически.** Inline-snapshot должен быть литералом в коде; external-snapshot — отдельным файлом, на который тест ссылается. Если эталона нет — тест не baseline.
39
+
40
+ 4. **Проверь полноту нормализации.** Заявленные в DoD динамические поля (timestamps, абсолютные пути, рандомные id) должны нормализоваться **до** сравнения, а не игнорироваться через регекс. Если нормализация не реализована или неполна — тест либо хрупкий, либо тавтологичный.
41
+
42
+ ## Красные флаги при чтении теста
43
+
44
+ - Регекс-ассерт в роли единственной проверки baseline-заявленного output.
45
+ - Несколько фрагментарных ассертов (regex-match, substring-include) подряд по разным частям одного output без сравнения целого.
46
+ - Отсутствие литерала-эталона в файле теста (нет inline-string, нет ссылки на snapshot-файл).
47
+ - Нормализация-функция объявлена, но её результат используется только для фрагментарных ассертов, не для равенства целого.
48
+ - DoD пункт «эталон зафиксирован» отмечен `[x]`, но в коде нет ни одного точного сравнения.
49
+ - Утверждения в Result вида «N snapshot-тестов зелёные», когда в файле теста нет ни одного snapshot/equality-вызова.
50
+
51
+ ## Зелёные сигналы
52
+
53
+ - Каждый baseline-сценарий завершается одним точным сравнением целого нормализованного output с литералом-эталоном.
54
+ - Эталон прочитываем глазами: видно структуру и порядок полей; ревьюер может сказать, какое именно изменение уронит тест.
55
+ - Нормализация описана и применяется ко всему output до сравнения.
56
+ - При попытке вручную «сломать» один символ в эталоне или в исходнике — тест падает с понятной diff-разницей.
57
+
58
+ ## Что делать при обнаружении нарушения
59
+
60
+ - `failed` с конкретным issue: какой пункт DoD заявил эталон, какой ассерт фактически реализован, почему ассерт не отвергает регрессии вне его покрытия.
61
+ - Процитировать дословно: строку DoD с формулировкой эталона **и** строку теста с фактическим ассертом. Не пересказывать.
62
+ - Не смягчать finding на основании того, что «тесты зелёные» или «количество кейсов соответствует» — проверка способа реализации ортогональна прогону и счётчику.
63
+
64
+ ## Когда проверка не применяется
65
+
66
+ - DoD не содержит формулировок уровня эталона/snapshot/baseline/побайтного совпадения. Тест нацелен на проверку отдельной инвариантной фичи (наличие поля, формат id) — обычные ассерты допустимы.
67
+ - В тикете явно зафиксировано, что эталон будет добавлен следующим тикетом, и текущая задача — только smoke-каркас. Такая декомпозиция должна быть явной в плане; молчаливое сведение baseline к smoke — нарушение.