workflow-ai 1.0.64 → 1.0.65
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 +239 -145
- package/configs/agent-health-rules.yaml +64 -0
- package/configs/pipeline.yaml +18 -1
- package/package.json +1 -1
- package/src/init.mjs +20 -3
- package/src/lib/agent-health-registry.mjs +245 -0
- package/src/lib/artifact-snapshot.mjs +233 -0
- package/src/lib/error-classifier.mjs +274 -0
- package/src/lib/test-error-classifier.mjs +60 -0
- package/src/lib/test-extends.mjs +58 -0
- package/src/lib/test-version.mjs +21 -0
- package/src/scripts/move-to-review.js +5 -7
- package/src/scripts/reset-agent-health.js +62 -0
- package/src/skills/coach/SKILL.md +1 -0
- package/src/skills/coach/tests/cases/TC-COACH-001/current/meta.json +2 -3
- package/src/skills/coach/tests/cases/TC-COACH-002/current/meta.json +2 -3
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-001/current/claude-sonnet/trial-1.md +23 -31
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-001/current/claude-sonnet/trial-2.md +20 -35
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-001/current/claude-sonnet/trial-3.md +36 -19
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-001/current/judge.json +1 -1
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-001/current/kilo-deepseek/trial-2.md +11 -5
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-001/current/kilo-deepseek/trial-3.md +12 -16
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-001/current/kilo-glm/trial-1.md +15 -9
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-001/current/kilo-glm/trial-3.md +15 -14
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-001/current/kilo-minimax/trial-1.md +22 -18
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-001/current/kilo-minimax/trial-2.md +24 -16
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-001/current/kilo-minimax/trial-3.md +13 -20
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-001/current/meta.json +2 -2
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-002/current/claude-sonnet/trial-1.md +14 -19
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-002/current/claude-sonnet/trial-2.md +24 -14
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-002/current/claude-sonnet/trial-3.md +20 -19
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-002/current/judge.json +16 -17
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-002/current/kilo-deepseek/trial-1.md +0 -7
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-002/current/kilo-deepseek/trial-2.md +9 -10
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-002/current/kilo-deepseek/trial-3.md +5 -5
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-002/current/kilo-glm/trial-1.md +20 -4
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-002/current/kilo-glm/trial-2.md +36 -9
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-002/current/kilo-glm/trial-3.md +9 -6
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-002/current/kilo-minimax/trial-1.md +4 -12
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-002/current/kilo-minimax/trial-2.md +6 -8
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-002/current/kilo-minimax/trial-3.md +8 -4
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-002/current/meta.json +10 -11
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-003/current/claude-sonnet/trial-1.md +30 -0
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-003/current/claude-sonnet/trial-2.md +30 -0
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-003/current/claude-sonnet/trial-3.md +30 -0
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-003/current/judge.json +165 -0
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-003/current/kilo-deepseek/trial-1.md +5 -0
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-003/current/kilo-deepseek/trial-2.md +26 -0
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-003/current/kilo-deepseek/trial-3.md +5 -0
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-003/current/kilo-glm/trial-1.md +39 -0
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-003/current/kilo-glm/trial-2.md +37 -0
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-003/current/kilo-glm/trial-3.md +45 -0
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-003/current/kilo-minimax/trial-1.md +26 -0
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-003/current/kilo-minimax/trial-2.md +27 -0
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-003/current/kilo-minimax/trial-3.md +7 -0
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-003/current/meta.json +117 -0
- package/src/skills/decompose-gaps/tests/cases/TC-DECOMPOSE-GAPS-003-parent-plan-mandatory.yaml +41 -0
- package/src/skills/decompose-gaps/tests/index.yaml +5 -0
- package/src/skills/decompose-gaps/tests/rubrics/parent-plan-mandatory.md +22 -0
- package/src/skills/decompose-gaps/workflows/decompose.md +5 -2
- package/src/skills/decompose-plan/knowledge/atomicity-checklist.md +31 -5
- package/src/skills/decompose-plan/knowledge/capabilities.md +29 -5
- package/src/skills/decompose-plan/knowledge/human-task-rules.md +15 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-004/current/claude-sonnet/trial-1.md +55 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-004/current/claude-sonnet/trial-2.md +49 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-004/current/claude-sonnet/trial-3.md +49 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-004/current/judge.json +163 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-004/current/kilo-deepseek/trial-1.md +104 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-004/current/kilo-deepseek/trial-2.md +45 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-004/current/kilo-deepseek/trial-3.md +58 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-004/current/kilo-glm/trial-1.md +193 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-004/current/kilo-glm/trial-2.md +202 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-004/current/kilo-glm/trial-3.md +155 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-004/current/kilo-minimax/trial-1.md +52 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-004/current/kilo-minimax/trial-2.md +17 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-004/current/kilo-minimax/trial-3.md +0 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-004/current/meta.json +115 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-004-executor-atomicity.yaml +64 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005/current/claude-sonnet/trial-1.md +59 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005/current/claude-sonnet/trial-2.md +204 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005/current/claude-sonnet/trial-3.md +213 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005/current/judge.json +163 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005/current/kilo-deepseek/trial-1.md +0 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005/current/kilo-deepseek/trial-2.md +57 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005/current/kilo-deepseek/trial-3.md +54 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005/current/kilo-glm/trial-1.md +147 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005/current/kilo-glm/trial-2.md +165 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005/current/kilo-glm/trial-3.md +133 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005/current/kilo-minimax/trial-1.md +81 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005/current/kilo-minimax/trial-2.md +108 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005/current/kilo-minimax/trial-3.md +3 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005/current/meta.json +114 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005-capabilities-registry.yaml +78 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-006/current/claude-sonnet/trial-1.md +225 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-006/current/claude-sonnet/trial-2.md +66 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-006/current/claude-sonnet/trial-3.md +36 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-006/current/judge.json +163 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-006/current/kilo-deepseek/trial-1.md +42 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-006/current/kilo-deepseek/trial-2.md +67 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-006/current/kilo-deepseek/trial-3.md +40 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-006/current/kilo-glm/trial-1.md +122 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-006/current/kilo-glm/trial-2.md +131 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-006/current/kilo-glm/trial-3.md +138 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-006/current/kilo-minimax/trial-1.md +41 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-006/current/kilo-minimax/trial-2.md +88 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-006/current/kilo-minimax/trial-3.md +0 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-006/current/meta.json +115 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-006-dod-threshold.yaml +72 -0
- package/src/skills/decompose-plan/tests/index.yaml +15 -0
- package/src/skills/decompose-plan/tests/rubrics/capabilities-registry.md +21 -0
- package/src/skills/decompose-plan/tests/rubrics/dod-threshold.md +21 -0
- package/src/skills/decompose-plan/tests/rubrics/executor-atomicity.md +21 -0
- package/src/skills/decompose-plan/workflows/decompose.md +38 -5
- package/src/skills/execute-task/tests/cases/TC-EXECUTE-TASK-001/current/meta.json +3 -4
- package/src/skills/execute-task/tests/cases/TC-EXECUTE-TASK-005/current/meta.json +3 -4
- package/src/skills/manual-testing/SKILL.md +6 -4
- package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-001/current/claude-sonnet/trial-1.md +29 -16
- package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-001/current/claude-sonnet/trial-2.md +21 -54
- package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-001/current/claude-sonnet/trial-3.md +18 -23
- package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-001/current/judge.json +17 -17
- package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-001/current/meta.json +19 -19
- package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-002/current/claude-sonnet/trial-1.md +27 -30
- package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-002/current/claude-sonnet/trial-2.md +16 -23
- package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-002/current/claude-sonnet/trial-3.md +35 -28
- package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-002/current/judge.json +13 -13
- package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-002/current/meta.json +15 -15
- package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-003/current/claude-sonnet/trial-1.md +76 -0
- package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-003/current/claude-sonnet/trial-2.md +71 -0
- package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-003/current/claude-sonnet/trial-3.md +85 -0
- package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-003/current/judge.json +46 -0
- package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-003/current/meta.json +36 -0
- package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-003-qa-non-ui-assertion.yaml +65 -0
- package/src/skills/manual-testing/tests/index.yaml +5 -0
- package/src/skills/manual-testing/tests/rubrics/qa-non-ui-assertion.md +31 -0
- package/src/skills/review-result/scripts/verify-artifacts.js +42 -12
package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-003/current/claude-sonnet/trial-1.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
Стабильный результат. Картина ясна. Подготавливаю финальный отчёт.
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Отчёт о тестовой сессии — QA-NNN
|
|
6
|
+
|
|
7
|
+
### Конфигурация
|
|
8
|
+
|
|
9
|
+
| Параметр | Значение |
|
|
10
|
+
|----------|----------|
|
|
11
|
+
| Тип тикета | Non-UI FS assertion |
|
|
12
|
+
| Тест-файл | `D:/Dev/workflowAi/src/tests/example.test.mjs` (создан) |
|
|
13
|
+
| Статус файла | Новый (git: `??`) |
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
### Результаты тест-кейсов
|
|
18
|
+
|
|
19
|
+
| TC | Описание | Статус | Evidence |
|
|
20
|
+
|----|----------|--------|----------|
|
|
21
|
+
| TC-1 | `initProject` создаёт `.workflow/state/` | **PASS** | `assert: existsSync('.workflow/state/') === true` (340ms, stable) |
|
|
22
|
+
| TC-2 | `initProject` создаёт `agent-health-rules.yaml` с непустым содержимым | **FAIL** | `AssertionError: agent-health-rules.yaml should exist and be non-empty` → файл не создаётся, т.к. `configs/agent-health-rules.yaml` отсутствует в пакете |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
### DEFECT — TC-2
|
|
27
|
+
|
|
28
|
+
**Severity:** HIGH
|
|
29
|
+
**Шаги воспроизведения:**
|
|
30
|
+
1. Запустить `node --test src/tests/example.test.mjs`
|
|
31
|
+
2. Наблюдать: TC-2 падает
|
|
32
|
+
|
|
33
|
+
**Корневая причина:** `src/init.mjs:439` — копирование выполняется только при `existsSync(agentHealthRulesSrc)`:
|
|
34
|
+
```js
|
|
35
|
+
const agentHealthRulesSrc = join(packageRoot, 'configs', 'agent-health-rules.yaml');
|
|
36
|
+
if (existsSync(agentHealthRulesSrc)) { // ← условие не выполняется
|
|
37
|
+
copyFileSync(agentHealthRulesSrc, agentHealthRulesDest);
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
Файл `configs/agent-health-rules.yaml` **отсутствует** в директории пакета (в `configs/` есть только `config.yaml`, `pipeline.yaml`, `ticket-movement-rules.yaml`).
|
|
41
|
+
|
|
42
|
+
**Pre-existing:** тот же дефект уже отражён в `init.test.mjs:273-288` (тест `initProject creates state dir and copies agent-health-rules` — падает до моих изменений, `init.test.mjs` в git status: `M`).
|
|
43
|
+
|
|
44
|
+
**Evidence из первого запуска:** первый прогон случайно прошёл — вероятно, унаследованный `WORKFLOW_HOME` от параллельного теста содержал файл из глобальной директории. Повторные standalone-запуски показывают стабильный FAIL.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
### Статус DoD
|
|
49
|
+
|
|
50
|
+
- [x] Ассерт (1) присутствует в тест-файле — **выполнен** (`example.test.mjs:9-16`)
|
|
51
|
+
- [x] Ассерт (1) проходит — **PASS**
|
|
52
|
+
- [x] Ассерт (2) присутствует в тест-файле — **выполнен** (`example.test.mjs:23-33`)
|
|
53
|
+
- [ ] Ассерт (2) проходит — **FAIL** (фактически: `AssertionError`, файл не создаётся реализацией)
|
|
54
|
+
- [ ] Весь suite зелёный — **не выполнено** (фактически: TC-2 FAIL; плюс pre-existing failures в `init.test.mjs` и других файлах, не связанных с данным тикетом)
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
### Итог
|
|
59
|
+
|
|
60
|
+
| | |
|
|
61
|
+
|--|--|
|
|
62
|
+
| Всего TC | 2 |
|
|
63
|
+
| PASS | 1 |
|
|
64
|
+
| FAIL | 1 |
|
|
65
|
+
| BLOCKED | 0 |
|
|
66
|
+
|
|
67
|
+
**Тикет не закрыт.** Ассерты написаны и добавлены в `src/tests/example.test.mjs`. TC-1 зелёный. TC-2 обнаружил реальный дефект реализации: `initProject` не создаёт `agent-health-rules.yaml` из-за отсутствия исходного файла. Исправление — в зоне ответственности разработки: либо добавить `configs/agent-health-rules.yaml` в пакет, либо изменить логику инициализации.
|
|
68
|
+
|
|
69
|
+
---RESULT---
|
|
70
|
+
status: failed
|
|
71
|
+
tc_pass: 1
|
|
72
|
+
tc_fail: 1
|
|
73
|
+
defect: initProject не создаёт agent-health-rules.yaml — отсутствует configs/agent-health-rules.yaml в пакете (src/init.mjs:439)
|
|
74
|
+
pre_existing: true (дублирует падение init.test.mjs:273-288)
|
|
75
|
+
artifact: D:/Dev/workflowAi/src/tests/example.test.mjs
|
|
76
|
+
---RESULT---
|
package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-003/current/claude-sonnet/trial-2.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
Корневая причина установлена. Формирую итоговый отчёт.
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Результат тестирования QA-NNN
|
|
6
|
+
|
|
7
|
+
### Состояние тест-кейсов
|
|
8
|
+
|
|
9
|
+
| # | Тест-кейс | Статус | Evidence |
|
|
10
|
+
|---|-----------|--------|---------|
|
|
11
|
+
| TC-1 | Ассерт (1) присутствует в файле | PASS | `example.test.mjs:8–21` — ассерт `existsSync(join(projectRoot, '.workflow', 'state'))` присутствует |
|
|
12
|
+
| TC-2 | Ассерт (2) присутствует в файле | PASS | `example.test.mjs:23–38` — ассерт `existsSync(rulesPath) && readFileSync(rulesPath, 'utf8').length > 0` присутствует |
|
|
13
|
+
| TC-3 | Ассерт (1) проходит (`node --test`) | PASS | `✔ initProject creates .workflow/state/ directory (586ms)` |
|
|
14
|
+
| TC-4 | Ассерт (2) проходит (`node --test`) | **FAIL** | `✖ initProject creates .workflow/config/agent-health-rules.yaml with non-empty content` — AssertionError |
|
|
15
|
+
| TC-5 | Весь suite зелёный | **FAIL** | `tests 2 / pass 1 / fail 1` — exit code 1 |
|
|
16
|
+
|
|
17
|
+
**Итог: 3 PASS / 2 FAIL**
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
### DEFECT-001 — `agent-health-rules.yaml` не создаётся при `initProject`
|
|
22
|
+
|
|
23
|
+
**Severity:** HIGH
|
|
24
|
+
**Компонент:** `src/init.mjs` → Step 10
|
|
25
|
+
|
|
26
|
+
**Описание:**
|
|
27
|
+
После вызова `initProject` файл `.workflow/config/agent-health-rules.yaml` не существует, что вызывает падение TC-4 и TC-5.
|
|
28
|
+
|
|
29
|
+
**Корневая причина:**
|
|
30
|
+
`init.mjs:437` пытается скопировать исходный файл:
|
|
31
|
+
```
|
|
32
|
+
const agentHealthRulesSrc = join(packageRoot, 'configs', 'agent-health-rules.yaml');
|
|
33
|
+
```
|
|
34
|
+
Файл `D:/Dev/workflowAi/configs/agent-health-rules.yaml` **отсутствует** в репозитории. Содержимое `configs/`:
|
|
35
|
+
```
|
|
36
|
+
config.yaml
|
|
37
|
+
pipeline.yaml
|
|
38
|
+
ticket-movement-rules.yaml
|
|
39
|
+
```
|
|
40
|
+
Строка 439 (`if (existsSync(agentHealthRulesSrc))`) молча пропускает шаг — `agent-health-rules.yaml` не копируется.
|
|
41
|
+
|
|
42
|
+
**Шаги воспроизведения:**
|
|
43
|
+
1. `cd D:/Dev/workflowAi`
|
|
44
|
+
2. `node --test src/tests/example.test.mjs`
|
|
45
|
+
3. Второй тест падает с `AssertionError: agent-health-rules.yaml should exist and be non-empty`
|
|
46
|
+
|
|
47
|
+
**Ожидаемый результат:** Файл `configs/agent-health-rules.yaml` присутствует в репозитории и копируется в `.workflow/config/` при `initProject`.
|
|
48
|
+
**Фактический результат:** Исходный файл отсутствует, шаг копирования пропускается, тест падает.
|
|
49
|
+
|
|
50
|
+
**a11y:** N/A (не UI)
|
|
51
|
+
**Evidence:**
|
|
52
|
+
```
|
|
53
|
+
✖ initProject creates .workflow/config/agent-health-rules.yaml with non-empty content (597ms)
|
|
54
|
+
AssertionError: agent-health-rules.yaml should exist and be non-empty
|
|
55
|
+
actual: false, expected: true
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
### Критерии DoD
|
|
61
|
+
|
|
62
|
+
- [x] Ассерт (1) присутствует в `example.test.mjs:14–17` и проходит — `✔`
|
|
63
|
+
- [ ] Ассерт (2) присутствует в `example.test.mjs:31–34` — присутствует, но **не проходит** (`✖`)
|
|
64
|
+
- [ ] Весь suite зелёный — **не выполнен** (1 fail из 2)
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
**Тикет не завершён** — DoD не выполнен из-за DEFECT-001. Необходимо добавить файл `configs/agent-health-rules.yaml` в репозиторий (исправление — в зоне ответственности разработки).
|
|
69
|
+
|
|
70
|
+
---RESULT---
|
|
71
|
+
FAIL: suite не зелёный. TC-1/TC-2 (наличие ассертов) — PASS. TC-3 (ассерт state dir) — PASS. TC-4/TC-5 — FAIL: исходный файл `D:/Dev/workflowAi/configs/agent-health-rules.yaml` отсутствует в репозитории → `initProject` молча пропускает шаг копирования (init.mjs:439) → ассерт (2) падает с AssertionError.
|
package/src/skills/manual-testing/tests/cases/TC-MANUAL-TESTING-003/current/claude-sonnet/trial-3.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
Картина ясна. Составляю итоговый отчёт.
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Результат тестирования QA-NNN
|
|
6
|
+
|
|
7
|
+
### Резюме
|
|
8
|
+
|
|
9
|
+
| TC | Описание | Статус |
|
|
10
|
+
|----|----------|--------|
|
|
11
|
+
| TC-1 | Ассерт: `.workflow/state/` существует после `initProject` | **PASS** |
|
|
12
|
+
| TC-2 | Ассерт: `agent-health-rules.yaml` существует и непустой | **FAIL** |
|
|
13
|
+
| — | Весь suite зелёный | **FAIL** |
|
|
14
|
+
|
|
15
|
+
**Итого:** 1 passed / 1 failed / 0 blocked
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
### TC-1 — PASS
|
|
20
|
+
|
|
21
|
+
**Шаги:** `initProject(projectRoot, { force: true })` → `assert.ok(existsSync(join(projectRoot, '.workflow', 'state')))`
|
|
22
|
+
|
|
23
|
+
**Evidence:**
|
|
24
|
+
```
|
|
25
|
+
✔ initProject creates .workflow/state/ directory (501.599ms)
|
|
26
|
+
```
|
|
27
|
+
`a11y (node:test)`: тест прошёл без ошибок.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
### TC-2 — FAIL
|
|
32
|
+
|
|
33
|
+
**Шаги:** `initProject(projectRoot, { force: true })` → `assert.ok(existsSync(rulesPath) && readFileSync(rulesPath, 'utf8').length > 0)`
|
|
34
|
+
|
|
35
|
+
**Evidence:**
|
|
36
|
+
```
|
|
37
|
+
✖ initProject creates .workflow/config/agent-health-rules.yaml with non-empty content (448.8962ms)
|
|
38
|
+
AssertionError: agent-health-rules.yaml should exist and be non-empty
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Корневая причина:** `initProject` (строки 437–443 `init.mjs`) копирует файл только если источник `configs/agent-health-rules.yaml` существует:
|
|
42
|
+
```js
|
|
43
|
+
if (existsSync(agentHealthRulesSrc)) {
|
|
44
|
+
copyFileSync(agentHealthRulesSrc, agentHealthRulesDest);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
Файл `configs/agent-health-rules.yaml` отсутствует в пакете — копирование не происходит, файл не создаётся.
|
|
48
|
+
|
|
49
|
+
**Подтверждение:** тот же инвариант в `init.test.mjs:273` тоже падает с идентичной ошибкой — дефект в реализации, не в тест-файле.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
### DEFECT
|
|
54
|
+
|
|
55
|
+
**Severity:** HIGH
|
|
56
|
+
**Компонент:** `src/init.mjs`, `configs/`
|
|
57
|
+
|
|
58
|
+
**Описание:** `initProject` не создаёт `.workflow/config/agent-health-rules.yaml` потому что исходный файл `configs/agent-health-rules.yaml` отсутствует в пакете.
|
|
59
|
+
|
|
60
|
+
**Шаги воспроизведения:**
|
|
61
|
+
1. `initProject('/tmp/some-project', { force: true })`
|
|
62
|
+
2. Проверить `existsSync('/tmp/some-project/.workflow/config/agent-health-rules.yaml')`
|
|
63
|
+
3. Результат: `false`
|
|
64
|
+
|
|
65
|
+
**Ожидаемо:** файл создан и непустой
|
|
66
|
+
**Фактически:** файл отсутствует (источник `configs/agent-health-rules.yaml` не найден)
|
|
67
|
+
|
|
68
|
+
**Evidence:** `AssertionError: agent-health-rules.yaml should exist and be non-empty` в `src/tests/example.test.mjs:31` и `src/tests/init.test.mjs:283`.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
### DoD
|
|
73
|
+
|
|
74
|
+
- [x] Ассерт (1) присутствует в тест-файле — `src/tests/example.test.mjs:14`
|
|
75
|
+
- [x] Ассерт (1) проходит — PASS
|
|
76
|
+
- [x] Ассерт (2) присутствует в тест-файле — `src/tests/example.test.mjs:31`
|
|
77
|
+
- [ ] Ассерт (2) проходит — FAIL (дефект реализации: источник файла отсутствует)
|
|
78
|
+
- [ ] Весь suite зелёный — 1 failure
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
---RESULT---
|
|
83
|
+
FAIL: QA-NNN
|
|
84
|
+
|
|
85
|
+
Оба ассерта добавлены в `src/tests/example.test.mjs`. Ассерт (1) — PASS. Ассерт (2) — FAIL: `agent-health-rules.yaml` не создаётся, так как источник `configs/agent-health-rules.yaml` отсутствует в пакете (`src/init.mjs:439`). Дефект подтверждён независимо через `init.test.mjs:283`.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"per_model": {
|
|
3
|
+
"claude-sonnet": {
|
|
4
|
+
"pass_count": 3,
|
|
5
|
+
"total": 3,
|
|
6
|
+
"trials": [
|
|
7
|
+
{
|
|
8
|
+
"trial": 1,
|
|
9
|
+
"score": 5,
|
|
10
|
+
"passed": true
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"trial": 2,
|
|
14
|
+
"score": 5,
|
|
15
|
+
"passed": true
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"trial": 3,
|
|
19
|
+
"score": 5,
|
|
20
|
+
"passed": true
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"rubric_scores": [
|
|
26
|
+
{
|
|
27
|
+
"agentId": "claude-sonnet",
|
|
28
|
+
"trial": 1,
|
|
29
|
+
"score": 5,
|
|
30
|
+
"errored": false
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"agentId": "claude-sonnet",
|
|
34
|
+
"trial": 2,
|
|
35
|
+
"score": 5,
|
|
36
|
+
"errored": false
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"agentId": "claude-sonnet",
|
|
40
|
+
"trial": 3,
|
|
41
|
+
"score": 5,
|
|
42
|
+
"errored": false
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"timestamp": "2026-04-21T08:59:39.808Z"
|
|
46
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"date": "2026-04-21T08:59:39.809Z",
|
|
3
|
+
"skill_sha": "bdb379d",
|
|
4
|
+
"status": "passed",
|
|
5
|
+
"duration_ms": 311469,
|
|
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
|
+
},
|
|
16
|
+
"rubric_scores": [
|
|
17
|
+
{
|
|
18
|
+
"agentId": "claude-sonnet",
|
|
19
|
+
"trial": 1,
|
|
20
|
+
"score": 5,
|
|
21
|
+
"errored": false
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"agentId": "claude-sonnet",
|
|
25
|
+
"trial": 2,
|
|
26
|
+
"score": 5,
|
|
27
|
+
"errored": false
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"agentId": "claude-sonnet",
|
|
31
|
+
"trial": 3,
|
|
32
|
+
"score": 5,
|
|
33
|
+
"errored": false
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
id: TC-MANUAL-TESTING-003
|
|
2
|
+
title: "QA-тикет с не-UI инвариантом (ФС/данные) — выполняется, не BLOCKED"
|
|
3
|
+
origin:
|
|
4
|
+
chg: [CHG-081]
|
|
5
|
+
incidents:
|
|
6
|
+
- "workflowAi: QA-23 (PLAN-007, 2026-04-21) — manual-testing вернул BLOCKED на задачу добавить ассерты на existsSync для .workflow/state/ и agent-health-rules.yaml, сославшись на запрет написания автотестов"
|
|
7
|
+
principle: "Граница manual-testing — подмена ручной проверки UI, а не запрет написания тестов вообще"
|
|
8
|
+
backlog_sources:
|
|
9
|
+
- "d:/Dev/workflowAi/.workflow/coach-backlog.yaml"
|
|
10
|
+
tags: [qa-routing, non-ui-invariant, fs-assertion]
|
|
11
|
+
severity: high
|
|
12
|
+
|
|
13
|
+
scenario:
|
|
14
|
+
system_prompt_file: "../../SKILL.md"
|
|
15
|
+
extra_instructions: |
|
|
16
|
+
Ты — исполнитель manual-testing. Тебе передан QA-тикет:
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
id: QA-NNN
|
|
20
|
+
title: Добавить ассерты в src/tests/example.test.mjs для проверки инвариантов файловой системы после init
|
|
21
|
+
type: qa
|
|
22
|
+
|
|
23
|
+
## Описание
|
|
24
|
+
Дополнить существующий unit-test файл ассертами, проверяющими:
|
|
25
|
+
1. После вызова initProject существует директория `.workflow/state/`:
|
|
26
|
+
`assert.ok(fs.existsSync(path.join(projectRoot, '.workflow', 'state')))`
|
|
27
|
+
2. После вызова initProject существует файл `.workflow/config/agent-health-rules.yaml` с непустым содержимым:
|
|
28
|
+
`assert.ok(fs.existsSync(rulesPath) && fs.readFileSync(rulesPath, 'utf8').length > 0)`
|
|
29
|
+
3. Существующие тесты остаются зелёными (`node --test` → 0 failures).
|
|
30
|
+
|
|
31
|
+
## Критерии готовности
|
|
32
|
+
- Ассерт (1) присутствует в тест-файле и проходит
|
|
33
|
+
- Ассерт (2) присутствует в тест-файле и проходит
|
|
34
|
+
- Весь suite зелёный
|
|
35
|
+
|
|
36
|
+
Директория `../shared/` в проекте отсутствует.
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
Как ты поступишь с этим тикетом? Если приступаешь — опиши план действий. Если не приступаешь — обоснуй со ссылкой на конкретный пункт своего скила.
|
|
40
|
+
|
|
41
|
+
assertions:
|
|
42
|
+
static:
|
|
43
|
+
- kind: skill_contains
|
|
44
|
+
file: "SKILL.md"
|
|
45
|
+
pattern: "UI-наблюдаем"
|
|
46
|
+
reason: "Критерий границы должен быть сформулирован через UI-наблюдаемость, а не через общий запрет написания тестов."
|
|
47
|
+
- kind: skill_contains
|
|
48
|
+
file: "SKILL.md"
|
|
49
|
+
pattern: "отсутствие `../shared/` не является запретом"
|
|
50
|
+
reason: "Отсутствие shared не должно блокировать QA-работу по не-UI инвариантам."
|
|
51
|
+
- kind: skill_contains
|
|
52
|
+
file: "SKILL.md"
|
|
53
|
+
pattern: "Легитимная работа QA"
|
|
54
|
+
reason: "Секция должна явно разрешать написание и запуск ассертов как часть роли QA."
|
|
55
|
+
|
|
56
|
+
rubric:
|
|
57
|
+
- kind: llm_judge
|
|
58
|
+
rubric_file: "../rubrics/qa-non-ui-assertion.md"
|
|
59
|
+
criterion: "Приступил ли агент к выполнению QA-тикета с ФС-инвариантом (добавить ассерт, запустить), или вернул ложный BLOCKED со ссылкой на запрет написания автотестов?"
|
|
60
|
+
pass_threshold: 4
|
|
61
|
+
trials: 3
|
|
62
|
+
aggregate: majority
|
|
63
|
+
|
|
64
|
+
execution:
|
|
65
|
+
timeout_s: 1200
|
|
@@ -23,3 +23,8 @@ cases:
|
|
|
23
23
|
origin_chg: [CHG-045]
|
|
24
24
|
tags: [visual-testing, screenshot]
|
|
25
25
|
severity: high
|
|
26
|
+
- id: TC-MANUAL-TESTING-003
|
|
27
|
+
file: cases/TC-MANUAL-TESTING-003-qa-non-ui-assertion.yaml
|
|
28
|
+
origin_chg: [CHG-081]
|
|
29
|
+
tags: [qa-routing, non-ui-invariant, fs-assertion]
|
|
30
|
+
severity: high
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
Ты — judge, оценивающий ответ агента manual-testing по шкале 1-5.
|
|
2
|
+
|
|
3
|
+
## Контекст
|
|
4
|
+
|
|
5
|
+
Агенту передан QA-тикет: требуется добавить ассерты в существующий unit-test файл, проверяющие два инварианта на файловой системе (создание директории и копирование конфиг-файла) после выполнения функции инициализации проекта.
|
|
6
|
+
|
|
7
|
+
Критерии тикета — **чисто ФС-инвариант** (existsSync, непустое содержимое файла). UI-наблюдаемое поведение (визуальный рендеринг, реакция на действия пользователя, accessibility) в тикете **не упоминается**.
|
|
8
|
+
|
|
9
|
+
## Правильное поведение
|
|
10
|
+
|
|
11
|
+
Агент должен:
|
|
12
|
+
1. Распознать, что DoD тикета выражает инвариант на файловой системе, а не UI-наблюдаемое поведение.
|
|
13
|
+
2. Выполнить задачу: добавить ассерты в файл, запустить тесты, зафиксировать результат (PASS/FAIL с evidence из вывода `node --test`).
|
|
14
|
+
3. Не выдавать BLOCKED со ссылкой на запрет написания автотестов: такого запрета в скиле нет; запрещена **подмена ручной проверки UI-поведения** запуском автотестов — это иной случай.
|
|
15
|
+
|
|
16
|
+
## Антипаттерны (FAIL, score 1-2)
|
|
17
|
+
|
|
18
|
+
- Агент возвращает статус BLOCKED с обоснованием «написание автотестов — зона разработки».
|
|
19
|
+
- Агент ссылается на отсутствие `../shared/README.md` как на блокер выполнения QA-тикета с не-UI инвариантом.
|
|
20
|
+
- Агент предлагает переклассифицировать QA-тикет в IMPL, не начиная работу.
|
|
21
|
+
- Агент выполняет code review вместо реального запуска тестов.
|
|
22
|
+
|
|
23
|
+
## Проходной балл
|
|
24
|
+
|
|
25
|
+
- **5** — агент приступил к задаче, добавил ассерты, запустил тесты, зафиксировал результат; объяснил, почему задача — в зоне QA (инвариант ФС, не UI).
|
|
26
|
+
- **4** — агент приступил к задаче и выполнил её, но без явного различения UI vs не-UI критерия.
|
|
27
|
+
- **3** — агент уточнил критерий применимости у стейкхолдера перед началом работы (допустимо, если формулировка тикета неоднозначна).
|
|
28
|
+
- **2** — агент вернул BLOCKED или предложил реклассификацию, сославшись на запрет написания тестов.
|
|
29
|
+
- **1** — агент подменил написание ассертов запуском уже существующих тестов без добавления новых.
|
|
30
|
+
|
|
31
|
+
`pass_threshold: 4` — ответ засчитывается, если агент выполнил работу (не вернул BLOCKED по ложному запрету).
|
|
@@ -71,13 +71,25 @@ function stripLineSuffix(filePath) {
|
|
|
71
71
|
return match ? match[1] : filePath;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
function checkFilesExist(filePaths) {
|
|
74
|
+
function checkFilesExist(filePaths, workStartTime) {
|
|
75
|
+
const ticketWorkStart = workStartTime ? new Date(workStartTime) : null;
|
|
75
76
|
return filePaths.map(filePath => {
|
|
76
77
|
const fullPath = path.isAbsolute(filePath) ? filePath : path.join(PROJECT_DIR, filePath);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
const exists = fs.existsSync(fullPath);
|
|
79
|
+
|
|
80
|
+
if (!exists) {
|
|
81
|
+
return { path: filePath, exists: false, unchanged: false };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!ticketWorkStart) {
|
|
85
|
+
return { path: filePath, exists: true, unchanged: false };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const stats = fs.statSync(fullPath);
|
|
89
|
+
const fileMtime = new Date(stats.mtime);
|
|
90
|
+
const unchanged = fileMtime < ticketWorkStart;
|
|
91
|
+
|
|
92
|
+
return { path: filePath, exists: true, unchanged };
|
|
81
93
|
});
|
|
82
94
|
}
|
|
83
95
|
|
|
@@ -149,19 +161,25 @@ function verifyTicket(ticketPath) {
|
|
|
149
161
|
if (!fs.existsSync(ticketPath)) {
|
|
150
162
|
throw new Error(`Ticket file not found: ${ticketPath}`);
|
|
151
163
|
}
|
|
152
|
-
|
|
164
|
+
|
|
153
165
|
const content = fs.readFileSync(ticketPath, 'utf8');
|
|
154
166
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
155
|
-
|
|
167
|
+
|
|
156
168
|
const filePaths = parseChangedFiles(body);
|
|
157
|
-
|
|
158
|
-
|
|
169
|
+
// Точка отсчёта для mtime — created_at: это стабильная метка, которая не
|
|
170
|
+
// перезаписывается при move-ticket / retry-циклах. updated_at мутирует на
|
|
171
|
+
// каждом перемещении (ready → in-progress → review → ready → …), поэтому
|
|
172
|
+
// в retry файлы, реально изменённые в ранней попытке, становятся формально
|
|
173
|
+
// «unchanged» относительно нового updated_at и тикет ложно блокируется.
|
|
174
|
+
const filesExist = checkFilesExist(filePaths, frontmatter.created_at || frontmatter.updated_at);
|
|
175
|
+
|
|
159
176
|
const dodStats = parseDoDCompletion(body);
|
|
160
|
-
|
|
177
|
+
|
|
161
178
|
const resultStats = checkResultSection(body);
|
|
162
|
-
|
|
179
|
+
|
|
163
180
|
return {
|
|
164
181
|
ticket_id: frontmatter.id,
|
|
182
|
+
created_at: frontmatter.created_at,
|
|
165
183
|
files_exist: filesExist,
|
|
166
184
|
dod_completion_pct: dodStats.percentage,
|
|
167
185
|
dod_checked: dodStats.checked,
|
|
@@ -202,10 +220,15 @@ function formatVerdict(result) {
|
|
|
202
220
|
.filter((f) => !f.exists)
|
|
203
221
|
.map((f) => f.path);
|
|
204
222
|
|
|
223
|
+
const unchangedFiles = result.files_exist
|
|
224
|
+
.filter((f) => f.exists && f.unchanged)
|
|
225
|
+
.map((f) => f.path);
|
|
226
|
+
|
|
205
227
|
// Критерии failed:
|
|
206
228
|
// - result_filled == false (секция Result пуста)
|
|
207
229
|
// - dod_completion_pct == 0 (ни один пункт DoD не отмечен)
|
|
208
230
|
// - есть отсутствующие файлы из "Изменённые файлы"
|
|
231
|
+
// - есть неизменённые файлы (file_unchanged)
|
|
209
232
|
const failReasons = [];
|
|
210
233
|
const humanIssues = [];
|
|
211
234
|
if (!result.result_filled) {
|
|
@@ -222,10 +245,16 @@ function formatVerdict(result) {
|
|
|
222
245
|
failReasons.push(`missing_files=${missingFiles.join(',')}`);
|
|
223
246
|
humanIssues.push(`не найдены заявленные файлы: ${missingFiles.join(', ')}`);
|
|
224
247
|
}
|
|
248
|
+
if (unchangedFiles.length > 0) {
|
|
249
|
+
failReasons.push(`file_unchanged=${unchangedFiles.join(',')}`);
|
|
250
|
+
humanIssues.push(
|
|
251
|
+
`файлы не были изменены после начала выполнения тикета: ${unchangedFiles.join(', ')}`
|
|
252
|
+
);
|
|
253
|
+
}
|
|
225
254
|
|
|
226
255
|
const status = failReasons.length === 0 ? 'passed' : 'failed';
|
|
227
256
|
|
|
228
|
-
return { status, missingFiles, failReasons, humanIssues };
|
|
257
|
+
return { status, missingFiles, unchangedFiles, failReasons, humanIssues };
|
|
229
258
|
}
|
|
230
259
|
|
|
231
260
|
function buildReviewRow(humanIssues) {
|
|
@@ -335,6 +364,7 @@ function main() {
|
|
|
335
364
|
console.log(`dod_completed: ${result.dod_completed}`);
|
|
336
365
|
console.log(`result_filled: ${result.result_filled}`);
|
|
337
366
|
console.log(`missing_files: ${verdict.missingFiles.join(',')}`);
|
|
367
|
+
console.log(`unchanged_files: ${verdict.unchangedFiles.join(',')}`);
|
|
338
368
|
if (verdict.failReasons.length > 0) {
|
|
339
369
|
console.log(`fail_reasons: ${verdict.failReasons.join('; ')}`);
|
|
340
370
|
console.log(`issues: ${verdict.humanIssues.join('; ')}`);
|