gennady 0.8.2 → 0.8.4
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 +212 -3
- package/ai/agents/agent-resolve-conflicts.xml +148 -3
- package/ai/agents/agent-review-verifier.xml +181 -3
- package/ai/directives/sdd/audit.directive.xml +22 -11
- package/ai/directives/sdd/critic-protocol.xml +3 -0
- package/ai/directives/sdd/critic.directive.xml +25 -3
- package/ai/skills/README.md +150 -0
- package/ai/skills/sdd-check/SKILL.md +19 -9
- package/ai/skills/sdd-execute/scripts/README.md +3 -0
- package/ai/skills/sdd-execute/scripts/_sdd-lib.sh +60 -0
- package/ai/skills/sdd-execute/scripts/check.sh +238 -0
- package/ai/skills/sdd-execute/scripts/sdd +12 -0
- package/ai/skills/sdd-hooks-install/SKILL.md +88 -0
- package/ai/skills/workspace-permission-setup/SKILL.md +193 -0
- package/cli/cmd/README.md +185 -0
- package/dist/ai/agents/agent-resolve-conflicts.xml +148 -3
- package/dist/ai/agents/agent-review-verifier.xml +181 -3
- package/dist/ai/directives/sdd/audit.directive.xml +22 -11
- package/dist/ai/directives/sdd/critic-protocol.xml +3 -0
- package/dist/ai/directives/sdd/critic.directive.xml +25 -3
- package/dist/ai/skills/README.md +150 -0
- package/dist/ai/skills/sdd-check/SKILL.md +19 -9
- package/dist/ai/skills/sdd-execute/scripts/README.md +3 -0
- package/dist/ai/skills/sdd-execute/scripts/_sdd-lib.sh +60 -0
- package/dist/ai/skills/sdd-execute/scripts/check.sh +238 -0
- package/dist/ai/skills/sdd-execute/scripts/sdd +12 -0
- package/dist/ai/skills/sdd-hooks-install/SKILL.md +88 -0
- package/dist/ai/skills/workspace-permission-setup/SKILL.md +193 -0
- package/dist/chunks/index-BHEP1kYv.js +164 -0
- package/dist/chunks/index-DqSFtXFv.js +813 -0
- package/dist/chunks/index-Ukr3nSKB.js +377 -0
- package/dist/cli/cmd/lint/checks/anchor-thin.check.d.ts +11 -0
- package/dist/cli/cmd/lint/lint.cmd.d.ts +1 -1
- package/dist/cli/cmd/lint/lint.types.d.ts +2 -0
- package/dist/gennady.js +4 -4
- package/package.json +1 -1
- package/dist/chunks/index-4fUNp3za.js +0 -156
- package/dist/chunks/index-C9UEDrqg.js +0 -754
- package/dist/chunks/index-jOIhIHdS.js +0 -369
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
7. Non-goals — explicit exclusions.
|
|
22
22
|
8. Consistency — no internal contradictions.
|
|
23
23
|
9. Testability — claims verifiable, not vague ("should be fast").
|
|
24
|
+
10. Parsimony — every entity/type/contract traces to a requirement or use-case. Flag: unjustified (no driving requirement), redundant (two entities overlap WITHIN this spec), speculative (surface beyond stated boundary). Prefer reuse/extend over a new entity. Scope is intra-spec only — you cannot see the project, so never claim "X already exists elsewhere" (that is the orchestrator's check).
|
|
24
25
|
</Axiom>
|
|
25
26
|
|
|
26
27
|
<Axiom id="AX_TASK_DIMENSIONS">
|
|
@@ -30,6 +31,7 @@
|
|
|
30
31
|
3. Phases — ordered correctly, deps explicit, no gaps (impl without test), Target Files non-overlapping.
|
|
31
32
|
4. Clarity — fresh agent can build from this alone. Inputs/outputs specified.
|
|
32
33
|
5. Completeness — all sections filled, no <TBD> or <YYYY-MM-DD>.
|
|
34
|
+
6. Parsimony — task introduces NO entity/type/method absent from the parent spec inventory. New surface declared in a task without spec backing = finding. Spec entities are reused, not re-declared. Cross-project reuse is the orchestrator's check, not yours.
|
|
33
35
|
</Axiom>
|
|
34
36
|
|
|
35
37
|
<Axiom id="AX_SEVERITY">
|
|
@@ -54,6 +56,7 @@
|
|
|
54
56
|
<Pattern id="AP_FORMAT_BIAS">Assuming quality from formatting. Structure ≠ correctness.</Pattern>
|
|
55
57
|
<Pattern id="AP_CREEP">Reading beyond artifact + parent spec.</Pattern>
|
|
56
58
|
<Pattern id="AP_INTERNAL_PARANOIA">Flagging type violations at internal boundaries. Type system prevents these.</Pattern>
|
|
59
|
+
<Pattern id="AP_REUSE_BLIND">Claiming an entity duplicates something "in the project". You see only artifact + parent spec — judge redundancy ONLY within that scope. Project-wide dedup is the orchestrator's job.</Pattern>
|
|
57
60
|
</Anti_Patterns>
|
|
58
61
|
|
|
59
62
|
<Execution_Plan>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<SddCritic keywords="critic, spec-review, task-review, bdd-audit, blind-spot, autonomous-polish, multi-round, pre-execute, orchestration" type="directive" ver="2.0">
|
|
2
2
|
<Directive_Context>
|
|
3
3
|
<Mission>
|
|
4
|
-
Autonomous critique loop for SDD artifacts — spec, task, or batch (spec + all tasks). You are the ORCHESTRATOR in the root session. Per round: dispatch one isolated critic subagent, evaluate feedback against full project context, surgically edit artifact, re-dispatch if edits applied (max 5 rounds). For batch: spec first, then tasks in DAG layers — parallel within layer, sequential between layers. On CLEAN: delete `## Critic Rounds` (temporary scaffolding). Artifact is clean, improvements integrated.
|
|
4
|
+
Autonomous critique loop for SDD artifacts — spec, task, or batch (spec + all tasks). You are the ORCHESTRATOR in the root session. Per round: dispatch one isolated critic subagent, evaluate feedback against full project context, reconcile every introduced entity against existing project surface (reuse > extend > justify > escalate), surgically edit artifact, re-dispatch if edits applied (max 5 rounds). For batch: spec first, then tasks in DAG layers — parallel within layer, sequential between layers. On CLEAN: delete `## Critic Rounds` (temporary scaffolding). Artifact is clean, improvements integrated.
|
|
5
5
|
</Mission>
|
|
6
6
|
</Directive_Context>
|
|
7
7
|
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
<Axiom id="AX_DEFAULT_ACCEPT">Uncertain → ACCEPT. Cost of rejecting real finding > cost of accepting marginal one.</Axiom>
|
|
16
16
|
<Axiom id="AX_CONFUSION_BUG">Critic confusion ("don't understand X") → artifact underspecified → ALWAYS accept, clarify.</Axiom>
|
|
17
17
|
<Axiom id="AX_SURGICAL">Close the gap only. No restructure, rewrite, restyle. Structural overhaul → suggest re-scaffold, stop.</Axiom>
|
|
18
|
+
<Axiom id="AX_REUSE_FIRST">Orchestrator holds full project context — the ONLY actor able to detect duplication against existing code/specs (the isolated critic cannot). For every entity/type/method/contract the artifact introduces, reconcile before accepting it: REUSE existing (adapt artifact to it) > EXTEND existing > JUSTIFY new (record one-line rationale in artifact: why existing surface is insufficient) > ESCALATE to operator. Unjustified new surface is a finding to resolve, not to wave through. Bias toward consolidation — do not let the system grow a duplicate entity each round.</Axiom>
|
|
18
19
|
<Axiom id="AX_SCRATCH_LOG">`## Critic Rounds` = temporary working memory for orchestrator between rounds. Deleted on final CLEAN.</Axiom>
|
|
19
20
|
<Axiom id="AX_TASK_PARALLEL">Tasks critiqued in DAG layers. Same layer (no inter-dependencies) → parallel dispatch. Different layers → sequential.</Axiom>
|
|
20
21
|
<Axiom id="AX_LANG">Directive: English. Artifact prose (Critic Rounds, summary): Russian.</Axiom>
|
|
@@ -36,6 +37,7 @@
|
|
|
36
37
|
<Pattern id="AP_OVEREDIT">Rewriting sections. Close the gap, don't redecorate.</Pattern>
|
|
37
38
|
<Pattern id="AP_DISMISS_CONFUSION">Ignoring "I don't understand X". Always fix.</Pattern>
|
|
38
39
|
<Pattern id="AP_VAGUE_REJECT">Rejecting without citing specific context.</Pattern>
|
|
40
|
+
<Pattern id="AP_ENTITY_SPRAWL">Accepting a new entity/type/method without first searching the project for one to reuse or extend. New surface every round = sprawl.</Pattern>
|
|
39
41
|
</Anti_Patterns>
|
|
40
42
|
|
|
41
43
|
<Execution_Plan>
|
|
@@ -71,14 +73,27 @@
|
|
|
71
73
|
ACCEPT: genuine gap, misunderstanding reveals ambiguity, would break downstream agent, or uncertain (AX_DEFAULT_ACCEPT).
|
|
72
74
|
REJECT: misreading no competent agent would make AND specific context cited that resolves it.
|
|
73
75
|
Confusion points → ALWAYS accept (AX_CONFUSION_BUG).
|
|
74
|
-
|
|
76
|
+
Go to STEP_2B (reconcile entities) — always, even with zero accepted findings.
|
|
77
|
+
</Action>
|
|
78
|
+
</Step>
|
|
79
|
+
|
|
80
|
+
<Step id="2B_RECONCILE">
|
|
81
|
+
<Goal>Reuse-first reconciliation — orchestrator-only, uses full project context (AX_REUSE_FIRST).</Goal>
|
|
82
|
+
<Action>
|
|
83
|
+
Round 1: reconcile EVERY entity/type/method/contract the artifact introduces. Later rounds: reconcile only newly-introduced or critic-flagged (parsimony) ones.
|
|
84
|
+
For each, search the project (code + sibling specs) for surface that already covers it, then classify:
|
|
85
|
+
- REUSE — existing covers it → edit artifact to point at it, drop the new declaration (surgical).
|
|
86
|
+
- EXTEND — existing should grow → note the extension target in the artifact, drop the duplicate (surgical).
|
|
87
|
+
- JUSTIFY — genuinely new → record one-line rationale in the artifact (why existing surface is insufficient) (surgical).
|
|
88
|
+
- ESCALATE — call unclear, OR consolidation requires merging/removing entities (structural) → surface to operator; H_STRUCTURAL if it needs re-scaffold. Do NOT perform structural consolidation yourself.
|
|
89
|
+
Surgical reuse/justify edits + accepted findings → go to STEP_3. Zero edits after reconciliation → go to STEP_5 (stop).
|
|
75
90
|
</Action>
|
|
76
91
|
</Step>
|
|
77
92
|
|
|
78
93
|
<Step id="3_EDIT">
|
|
79
94
|
<Goal>Surgical edit.</Goal>
|
|
80
95
|
<Action>
|
|
81
|
-
Per accepted finding: add missing BDD scenario, clarify boundary, correct entity ref, fill placeholder. Target only the gap. Structural rewrite → H_STRUCTURAL.
|
|
96
|
+
Per accepted finding: add missing BDD scenario, clarify boundary, correct entity ref, fill placeholder. Per reconcile decision: point at existing surface (REUSE/EXTEND) or record new-entity rationale (JUSTIFY). Target only the gap. Structural rewrite or entity merge/removal → H_STRUCTURAL / ESCALATE.
|
|
82
97
|
</Action>
|
|
83
98
|
</Step>
|
|
84
99
|
|
|
@@ -91,6 +106,7 @@
|
|
|
91
106
|
- Verdict: <CLEAN|NEEDS_WORK|CRITICAL>
|
|
92
107
|
- Accepted: N — <list>
|
|
93
108
|
- Rejected: N — "<finding>" → <reason>
|
|
109
|
+
- Reconcile: <entity → REUSE <target> | EXTEND <target> | JUSTIFY <rationale> | ESCALATE>
|
|
94
110
|
- Changes: <list>
|
|
95
111
|
```
|
|
96
112
|
Append-only.
|
|
@@ -136,6 +152,10 @@
|
|
|
136
152
|
- <change>
|
|
137
153
|
- ...
|
|
138
154
|
|
|
155
|
+
♻️ Reuse decisions:
|
|
156
|
+
- <entity> → REUSE <target> | EXTEND <target> | JUSTIFY <rationale> | ESCALATE <question>
|
|
157
|
+
- ...
|
|
158
|
+
|
|
139
159
|
⚠️ Unresolved (if not CLEAN):
|
|
140
160
|
- <finding> → <recommendation>
|
|
141
161
|
|
|
@@ -154,5 +174,7 @@
|
|
|
154
174
|
- Keep `## Critic Rounds` after CLEAN.
|
|
155
175
|
- ACCEPT without edit.
|
|
156
176
|
- REJECT without specific context.
|
|
177
|
+
- Accept a new entity without reuse-first reconciliation (AX_REUSE_FIRST).
|
|
178
|
+
- Structurally merge/remove entities yourself — ESCALATE instead.
|
|
157
179
|
</HardForbidden>
|
|
158
180
|
</SddCritic>
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# ai/skills — AI-навыки для агентов
|
|
2
|
+
|
|
3
|
+
15 навыков (13 SDD + alt-opinion + workspace-permission-setup) для Specification-Driven Development, мульти-модельного анализа и настройки автономной среды.
|
|
4
|
+
|
|
5
|
+
> `sdd-hooks-install` (хуки live-прогресса для `sdd-execute`) и `workspace-permission-setup` мигрированы в хранилище из `~/.claude/skills` — теперь под git и деплоятся через `sync-skills`.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Типовые сценарии (Use Cases)
|
|
10
|
+
|
|
11
|
+
### 1. Спроектировать новый модуль с нуля
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx gennady sync-skills
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Затем в агенте: «@sdd-setup создай проект» → «@sdd-discover спроектируй scope vcs-client» → «@sdd-module-decomposition разбей на модули» → «@sdd-scaffold сгенерируй таски» → «@sdd-critic проверь таски» → «@sdd-execute TSK-01»
|
|
18
|
+
|
|
19
|
+
| Шаг | Навык | Что делает |
|
|
20
|
+
| --- | ----- | ---------- |
|
|
21
|
+
| 1 | `sdd-setup` | Инициализирует `specs/README.md` (Vision, Scope Graph, таблица скоупов) |
|
|
22
|
+
| 2 | `sdd-discover` | Создаёт `specs/<scope>/<scope>.spec.md` — видение, требования, архитектура |
|
|
23
|
+
| 3 | `sdd-module-decomposition` | Декомпозирует product/library scope на модульные спеки с инвентарём сущностей |
|
|
24
|
+
| 4 | `sdd-scaffold` | Генерирует DAG тасков из спек: Cascade Table, BDD, Phases Overview |
|
|
25
|
+
| 5 | `sdd-critic` | Многораундовая критика тасков: диспатчит критика, правит артефакт (до 5 раундов) |
|
|
26
|
+
| 6 | `sdd-execute` | Исполняет один таск от начала до конца: dispatch фаз → audit |
|
|
27
|
+
|
|
28
|
+
### 2. Выполнить задачу
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
@sdd-execute TSK-03
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Или: «выполни следующую», «execute pickable», «выбери что делать дальше».
|
|
35
|
+
|
|
36
|
+
Навык читает таск, диспатчит фазы одну за другой, закрывает round, диспатчит fresh-eyes audit.
|
|
37
|
+
|
|
38
|
+
### 3. Выполнить пачку задач
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
@sdd-execute-batch выполни всю очередь
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Параллелит таски с непересекающимися файлами. Опциональный epic-level audit.
|
|
45
|
+
|
|
46
|
+
### 4. Проверить качество спеки / таска
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
@sdd-critic проверь спеку cli/cli.spec.md
|
|
50
|
+
@sdd-critic проверь таск TSK-03
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Многораундовая критика (до 5 раундов): диспатчит изолированного критика, оценивает фидбек, правит артефакт.
|
|
54
|
+
|
|
55
|
+
### 5. Продолжить / доработать существующую спеку
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
@sdd-continue добавь sync-skills в cli
|
|
59
|
+
@sdd-continue измени архитектуру на event-driven
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Автоопределение режима: refine (добавить) или pivot (заменить).
|
|
63
|
+
|
|
64
|
+
### 6. Проверить целостность SDD-воркфлоу
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
@sdd-check
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Read-only: проверяет связность спек, синхронизацию трекеров, полноту execution-логов, консистентность DAG.
|
|
71
|
+
|
|
72
|
+
### 7. Аудит завершённой задачи
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
@sdd-audit TSK-05
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Fresh-eyes: читает таск + спеку + git diff, механический линтинг, верификация правил. Фидинги роутятся в артефакты (правки спек, переоткрытие тасков).
|
|
79
|
+
|
|
80
|
+
### 8. Починить после ревью / sdd-check
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
@sdd-fix найди и исправь проблемы из sdd-check
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Классифицирует фидинги, согласовывает с оператором, исполняет фиксы, переоткрывает таски, верифицирует.
|
|
87
|
+
|
|
88
|
+
### 9. Спроектировать инфраструктурный скоуп
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
@sdd-infra спроектируй infra-golang
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Для bootstrap'а tooling'а: package manager, type-checker, linter, formatter, test runner, git hooks, CI.
|
|
95
|
+
|
|
96
|
+
### 10. Получить мульти-модельное мнение
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
@alt-opinion оцени спеку cli/cli.spec.md
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Запускает 2+ модели параллельно, синтезирует через третью. Без аргументов — автоаудит текущего контекста сессии.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Execution-паттерны
|
|
107
|
+
|
|
108
|
+
| Паттерн | Как работает | Навыки |
|
|
109
|
+
| ------- | ----------- | ------ |
|
|
110
|
+
| **Directive activation** | Извлечь intent → загрузить директиву → активироваться как она → выполнить план | sdd-setup, sdd-discover, sdd-module-decomposition, sdd-scaffold, sdd-audit, sdd-continue, sdd-critic, sdd-fix, sdd-infra |
|
|
111
|
+
| **Orchestrator** | Прочитать таск → dispatch фаз (typed Handoff) → dispatch audit. Сам код не пишет | sdd-execute, sdd-execute-batch |
|
|
112
|
+
| **CLI delegation** | Подготовить артефакт → вызвать `npx gennady alt-opinion` → показать результат | alt-opinion |
|
|
113
|
+
| **Read-only verifier** | Саморефлексия + механические проверки через `sdd scan`. Код не пишет | sdd-check |
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Структура навыка
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
ai/skills/<name>/
|
|
121
|
+
├── SKILL.md # YAML frontmatter (name, description, compatibility) + markdown body
|
|
122
|
+
├── scripts/ # опционально: bash/js утилиты
|
|
123
|
+
└── *.prompt.md # опционально: кастомные промпты
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Синхронизация в проекты
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Синхронизировать все навыки
|
|
132
|
+
npx gennady sync-skills
|
|
133
|
+
|
|
134
|
+
# Предпросмотр
|
|
135
|
+
npx gennady sync-skills --dry-run
|
|
136
|
+
|
|
137
|
+
# Конкретный навык
|
|
138
|
+
npx gennady sync-skills sdd-execute
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Навыки деплоятся из `ai/skills/` → `.claude/skills/` проекта. Пути нормализуются: dev-пути (`~/Developer/gennady/...`) заменяются на продуктовые.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Связанные спеки
|
|
146
|
+
|
|
147
|
+
- `specs/ai-skills/ai-skills.spec.md` — общая спека библиотеки
|
|
148
|
+
- `specs/ai-skills/skill-contract/skill-contract.spec.md` — контракт навыка
|
|
149
|
+
- `specs/ai-skills/sdd-skills/sdd-skills.spec.md` — SDD-навыки
|
|
150
|
+
- `specs/ai-skills/alt-opinion/alt-opinion.spec.md` — alt-opinion
|
|
@@ -42,9 +42,12 @@ Output: a compact self-assessment table before proceeding to mechanical checks.
|
|
|
42
42
|
<ExecutionStrategy>
|
|
43
43
|
**BATCH ALL READS.** Use the SDD scan tool for one-shot snapshot, then targeted reads.
|
|
44
44
|
|
|
45
|
-
1. **
|
|
46
|
-
`~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd scan <project-root>`
|
|
47
|
-
|
|
45
|
+
1. **Two bash calls** to get comprehensive snapshot + mechanical findings:
|
|
46
|
+
- `~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd scan <project-root>`
|
|
47
|
+
→ [HEADER] [TASKS] [TRACKERS] [SPECS] [WARNINGS] [SUMMARY] in one call.
|
|
48
|
+
- `~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd check <project-root>`
|
|
49
|
+
→ [TASKID] (collisions, orphan @tasks refs) + [TRACKER_SYNC] (ticket Meta.Status vs tracker row).
|
|
50
|
+
This is the SAME tool sdd-audit uses — do NOT re-implement these greps by hand (Checks 3 & 5b below read its output).
|
|
48
51
|
|
|
49
52
|
2. **Concurrent reads** of all key files: Portal, scope specs (from SPECS list), trackers with issues (from WARNINGS). Do NOT read module specs initially — only if Check 2 needs them.
|
|
50
53
|
|
|
@@ -52,7 +55,7 @@ Output: a compact self-assessment table before proceeding to mechanical checks.
|
|
|
52
55
|
|
|
53
56
|
4. **Do NOT scan code files.** Check 6 samples at most 3 files.
|
|
54
57
|
|
|
55
|
-
Total: ≤
|
|
58
|
+
Total: ≤6 tool calls for all checks (scan + check + check --files + targeted reads). Target: <15 seconds.
|
|
56
59
|
</ExecutionStrategy>
|
|
57
60
|
|
|
58
61
|
<Checks>
|
|
@@ -66,9 +69,9 @@ Read `specs/README.md`. Verify: scopes table entries match `specs/<scope>/<scope
|
|
|
66
69
|
|
|
67
70
|
For each scope spec 9/7: module paths resolve to files. Module spec 1 links to parent. Cross-scope references resolve.
|
|
68
71
|
|
|
69
|
-
### Check 3 — Tracker Sync (
|
|
72
|
+
### Check 3 — Tracker Sync (from `sdd check` [TRACKER_SYNC])
|
|
70
73
|
|
|
71
|
-
|
|
74
|
+
Read the [TRACKER_SYNC] section of `sdd check`. Each row `match=NO` (ticket Meta.Status ≠ tracker row) or `NO_ROW` (ticket has no tracker row) is a FAIL with the Task-ID. `UNPARSEABLE` (old-template ticket without Meta.Status) → INFO, not a fail. Do NOT re-grep counts by hand.
|
|
72
75
|
|
|
73
76
|
### Check 4 — DAG Consistency (parse from tracker)
|
|
74
77
|
|
|
@@ -78,9 +81,15 @@ Parse `Dependencies:` from each task ticket planning surface. Topological sort.
|
|
|
78
81
|
|
|
79
82
|
From scan [TASKS] output: check `placeholders` column for any task with >0. Flag tasks where placeholders > 0 even if status DONE. Also inspect `warnings` column for `no-execlog-section` or `anchors-mismatch`.
|
|
80
83
|
|
|
81
|
-
### Check
|
|
84
|
+
### Check 5b — Task-ID Integrity (from `sdd check` [TASKID])
|
|
82
85
|
|
|
83
|
-
|
|
86
|
+
Read the [TASKID] section of `sdd check`. `collision` (one Task-ID on ≥2 ticket files) → FAIL (BLOCKER). `orphan` (a code `@tasks: TSK-NN` with no ticket file) → FAIL. Empty section → PASS. Same tool sdd-audit STEP_2_5 uses.
|
|
87
|
+
|
|
88
|
+
### Check 6 — File Headers (from `sdd check --files`)
|
|
89
|
+
|
|
90
|
+
Pass recently modified source files to the shared checker instead of sampling by hand:
|
|
91
|
+
`sdd check --files $(git diff --name-only HEAD~3 | grep -E '\.(ts|js|sh|go)$')`.
|
|
92
|
+
Read [HEADERS]: `verdict=PARTIAL` (has some markers, missing `@file` or `@tasks`) or `NONE` on a code file → FAIL. `@consumers` absence alone → MINOR.
|
|
84
93
|
|
|
85
94
|
### Check 7 — Test Coverage (1 find)
|
|
86
95
|
|
|
@@ -107,7 +116,8 @@ First: Self-Reflection. Then: Mechanical Checks. Use compact single-line-per-che
|
|
|
107
116
|
▸ MECHANICAL
|
|
108
117
|
✅ Portal 4 scopes, graph ↔ table
|
|
109
118
|
✅ Spec linking all modules → parent, cross-scope refs resolve
|
|
110
|
-
✅ Tracker sync dbc 13/13 cli 7/7 vcs 1/1
|
|
119
|
+
✅ Tracker sync dbc 13/13 cli 7/7 vcs 1/1 (sdd check [TRACKER_SYNC])
|
|
120
|
+
✅ Task-ID no collisions, no orphan @tasks (sdd check [TASKID])
|
|
111
121
|
✅ DAG no cycles, all deps satisfied
|
|
112
122
|
✅ Execution Log no <YYYY-MM-DD> placeholders in tasks
|
|
113
123
|
✅ File headers 21 files scanned, all have @file: + @consumers:
|
|
@@ -13,6 +13,7 @@ All scripts honor the contract: **never produce silent empty output**. On miss,
|
|
|
13
13
|
~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd verify <file>... # comprehensive gate (typecheck + lint + grep)
|
|
14
14
|
~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd check-blockers <ticket-file> # scan Execution Log per AX_BLOCKER_RESOLUTION_TRAIL
|
|
15
15
|
~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd scan [project-root] # one-shot rich snapshot for triage skills
|
|
16
|
+
~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd check [root|--task TSK-NN|--files f...] # deterministic mechanical checks (shared by sdd-check + sdd-audit)
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
Single permission rule covers all subcommands:
|
|
@@ -37,6 +38,8 @@ Or broader:
|
|
|
37
38
|
| `verify.sh` | Three-gate verification: typecheck + gennady DBC lint + forbidden-construct grep |
|
|
38
39
|
| `check-blockers.sh` | Detect unresolved BLOCKER entries in ticket Execution Log per `AX_BLOCKER_RESOLUTION_TRAIL` |
|
|
39
40
|
| `scan.sh` | Emit comprehensive project snapshot ([HEADER]/[TASKS]/[TRACKERS]/[SPECS]/[WARNINGS]/[SUMMARY]). Designed so triage skills make ONE call instead of many ad-hoc find/grep. Surfaces suspicious states automatically (DONE+placeholders, DONE+active-blocker, anchor mismatch, unparseable Status, broken spec links). |
|
|
41
|
+
| `check.sh` | Deterministic mechanical checks — [TASKID] (collisions, orphan `@tasks`), [TRACKER_SYNC] (ticket Meta.Status vs tracker row), [HEADERS] (`--files` mode). Single source of mechanical truth shared by `sdd-check` (whole tree) and `sdd-audit` (scoped via `--task`/`--files`). Exit 0 clean / 3 findings / 2 structural / 4 bad-invocation. |
|
|
42
|
+
| `_sdd-lib.sh` | Shared artifact parsers (status, Task-ID, tracker-row, header flags) sourced by `check.sh`. Not executed directly. `scan.sh` migration to this lib pending. |
|
|
40
43
|
|
|
41
44
|
## Anchor convention (used by `extract`)
|
|
42
45
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# @file: Shared SDD artifact parsers — sourced by scan.sh / check.sh, never executed directly.
|
|
2
|
+
# @consumers: check.sh (canonical), scan.sh (migration pending).
|
|
3
|
+
# @contract: pure functions of file contents; no stdout side effects beyond the echoed result.
|
|
4
|
+
#
|
|
5
|
+
# Why this lib exists:
|
|
6
|
+
# - check.sh and scan.sh both parse ticket Meta.Status, Task-ID, tracker rows, file headers.
|
|
7
|
+
# - One implementation here = no drift between the two SDD tree tools (the whole point of
|
|
8
|
+
# `sdd check`: a single source of mechanical truth that both sdd-check and sdd-audit consume).
|
|
9
|
+
#
|
|
10
|
+
# macOS bash 3.2 compatible: grep -E / sed -E / awk only. No grep -P, no GNU sed -i.
|
|
11
|
+
|
|
12
|
+
# Extract Meta.Status flag from a ticket. Echoes: DONE | TODO | IN_PROGRESS | BLOCKED | UNKNOWN
|
|
13
|
+
sdd_lib_status() {
|
|
14
|
+
local f="$1" line flag
|
|
15
|
+
line=$(head -60 "$f" 2>/dev/null | grep -m1 -E '^\s*-?\s*\*?\*?Status:\*?\*?\s*\[.\]' || true)
|
|
16
|
+
[[ -z "$line" ]] && { echo "UNKNOWN"; return; }
|
|
17
|
+
flag=$(echo "$line" | sed -nE 's/.*\[(.)\].*/\1/p')
|
|
18
|
+
case "$flag" in
|
|
19
|
+
x|X) echo "DONE" ;;
|
|
20
|
+
' ') echo "TODO" ;;
|
|
21
|
+
'~') echo "IN_PROGRESS" ;;
|
|
22
|
+
'!') echo "BLOCKED" ;;
|
|
23
|
+
*) echo "UNKNOWN" ;;
|
|
24
|
+
esac
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Extract Task-ID (TSK-NN) from a ticket Meta. Echoes the ID or empty string.
|
|
28
|
+
sdd_lib_task_id() {
|
|
29
|
+
local f="$1"
|
|
30
|
+
head -30 "$f" 2>/dev/null \
|
|
31
|
+
| grep -m1 -oE 'Task-ID:\*?\*?\s*TSK-[0-9]+' \
|
|
32
|
+
| grep -oE 'TSK-[0-9]+' || true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Map a tracker-row status cell (`[x]` DONE etc.) to canonical token for ONE Task-ID.
|
|
36
|
+
# Args: <tracker-file> <TSK-NN>. Echoes DONE|TODO|IN_PROGRESS|BLOCKED|UNKNOWN (UNKNOWN if no row).
|
|
37
|
+
sdd_lib_tracker_status() {
|
|
38
|
+
local tr="$1" id="$2" row flag
|
|
39
|
+
# Row form: | [TSK-NN](...) | ... | `[x]` DONE | ... | (also bare TSK-NN)
|
|
40
|
+
row=$(grep -m1 -E "\|[[:space:]]*\[?${id}[]\(]" "$tr" 2>/dev/null || true)
|
|
41
|
+
[[ -z "$row" ]] && { echo "UNKNOWN"; return; }
|
|
42
|
+
flag=$(echo "$row" | sed -nE 's/.*`?\[(.)\]`?[[:space:]]+(DONE|TODO|IN_PROGRESS|BLOCKED).*/\1/p')
|
|
43
|
+
case "$flag" in
|
|
44
|
+
x|X) echo "DONE" ;;
|
|
45
|
+
' ') echo "TODO" ;;
|
|
46
|
+
'~') echo "IN_PROGRESS" ;;
|
|
47
|
+
'!') echo "BLOCKED" ;;
|
|
48
|
+
*) echo "UNKNOWN" ;;
|
|
49
|
+
esac
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Header-trio presence for a source file. Echoes three space-separated flags: <file> <consumers> <tasks>
|
|
53
|
+
# Each flag is 1 (marker present) or 0 (absent). `@tasks: N/A` counts as present.
|
|
54
|
+
sdd_lib_header_flags() {
|
|
55
|
+
local f="$1" hf=0 hc=0 ht=0
|
|
56
|
+
head -20 "$f" 2>/dev/null | grep -qE '@file:' && hf=1
|
|
57
|
+
head -20 "$f" 2>/dev/null | grep -qE '@consumers:' && hc=1
|
|
58
|
+
head -20 "$f" 2>/dev/null | grep -qE '@tasks:' && ht=1
|
|
59
|
+
echo "$hf $hc $ht"
|
|
60
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# @file: Deterministic SDD mechanical checks shared by sdd-check (whole tree) and sdd-audit (scoped).
|
|
3
|
+
# @consumers: sdd-check skill (whole-tree preflight); sdd-audit directive STEP_2_5 (scoped pre-pass).
|
|
4
|
+
# @contract: AX_BASH_NO_SILENT_EMPTY. Single source of mechanical truth — neither skill re-implements
|
|
5
|
+
# header presence, Task-ID integrity, or tracker sync. Pure function of files on disk.
|
|
6
|
+
#
|
|
7
|
+
# Three modes:
|
|
8
|
+
# check.sh [project-root] — whole tree: TASKID + TRACKER_SYNC (all tickets) + HEADERS (all marker-bearing src)
|
|
9
|
+
# check.sh --task <TSK-NN> [root] — one ticket: TASKID (collision/orphan-for-its-refs) + TRACKER_SYNC for that id
|
|
10
|
+
# check.sh --files <f1> [f2 ...] — header-trio presence for an explicit file list (audit passes its git-diff scope)
|
|
11
|
+
#
|
|
12
|
+
# Output sections (TSV, machine-readable, stable):
|
|
13
|
+
# [HEADERS] — file \t has_file \t has_consumers \t has_tasks \t verdict(OK|PARTIAL|NONE)
|
|
14
|
+
# [TASKID] — kind(orphan|collision) \t id \t detail
|
|
15
|
+
# [TRACKER_SYNC] — task_id \t ticket_status \t tracker_status \t match(YES|NO|NO_ROW)
|
|
16
|
+
# [SUMMARY] — key=value totals + findings count
|
|
17
|
+
#
|
|
18
|
+
# Exit codes:
|
|
19
|
+
# 0 — all checks clean (zero findings)
|
|
20
|
+
# 3 — one or more findings (desync / orphan / collision / partial-or-missing header)
|
|
21
|
+
# 2 — structural failure (bad root / not an SDD project)
|
|
22
|
+
# 4 — bad invocation
|
|
23
|
+
|
|
24
|
+
set -uo pipefail
|
|
25
|
+
|
|
26
|
+
PROG="check"
|
|
27
|
+
VERSION="1"
|
|
28
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
29
|
+
# shellcheck source=_sdd-lib.sh
|
|
30
|
+
. "$SCRIPT_DIR/_sdd-lib.sh"
|
|
31
|
+
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
# Argument parsing → MODE
|
|
34
|
+
# ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
MODE="tree"
|
|
37
|
+
TASK_ID=""
|
|
38
|
+
FILES=()
|
|
39
|
+
ROOT="."
|
|
40
|
+
|
|
41
|
+
case "${1:-}" in
|
|
42
|
+
--task)
|
|
43
|
+
MODE="task"
|
|
44
|
+
TASK_ID="${2:-}"
|
|
45
|
+
ROOT="${3:-.}"
|
|
46
|
+
if [[ -z "$TASK_ID" || ! "$TASK_ID" =~ ^TSK-[0-9]+$ ]]; then
|
|
47
|
+
cat <<EOF
|
|
48
|
+
[$PROG] BAD_INVOCATION
|
|
49
|
+
expected: $PROG --task TSK-NN [project-root]
|
|
50
|
+
got: $PROG --task '${TASK_ID:-}' ...
|
|
51
|
+
Required action: pass a Task-ID of the form TSK-<number>.
|
|
52
|
+
EOF
|
|
53
|
+
exit 4
|
|
54
|
+
fi
|
|
55
|
+
;;
|
|
56
|
+
--files)
|
|
57
|
+
MODE="files"
|
|
58
|
+
shift 2>/dev/null || true
|
|
59
|
+
FILES=("$@")
|
|
60
|
+
if [[ ${#FILES[@]} -eq 0 ]]; then
|
|
61
|
+
cat <<EOF
|
|
62
|
+
[$PROG] BAD_INVOCATION
|
|
63
|
+
expected: $PROG --files <file1> [file2 ...]
|
|
64
|
+
got: $PROG --files (no files)
|
|
65
|
+
Required action: pass at least one source file to check headers on.
|
|
66
|
+
EOF
|
|
67
|
+
exit 4
|
|
68
|
+
fi
|
|
69
|
+
;;
|
|
70
|
+
--*)
|
|
71
|
+
cat <<EOF
|
|
72
|
+
[$PROG] BAD_INVOCATION
|
|
73
|
+
unknown flag: $1
|
|
74
|
+
expected: $PROG [project-root] | $PROG --task TSK-NN [root] | $PROG --files <files...>
|
|
75
|
+
EOF
|
|
76
|
+
exit 4
|
|
77
|
+
;;
|
|
78
|
+
*)
|
|
79
|
+
MODE="tree"
|
|
80
|
+
ROOT="${1:-.}"
|
|
81
|
+
;;
|
|
82
|
+
esac
|
|
83
|
+
|
|
84
|
+
FINDINGS=0
|
|
85
|
+
|
|
86
|
+
# ---------------------------------------------------------------------------
|
|
87
|
+
# Mode: --files → HEADERS only
|
|
88
|
+
# ---------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
emit_header_row() {
|
|
91
|
+
local f="$1" flags hf hc ht verdict
|
|
92
|
+
flags=$(sdd_lib_header_flags "$f")
|
|
93
|
+
hf=$(echo "$flags" | cut -d' ' -f1)
|
|
94
|
+
hc=$(echo "$flags" | cut -d' ' -f2)
|
|
95
|
+
ht=$(echo "$flags" | cut -d' ' -f3)
|
|
96
|
+
if [[ "$hf" -eq 1 && "$ht" -eq 1 ]]; then
|
|
97
|
+
verdict="OK" # @consumers is MINOR; @file + @tasks are the required pair
|
|
98
|
+
elif [[ "$hf" -eq 0 && "$hc" -eq 0 && "$ht" -eq 0 ]]; then
|
|
99
|
+
verdict="NONE"
|
|
100
|
+
else
|
|
101
|
+
verdict="PARTIAL"; FINDINGS=$((FINDINGS+1))
|
|
102
|
+
fi
|
|
103
|
+
printf '%s\t%d\t%d\t%d\t%s\n' "$f" "$hf" "$hc" "$ht" "$verdict"
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if [[ "$MODE" == "files" ]]; then
|
|
107
|
+
printf '# sdd check v%s (mode=files)\n' "$VERSION"
|
|
108
|
+
printf '\n[HEADERS]\n# file\thas_file\thas_consumers\thas_tasks\tverdict\n'
|
|
109
|
+
for f in "${FILES[@]}"; do
|
|
110
|
+
if [[ ! -f "$f" ]]; then
|
|
111
|
+
printf '%s\t-\t-\t-\tMISSING_FILE\n' "$f"; FINDINGS=$((FINDINGS+1)); continue
|
|
112
|
+
fi
|
|
113
|
+
emit_header_row "$f"
|
|
114
|
+
done
|
|
115
|
+
printf '\n[SUMMARY]\nmode=files\nfiles_checked=%d\nfindings=%d\n' "${#FILES[@]}" "$FINDINGS"
|
|
116
|
+
[[ "$FINDINGS" -gt 0 ]] && exit 3 || exit 0
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# ---------------------------------------------------------------------------
|
|
120
|
+
# tree / task modes need an SDD root
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
|
|
123
|
+
if [[ ! -d "$ROOT" ]]; then
|
|
124
|
+
echo "[$PROG] BAD_ROOT: $ROOT is not a directory"; exit 2
|
|
125
|
+
fi
|
|
126
|
+
ROOT_ABS="$(cd "$ROOT" && pwd)"
|
|
127
|
+
if [[ ! -d "$ROOT_ABS/tasks" && ! -d "$ROOT_ABS/specs" ]]; then
|
|
128
|
+
cat <<EOF
|
|
129
|
+
[$PROG] NOT_AN_SDD_PROJECT
|
|
130
|
+
root: $ROOT_ABS
|
|
131
|
+
reason: neither tasks/ nor specs/ found
|
|
132
|
+
Required action: run from an SDD project root, or pass it explicitly.
|
|
133
|
+
EOF
|
|
134
|
+
exit 2
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
printf '# sdd check v%s (mode=%s%s)\n' "$VERSION" "$MODE" "$([[ "$MODE" == task ]] && echo " $TASK_ID")"
|
|
138
|
+
printf 'ROOT=%s\n' "$ROOT_ABS"
|
|
139
|
+
|
|
140
|
+
TASK_FILES=$(find -L "$ROOT_ABS/tasks" -name '*.task-*.md' -type f 2>/dev/null | sort || true)
|
|
141
|
+
|
|
142
|
+
# ---------------------------------------------------------------------------
|
|
143
|
+
# [TASKID] — collisions (global) + orphan @tasks references
|
|
144
|
+
# ---------------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
printf '\n[TASKID]\n# kind\tid\tdetail\n'
|
|
147
|
+
|
|
148
|
+
# Build id → files map to detect collisions (two tickets declaring same Task-ID).
|
|
149
|
+
COLLISION_TMP="$(mktemp -t sdd-check-ids.XXXXXX)"
|
|
150
|
+
trap 'rm -f "$COLLISION_TMP"' EXIT
|
|
151
|
+
while IFS= read -r f; do
|
|
152
|
+
[[ -z "$f" ]] && continue
|
|
153
|
+
id=$(sdd_lib_task_id "$f")
|
|
154
|
+
[[ -z "$id" ]] && continue
|
|
155
|
+
printf '%s\t%s\n' "$id" "${f#$ROOT_ABS/}" >> "$COLLISION_TMP"
|
|
156
|
+
done <<< "$TASK_FILES"
|
|
157
|
+
|
|
158
|
+
# Collisions: ids appearing on >1 ticket. In task mode, restrict to TASK_ID.
|
|
159
|
+
while IFS= read -r id; do
|
|
160
|
+
[[ -z "$id" ]] && continue
|
|
161
|
+
[[ "$MODE" == "task" && "$id" != "$TASK_ID" ]] && continue
|
|
162
|
+
files=$(awk -F'\t' -v k="$id" '$1==k {print $2}' "$COLLISION_TMP" | paste -sd',' -)
|
|
163
|
+
printf 'collision\t%s\t%s\n' "$id" "$files"
|
|
164
|
+
FINDINGS=$((FINDINGS+1))
|
|
165
|
+
done < <(cut -f1 "$COLLISION_TMP" | sort | uniq -d)
|
|
166
|
+
|
|
167
|
+
# Orphans: @tasks: TSK-NN in source with no matching ticket file.
|
|
168
|
+
# Whole-tree mode only (task mode trusts its own ticket exists).
|
|
169
|
+
if [[ "$MODE" == "tree" ]]; then
|
|
170
|
+
known_ids=$(cut -f1 "$COLLISION_TMP" | sort -u)
|
|
171
|
+
# Collect @tasks references from source files (exclude heavy dirs).
|
|
172
|
+
refs=$(grep -rhoE '@tasks:[^@]*' "$ROOT_ABS" \
|
|
173
|
+
--include='*.ts' --include='*.js' --include='*.sh' --include='*.go' \
|
|
174
|
+
--exclude-dir=node_modules --exclude-dir=.git --exclude-dir=dist \
|
|
175
|
+
--exclude-dir=worktrees --exclude-dir=.claude 2>/dev/null \
|
|
176
|
+
| grep -oE 'TSK-[0-9]+' | sort -u || true)
|
|
177
|
+
while IFS= read -r rid; do
|
|
178
|
+
[[ -z "$rid" ]] && continue
|
|
179
|
+
if ! echo "$known_ids" | grep -qx "$rid"; then
|
|
180
|
+
printf 'orphan\t%s\t@tasks reference with no tasks/**/*.task-*.md\n' "$rid"
|
|
181
|
+
FINDINGS=$((FINDINGS+1))
|
|
182
|
+
fi
|
|
183
|
+
done <<< "$refs"
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
# ---------------------------------------------------------------------------
|
|
187
|
+
# [TRACKER_SYNC] — ticket Meta.Status vs tracker-row status
|
|
188
|
+
# ---------------------------------------------------------------------------
|
|
189
|
+
|
|
190
|
+
printf '\n[TRACKER_SYNC]\n# task_id\tticket_status\ttracker_status\tmatch\n'
|
|
191
|
+
|
|
192
|
+
sync_one() {
|
|
193
|
+
local f="$1" id ticket_status scope tracker tracker_status match
|
|
194
|
+
id=$(sdd_lib_task_id "$f")
|
|
195
|
+
[[ -z "$id" ]] && return
|
|
196
|
+
ticket_status=$(sdd_lib_status "$f")
|
|
197
|
+
# scope = first path segment under tasks/
|
|
198
|
+
scope=$(echo "${f#$ROOT_ABS/tasks/}" | awk -F/ '{print $1}')
|
|
199
|
+
tracker="$ROOT_ABS/tasks/$scope/README.md"
|
|
200
|
+
if [[ ! -f "$tracker" ]]; then
|
|
201
|
+
printf '%s\t%s\t-\tNO_ROW\n' "$id" "$ticket_status"; FINDINGS=$((FINDINGS+1)); return
|
|
202
|
+
fi
|
|
203
|
+
tracker_status=$(sdd_lib_tracker_status "$tracker" "$id")
|
|
204
|
+
if [[ "$ticket_status" == "UNKNOWN" ]]; then
|
|
205
|
+
# Old-template ticket lacking a parseable Meta **Status:** — cannot compare.
|
|
206
|
+
# Not a desync finding (mirrors scan.sh WARN, not ERROR); surface as UNPARSEABLE.
|
|
207
|
+
match="UNPARSEABLE"
|
|
208
|
+
elif [[ "$tracker_status" == "UNKNOWN" ]]; then
|
|
209
|
+
match="NO_ROW"; FINDINGS=$((FINDINGS+1))
|
|
210
|
+
elif [[ "$tracker_status" == "$ticket_status" ]]; then
|
|
211
|
+
match="YES"
|
|
212
|
+
else
|
|
213
|
+
match="NO"; FINDINGS=$((FINDINGS+1))
|
|
214
|
+
fi
|
|
215
|
+
printf '%s\t%s\t%s\t%s\n' "$id" "$ticket_status" "$tracker_status" "$match"
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
while IFS= read -r f; do
|
|
219
|
+
[[ -z "$f" ]] && continue
|
|
220
|
+
if [[ "$MODE" == "task" ]]; then
|
|
221
|
+
[[ "$(sdd_lib_task_id "$f")" == "$TASK_ID" ]] || continue
|
|
222
|
+
fi
|
|
223
|
+
sync_one "$f"
|
|
224
|
+
done <<< "$TASK_FILES"
|
|
225
|
+
|
|
226
|
+
# [HEADERS] is intentionally NOT run in tree mode: "which files must carry @tasks"
|
|
227
|
+
# is a policy (task-generated vs hand-authored), not a mechanical fact. Header presence
|
|
228
|
+
# is meaningful only against a known in-scope file set — provided by audit via --files.
|
|
229
|
+
|
|
230
|
+
# ---------------------------------------------------------------------------
|
|
231
|
+
# [SUMMARY]
|
|
232
|
+
# ---------------------------------------------------------------------------
|
|
233
|
+
|
|
234
|
+
printf '\n[SUMMARY]\nmode=%s\n' "$MODE"
|
|
235
|
+
[[ "$MODE" == "task" ]] && printf 'task=%s\n' "$TASK_ID"
|
|
236
|
+
printf 'findings=%d\n' "$FINDINGS"
|
|
237
|
+
|
|
238
|
+
[[ "$FINDINGS" -gt 0 ]] && exit 3 || exit 0
|