ll-skills 2.0.2 → 3.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.
- package/CHANGELOG.md +52 -0
- package/README.md +42 -20
- package/agents/ll-executor.md +2 -1
- package/agents/ll-verifier.md +1 -0
- package/assets/preamble.md +27 -39
- package/bin/install.js +4 -1
- package/hooks/ll-precompact.js +29 -1
- package/hooks/ll-skills-check-update.js +6 -6
- package/hooks/ll-state.js +30 -2
- package/package.json +3 -2
- package/scripts/evals/README.md +81 -0
- package/scripts/evals/cases/auto-dry-run/assert.sh +35 -0
- package/scripts/evals/cases/auto-dry-run/case.json +8 -0
- package/scripts/evals/cases/auto-dry-run/prompt.txt +1 -0
- package/scripts/evals/cases/auto-empty-repo/assert.sh +25 -0
- package/scripts/evals/cases/auto-empty-repo/case.json +8 -0
- package/scripts/evals/cases/auto-empty-repo/fixture/.gitkeep +0 -0
- package/scripts/evals/cases/auto-empty-repo/prompt.txt +1 -0
- package/scripts/evals/cases/decide-final-round/assert.sh +45 -0
- package/scripts/evals/cases/decide-final-round/case.json +8 -0
- package/scripts/evals/cases/decide-final-round/fixture/README.md +3 -0
- package/scripts/evals/cases/decide-final-round/prompt.txt +1 -0
- package/scripts/evals/cases/executor-block/assert.sh +33 -0
- package/scripts/evals/cases/executor-block/case.json +8 -0
- package/scripts/evals/cases/executor-block/prompt.txt +14 -0
- package/scripts/evals/cases/goal-autonomous/assert.sh +35 -0
- package/scripts/evals/cases/goal-autonomous/case.json +8 -0
- package/scripts/evals/cases/goal-autonomous/fixture/PLAN.md +42 -0
- package/scripts/evals/cases/goal-autonomous/fixture/PROGRESS.md +20 -0
- package/scripts/evals/cases/goal-autonomous/fixture/ROADMAP.md +29 -0
- package/scripts/evals/cases/goal-autonomous/fixture/package.json +8 -0
- package/scripts/evals/cases/goal-autonomous/fixture/src/money.js +6 -0
- package/scripts/evals/cases/goal-autonomous/fixture/test/reconcile.test.js +8 -0
- package/scripts/evals/cases/goal-autonomous/prompt.txt +1 -0
- package/scripts/evals/cases/implement-review-gate/assert.sh +37 -0
- package/scripts/evals/cases/implement-review-gate/case.json +8 -0
- package/scripts/evals/cases/implement-review-gate/prompt.txt +1 -0
- package/scripts/evals/cases/implement-stops-at-next/assert.sh +121 -0
- package/scripts/evals/cases/implement-stops-at-next/case.json +9 -0
- package/scripts/evals/cases/implement-stops-at-next/prompt.txt +1 -0
- package/scripts/evals/cases/preamble-no-ritual/assert.sh +17 -0
- package/scripts/evals/cases/preamble-no-ritual/case.json +8 -0
- package/scripts/evals/cases/preamble-no-ritual/fixture/README.md +3 -0
- package/scripts/evals/cases/preamble-no-ritual/fixture/src/a.ts +3 -0
- package/scripts/evals/cases/preamble-no-ritual/prompt.txt +1 -0
- package/scripts/evals/cases/router-no-skill/assert.sh +33 -0
- package/scripts/evals/cases/router-no-skill/case.json +8 -0
- package/scripts/evals/cases/router-no-skill/fixture/PLAN.md +21 -0
- package/scripts/evals/cases/router-no-skill/fixture/README.md +7 -0
- package/scripts/evals/cases/router-no-skill/fixture/decisions/README.md +3 -0
- package/scripts/evals/cases/router-no-skill/prompt.txt +1 -0
- package/scripts/evals/cases/router-small/assert.sh +21 -0
- package/scripts/evals/cases/router-small/case.json +8 -0
- package/scripts/evals/cases/router-small/fixture/README.md +17 -0
- package/scripts/evals/cases/router-small/prompt.txt +1 -0
- package/scripts/evals/cases/scout-no-plan/assert.sh +41 -0
- package/scripts/evals/cases/scout-no-plan/case.json +8 -0
- package/scripts/evals/cases/scout-no-plan/prompt.txt +8 -0
- package/scripts/evals/cases/verifier-weakened-test/assert.sh +19 -0
- package/scripts/evals/cases/verifier-weakened-test/case.json +8 -0
- package/scripts/evals/cases/verifier-weakened-test/prompt.txt +13 -0
- package/scripts/evals/cases/verifier-weakened-test/setup.sh +19 -0
- package/scripts/evals/fixtures/manual-contract/out.json +29 -0
- package/scripts/evals/fixtures/manual-contract/out.txt +5 -0
- package/scripts/evals/fixtures/manual-contract/with-skill.json +46 -0
- package/scripts/evals/fixtures/router-no-skill/fail.txt +5 -0
- package/scripts/evals/fixtures/router-no-skill/out.json +27 -0
- package/scripts/evals/fixtures/router-no-skill/pass.txt +4 -0
- package/scripts/evals/lib/assert.sh +107 -0
- package/scripts/evals/lib/extract.js +73 -0
- package/scripts/evals/run.sh +369 -0
- package/scripts/fixtures/auto-closed/PLAN.md +5 -0
- package/scripts/fixtures/auto-closed/PROGRESS.md +20 -0
- package/scripts/fixtures/auto-closed/ROADMAP.md +6 -0
- package/scripts/fixtures/auto-closed/docs/DELIVERY.md +3 -0
- package/scripts/fixtures/auto-decisions/decisions/DEC-0001-taken-alone.md +13 -0
- package/scripts/fixtures/auto-decisions/decisions/DEC-0002-owner.md +13 -0
- package/scripts/fixtures/auto-noroadmap/PLAN.md +20 -0
- package/scripts/fixtures/auto-noroadmap/PROGRESS.md +11 -0
- package/scripts/fixtures/auto-verify-next/PLAN.md +5 -0
- package/scripts/fixtures/auto-verify-next/PROGRESS.md +18 -0
- package/scripts/fixtures/auto-verify-next/ROADMAP.md +5 -0
- package/scripts/fixtures/auto-verify-next/phases/01/PLAN.md +6 -0
- package/scripts/fixtures/evals-auto/auto-dry-run/pass.txt +18 -0
- package/scripts/fixtures/evals-auto/auto-empty-repo/pass.txt +2 -0
- package/scripts/fixtures/evals-auto/goal-autonomous/pass.txt +29 -0
- package/scripts/fixtures/lint-bad/folded-description/SKILL.md +13 -0
- package/scripts/fixtures/lint-bad/jargon-in-questions/SKILL.md +36 -0
- package/scripts/fixtures/lint-bad/model-invocation-false/SKILL.md +10 -0
- package/scripts/fixtures/next-bad/skills/ll-bad/SKILL.md +30 -0
- package/scripts/fixtures/next-good/skills/ll-good/SKILL.md +26 -0
- package/scripts/fixtures/project/BACKLOG.md +6 -5
- package/scripts/fixtures/project/PROGRESS.md +4 -0
- package/scripts/lint-contract.cjs +495 -0
- package/scripts/lint-prompts.sh +443 -0
- package/scripts/ll-tools.js +497 -450
- package/scripts/smoke-test.sh +481 -4
- package/skills/ll-auto/SKILL.md +74 -0
- package/skills/ll-auto/references/run.md +75 -0
- package/skills/ll-auto/references/stages.md +66 -0
- package/skills/ll-auto/scripts/ll-auto.js +345 -0
- package/skills/ll-brainstorm/SKILL.md +14 -12
- package/skills/ll-brainstorm/references/decision-policy.md +22 -12
- package/skills/ll-close/SKILL.md +8 -7
- package/skills/ll-close/references/delivery.md +3 -1
- package/skills/ll-decide/SKILL.md +21 -19
- package/skills/ll-decide/references/decision-policy.md +22 -12
- package/skills/ll-decide/references/decision-room.md +5 -3
- package/skills/ll-decide/references/interview.md +29 -13
- package/skills/ll-decide/references/plan-skeleton.md +14 -14
- package/skills/ll-decide/references/premise-gate.md +34 -20
- package/skills/ll-goal/SKILL.md +22 -4
- package/skills/ll-goal/references/goal-template.md +57 -0
- package/skills/ll-implement/SKILL.md +25 -22
- package/skills/ll-implement/references/briefs.md +2 -1
- package/skills/ll-implement/references/decision-policy.md +22 -12
- package/skills/ll-implement/references/phase-conversation.md +20 -12
- package/skills/ll-implement/references/phase-plan.md +23 -0
- package/skills/ll-oncall/SKILL.md +3 -2
- package/skills/ll-refine/SKILL.md +3 -2
- package/skills/ll-research/SKILL.md +3 -2
- package/skills/ll-resume/SKILL.md +9 -4
- package/skills/ll-update/SKILL.md +6 -1
- package/skills/ll-verify/SKILL.md +2 -1
- package/skills/ll-verify/references/verifier-briefs.md +3 -0
package/scripts/smoke-test.sh
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Smoke test do ll-skills: gate estático, hooks, helper, instalador, preâmbulo,
|
|
3
3
|
# poda de skills antigas e uninstall. Tudo num CLAUDE_CONFIG_DIR isolado.
|
|
4
|
+
# Uso: smoke-test.sh [--only <seção>] (sem argumento roda tudo)
|
|
5
|
+
# As seções são os blocos numerados (1, 2, 3, 4, 4b, 4c, 4d, 5..10), lint-orquestrador,
|
|
6
|
+
# goal-autonomo, evals-auto, lint-scratch e no-talk; os blocos numerados montam estado uns para
|
|
7
|
+
# os outros, então --only puxa o pré-requisito de quem não roda sozinho (ver prereq abaixo).
|
|
4
8
|
set -euo pipefail
|
|
5
9
|
cd "$(dirname "$0")/.."
|
|
6
10
|
ROOT="$PWD"
|
|
7
11
|
|
|
12
|
+
ONLY=""
|
|
13
|
+
while [ $# -gt 0 ]; do
|
|
14
|
+
case "$1" in
|
|
15
|
+
--only) ONLY="${2:-}"; shift 2 ;;
|
|
16
|
+
*) echo "uso: smoke-test.sh [--only <seção>]" >&2; exit 2 ;;
|
|
17
|
+
esac
|
|
18
|
+
done
|
|
19
|
+
# Pré-requisitos: a seção 4 usa o $EMPTY que a 2 monta e o $FIX que a 3 monta, a 6 lê o
|
|
20
|
+
# CLAUDE_CONFIG_DIR e o log que a 5 monta, e a 8 desinstala o que a 5 instalou e o preâmbulo que a 6
|
|
21
|
+
# escreve. --only puxa essas seções antes da pedida.
|
|
22
|
+
prereq() { case "$1" in 4) echo "2 3" ;; 6) echo "5" ;; 8) echo "5 6" ;; *) echo "" ;; esac; }
|
|
23
|
+
RUN=""
|
|
24
|
+
[ -z "$ONLY" ] || RUN=" $(prereq "$ONLY") $ONLY "
|
|
25
|
+
section() { [ -z "$ONLY" ] || case "$RUN" in *" $1 "*) return 0 ;; *) return 1 ;; esac; }
|
|
26
|
+
|
|
8
27
|
TMP="$(mktemp -d)"
|
|
9
28
|
trap 'rm -rf "$TMP"' EXIT
|
|
10
29
|
|
|
@@ -12,15 +31,17 @@ N=0
|
|
|
12
31
|
check() { N=$((N + 1)); if ! eval "$2"; then echo "FALHOU: $1"; exit 1; fi; }
|
|
13
32
|
|
|
14
33
|
HELPER="node $ROOT/scripts/ll-tools.js"
|
|
34
|
+
AUTO="node $ROOT/skills/ll-auto/scripts/ll-auto.js"
|
|
15
35
|
region() { awk '/^\/\/ <ll-shared:state>$/,/^\/\/ <\/ll-shared:state>$/' "$1"; }
|
|
16
36
|
|
|
17
37
|
# ---------------------------------------------------------------------------
|
|
18
38
|
# 1. gate estático
|
|
19
39
|
# ---------------------------------------------------------------------------
|
|
40
|
+
if section 1; then
|
|
20
41
|
for f in bin/*.js hooks/*.js scripts/*.js; do node --check "$f"; done
|
|
21
42
|
|
|
22
|
-
check "helper ≤
|
|
23
|
-
check "helper ≤
|
|
43
|
+
check "helper ≤760 linhas" '[ "$(wc -l < scripts/ll-tools.js)" -le 760 ]'
|
|
44
|
+
check "helper ≤36000 bytes" '[ "$(wc -c < scripts/ll-tools.js)" -le 36000 ]'
|
|
24
45
|
|
|
25
46
|
npm pack --dry-run --json > "$TMP/pack.json" 2>/dev/null
|
|
26
47
|
check "tarball inclui o helper" 'grep -q "scripts/ll-tools.js" "$TMP/pack.json"'
|
|
@@ -30,10 +51,12 @@ region scripts/ll-tools.js > "$TMP/shared-tools.txt"
|
|
|
30
51
|
region hooks/ll-state.js > "$TMP/shared-hook.txt"
|
|
31
52
|
check "região ll-shared:state existe" '[ -s "$TMP/shared-tools.txt" ] && [ -s "$TMP/shared-hook.txt" ]'
|
|
32
53
|
check "região ll-shared:state idêntica" 'cmp -s "$TMP/shared-tools.txt" "$TMP/shared-hook.txt"'
|
|
54
|
+
fi
|
|
33
55
|
|
|
34
56
|
# ---------------------------------------------------------------------------
|
|
35
57
|
# 2. hooks silenciosos fora de um projeto
|
|
36
58
|
# ---------------------------------------------------------------------------
|
|
59
|
+
if section 2; then
|
|
37
60
|
EMPTY="$TMP/empty"
|
|
38
61
|
mkdir -p "$EMPTY"
|
|
39
62
|
cp -R scripts/fixtures/empty/. "$EMPTY/"
|
|
@@ -56,10 +79,12 @@ for h in ll-state.js ll-precompact.js; do
|
|
|
56
79
|
check "$h <300 ms no vazio" '[ "$(cat "$TMP/hook.ms")" -lt 300 ]'
|
|
57
80
|
done
|
|
58
81
|
check "hooks não criaram arquivos no vazio" '[ "$(find "$EMPTY" | wc -l)" -eq "$BEFORE_FILES" ]'
|
|
82
|
+
fi
|
|
59
83
|
|
|
60
84
|
# ---------------------------------------------------------------------------
|
|
61
85
|
# 3. hooks no fixture
|
|
62
86
|
# ---------------------------------------------------------------------------
|
|
87
|
+
if section 3; then
|
|
63
88
|
FIX="$TMP/fix"
|
|
64
89
|
bash scripts/fixtures/git-history.sh "$FIX" > /dev/null
|
|
65
90
|
|
|
@@ -88,10 +113,12 @@ check "precompact avisa a sessão" 'grep -q "compaction marked in PROGRESS.md
|
|
|
88
113
|
check "precompact inseriu 1 linha" '[ "$(grep -c "^- \[compaction" "$FIX/PROGRESS.md")" -eq "$((C0 + 1))" ]'
|
|
89
114
|
check "linha nova fica acima do epílogo" \
|
|
90
115
|
'grep -n "^## Epilogue" "$FIX/PROGRESS.md" | head -1 | cut -d: -f1 | xargs -I{} sh -c "sed -n \"\$(({} - 2))p\" \"$FIX/PROGRESS.md\"" | grep -q "^- \[compaction .* manual "'
|
|
116
|
+
fi
|
|
91
117
|
|
|
92
118
|
# ---------------------------------------------------------------------------
|
|
93
|
-
# 4. os
|
|
119
|
+
# 4. os 13 comandos do helper
|
|
94
120
|
# ---------------------------------------------------------------------------
|
|
121
|
+
if section 4; then
|
|
95
122
|
cd "$FIX"
|
|
96
123
|
$HELPER waves phases/07/PLAN.md --json > "$TMP/waves.json"
|
|
97
124
|
check "waves: 4 ondas" 'grep -q "\"wave\":4" "$TMP/waves.json"'
|
|
@@ -103,14 +130,24 @@ $HELPER tdd-gate M1 --json > "$TMP/tdd1.json"
|
|
|
103
130
|
check "tdd-gate M1 passa" 'grep -q "\"tdd\":\"pass\"" "$TMP/tdd1.json"'
|
|
104
131
|
check "tdd-gate M1 ignora M10" '! grep -q "M10" "$TMP/tdd1.json"'
|
|
105
132
|
check "tdd-gate M2 reprova" '$HELPER tdd-gate M2 --json | grep -q "\"tdd\":\"fail\""'
|
|
133
|
+
check "tdd-gate acha o id entre posicionais" '$HELPER tdd-gate phases/07/PLAN.md M1 --json | grep -q "\"tdd\":\"pass\""'
|
|
106
134
|
|
|
107
135
|
check "spot-check M1 passa" '$HELPER spot-check M1 --files src/a.ts,test/a.test.ts --json | grep -q "\"verdict\":\"pass\""'
|
|
108
136
|
check "state: fase 07, 1/3" '$HELPER state --json | grep -q "\"total\":3,\"passed\":1"'
|
|
109
137
|
check "ledger: 1 FRESH 1 STALE 1 UNKNOWN" '$HELPER ledger --json | grep -q "\"FRESH\":1,\"STALE\":1,\"UNKNOWN\":1"'
|
|
110
138
|
check "epilogue 07 aponta a fase 8" '$HELPER epilogue 07 --json | grep -q "\"next_command\":\"ll-implement 8\""'
|
|
111
139
|
check "phase-stats: 5 dias com trabalho" '$HELPER phase-stats --json | grep -q "\"days_with_work\":5"'
|
|
140
|
+
check "phase-stats: fase 07 com questions/owner_prompts" \
|
|
141
|
+
'$HELPER phase-stats --json | grep -q "\"phase\":\"07\"" && $HELPER phase-stats --json | grep -q "\"questions\":3" && $HELPER phase-stats --json | grep -q "\"owner_prompts\":2"'
|
|
142
|
+
check "phase-stats: targets presente" '$HELPER phase-stats --json | grep -q "\"targets\""'
|
|
143
|
+
mkdir -p "$TMP/ep" && awk '/^## Epilogue — phase 07/{print "## Epilogue — phase 06 — 2026-09-08"; print "passed: M1 · left: none"; print ""} {print}' PROGRESS.md | sed 's/· amendments 1 · verification:/· amendments 1 (M3) · verification:/' > "$TMP/ep/PROGRESS.md"
|
|
144
|
+
$HELPER phase-stats --cwd "$TMP/ep" --json > "$TMP/ep.json"
|
|
145
|
+
check "phase-stats: epílogo sem linha de contagem não rouba a da fase seguinte" 'grep -q "\"phase\":\"06\",\"count_line\":false" "$TMP/ep.json"'
|
|
146
|
+
check "phase-stats: parêntese após amendments é aceito" 'grep -q "\"amendments\":1,\"verdict\":\"APPROVED\",\"verification\":\"phases/07/VERIFICATION.md\"" "$TMP/ep.json"'
|
|
147
|
+
check "epilogue 07 humano traz targets" '$HELPER epilogue 07 | grep -q "targets:"'
|
|
112
148
|
check "plan-lint reprova o PLAN" '$HELPER plan-lint phases/07/PLAN.md --json | grep -q "\"verdict\":\"fail\""'
|
|
113
149
|
check "plan-lint acusa acceptance-missing em M6" '$HELPER plan-lint phases/07/PLAN.md --json | grep -q "acceptance-missing"'
|
|
150
|
+
check "plan-lint aceita o número da fase" '$HELPER plan-lint 7 --json | grep -q "\"verdict\":\"fail\"" && $HELPER waves 07 --json | grep -q "\"waves\""'
|
|
114
151
|
|
|
115
152
|
cp PROGRESS.md "$TMP/progress-before"
|
|
116
153
|
$HELPER passes M2 true --json > "$TMP/passes.json"
|
|
@@ -118,6 +155,9 @@ check "passes M2 true grava" 'grep -q "\"passes\":true" "$TMP/passes.js
|
|
|
118
155
|
check "passes muda exatamente 2 linhas" \
|
|
119
156
|
'[ "$(diff "$TMP/progress-before" PROGRESS.md | grep -c "^[<>]")" -eq 2 ]'
|
|
120
157
|
check "passes M9 cria a linha" '$HELPER passes M9 true --json | grep -q "\"created\":true"'
|
|
158
|
+
mkdir -p "$TMP/emptymap" && sed 's/^milestones:$/milestones: {}/' "$TMP/progress-before" | awk '/^milestones: \{\}$/{print; skip=1; next} skip && /^ (M[0-9]+|G-[0-9]+):/{next} {skip=0; print}' > "$TMP/emptymap/PROGRESS.md"
|
|
159
|
+
check "passes com milestones: {} cria a entrada" \
|
|
160
|
+
'$HELPER passes M1 true --cwd "$TMP/emptymap" --json | grep -q "\"created\":true" && grep -q "^milestones:$" "$TMP/emptymap/PROGRESS.md" && grep -q "^ M1: {" "$TMP/emptymap/PROGRESS.md"'
|
|
121
161
|
|
|
122
162
|
check "dec-reserve 2 → 0042/0043" '$HELPER dec-reserve 2 --json | grep -q "\"DEC-0042\",\"DEC-0043\""'
|
|
123
163
|
check "dec-reserve 2 → 0044/0045" '$HELPER dec-reserve 2 --json | grep -q "\"DEC-0044\",\"DEC-0045\""'
|
|
@@ -130,6 +170,20 @@ touch "$FIXDECF/decisions/DEC-X-001-a.md" "$FIXDECF/decisions/DEC-X-002-b.md"
|
|
|
130
170
|
check "dec-reserve sem --prefix detecta DEC-X → 003" 'grep -q "\"ids\":\[\"DEC-X-003\"\]" "$TMP/dec-x.json"'
|
|
131
171
|
check "dec-reserve sem --prefix retorna prefix DEC-X" 'grep -q "\"prefix\":\"DEC-X\"" "$TMP/dec-x.json"'
|
|
132
172
|
|
|
173
|
+
BSW="$TMP/bsw"
|
|
174
|
+
cp -R "$FIX" "$BSW"
|
|
175
|
+
$HELPER board-switch 08 --milestones M1,M2 --cwd "$BSW" --json > "$TMP/bsw.json"
|
|
176
|
+
check "board-switch 08 troca a fase do placar" \
|
|
177
|
+
'grep -q "\"phase\":\"08\"" "$TMP/bsw.json" && grep -q "\"from\":\"07\"" "$TMP/bsw.json" && grep -q "^phase: 08$" "$BSW/PROGRESS.md"'
|
|
178
|
+
check "board-switch semeia 2 marcos e apaga os antigos" \
|
|
179
|
+
'[ "$(grep -c "^ \(M[0-9]*\|G-[0-9]*\): {" "$BSW/PROGRESS.md")" -eq 2 ] && grep -q "^ M2: { passes: false" "$BSW/PROGRESS.md" && ! grep -q "a1b2c3d" "$BSW/PROGRESS.md"'
|
|
180
|
+
check "board-switch na mesma fase só semeia o que falta" \
|
|
181
|
+
'$HELPER board-switch 08 --milestones M1,M2,M3 --cwd "$BSW" --json | grep -q "\"seeded\":\[\"M3\"\]" && [ "$(grep -c "^ M[0-9]*: {" "$BSW/PROGRESS.md")" -eq 3 ]'
|
|
182
|
+
check "passes --phase 07 recusa o placar da 08" \
|
|
183
|
+
'$HELPER passes M1 true --commit abc1234 --phase 07 --cwd "$BSW" > "$TMP/pw.out" 2>/dev/null; [ $? -eq 1 ] && grep -q "\"ok\":false" "$TMP/pw.out" && grep -q "board is phase 08, not 07" "$TMP/pw.out"'
|
|
184
|
+
check "passes --phase 08 grava no placar da 08" \
|
|
185
|
+
'$HELPER passes M1 true --commit abc1234 --phase 08 --cwd "$BSW" --json | grep -q "\"passes\":true"'
|
|
186
|
+
|
|
133
187
|
check "heartbeat escreve no PROGRESS" '$HELPER heartbeat "smoke test ran" --json | grep -q "\"ok\":true"'
|
|
134
188
|
check "heartbeat acima do epílogo" 'grep -q "smoke test ran" PROGRESS.md'
|
|
135
189
|
|
|
@@ -137,6 +191,15 @@ $HELPER backlog-reconcile --run --json > "$TMP/backlog.json"
|
|
|
137
191
|
check "backlog-reconcile fecha só B-014" 'grep -q "\"closed\":\[\"B-014\"\]" "$TMP/backlog.json"'
|
|
138
192
|
check "B-014 virou CLOSED no arquivo" 'grep "B-014" BACKLOG.md | grep -q "CLOSED"'
|
|
139
193
|
check "B-015 continua OPEN" 'grep "B-015" BACKLOG.md | grep -q "OPEN"'
|
|
194
|
+
check "backlog-reconcile acusa B-017 unparsable" \
|
|
195
|
+
'$HELPER backlog-reconcile --json > "$TMP/bk017.json"; grep -q "\"id\":\"B-017\",\"state\":\"OPEN\"" "$TMP/bk017.json" && grep -q "\"unparsable-condition\"" "$TMP/bk017.json"'
|
|
196
|
+
check "epilogue 07 humano lista o unparsable" '$HELPER epilogue 07 | grep -q "unparsable: B-017"'
|
|
197
|
+
check "epilogue 07 json traz unparsable" '$HELPER epilogue 07 --json | grep -q "\"unparsable\":\[\"B-017\"\]"'
|
|
198
|
+
check "phase-stats --since do dia do último commit é inclusivo" \
|
|
199
|
+
'$HELPER phase-stats --since 2026-09-09 --json | grep -q "\"commits\":1"'
|
|
200
|
+
mkdir -p "$TMP/oldw" && sed "s/owner decisions open/band-1 open/" PROGRESS.md > "$TMP/oldw/PROGRESS.md"
|
|
201
|
+
check "phase-stats aceita a redação antiga da linha de contagem" \
|
|
202
|
+
'$HELPER phase-stats --cwd "$TMP/oldw" --json | grep -q "\"band1_open\":0"'
|
|
140
203
|
|
|
141
204
|
# comandos de leitura fora de um projeto: ok:false e exit 0
|
|
142
205
|
cd "$EMPTY"
|
|
@@ -145,10 +208,12 @@ for cmd in waves tdd-gate spot-check state ledger backlog-reconcile epilogue pha
|
|
|
145
208
|
"$HELPER $cmd M1 > \"$TMP/r.out\" 2>/dev/null; [ \$? -eq 0 ] && grep -q '\"ok\":false' \"$TMP/r.out\""
|
|
146
209
|
done
|
|
147
210
|
cd "$ROOT"
|
|
211
|
+
fi
|
|
148
212
|
|
|
149
213
|
# ---------------------------------------------------------------------------
|
|
150
214
|
# 4b. nested state root (PROGRESS.md/phases/ under docs/state/, sources at git top)
|
|
151
215
|
# ---------------------------------------------------------------------------
|
|
216
|
+
if section 4b; then
|
|
152
217
|
NEST="$TMP/nest"
|
|
153
218
|
bash scripts/fixtures/git-history.sh "$NEST" > /dev/null
|
|
154
219
|
mkdir -p "$NEST/docs/state"
|
|
@@ -166,6 +231,19 @@ check "nested state: git_top aponta ao topo do git" \
|
|
|
166
231
|
check "nested ledger: FRESH/STALE/UNKNOWN iguais ao topo" \
|
|
167
232
|
'$HELPER ledger --cwd "$NEST/docs/state" --json | grep -q "\"FRESH\":1,\"STALE\":1,\"UNKNOWN\":1"'
|
|
168
233
|
|
|
234
|
+
printf '{"cwd":"%s","source":"compact"}' "$NEST" | node hooks/ll-state.js > "$TMP/nest-hook.json"
|
|
235
|
+
check "nested hook ll-state acha o PROGRESS em docs/state" 'grep -q "post-compaction" "$TMP/nest-hook.json" && grep -q "milestones [0-9]/3" "$TMP/nest-hook.json"'
|
|
236
|
+
printf '{"cwd":"%s","trigger":"auto"}' "$NEST" | node hooks/ll-precompact.js > "$TMP/nest-pre.out"
|
|
237
|
+
check "nested hook precompact carimba docs/state/PROGRESS.md" 'grep -q "compaction marked" "$TMP/nest-pre.out" && grep -q "^- \[compaction " "$NEST/docs/state/PROGRESS.md"'
|
|
238
|
+
mkdir -p "$NEST/docs/other" && printf '# other progress\n\nno state block here\n' > "$NEST/docs/other/PROGRESS.md" && touch "$NEST/docs/other/PROGRESS.md"
|
|
239
|
+
printf '{"cwd":"%s","source":"compact"}' "$NEST" | node hooks/ll-state.js > "$TMP/nest-hook2.json"
|
|
240
|
+
check "nested hook: entre vários PROGRESS.md escolhe o que tem bloco ll-state" 'grep -q "milestones [0-9]/3" "$TMP/nest-hook2.json"'
|
|
241
|
+
rm -rf "$NEST/docs/other"
|
|
242
|
+
mkdir -p "$NEST/docs/fixtures/project" && cp "$NEST/docs/state/PROGRESS.md" "$NEST/docs/fixtures/project/PROGRESS.md" && mv "$NEST/docs/state" "$TMP/state-aside"
|
|
243
|
+
check "hook ignora PROGRESS.md dentro de fixtures/" '[ -z "$(printf "{\"cwd\":\"%s\",\"source\":\"startup\"}" "$NEST" | node hooks/ll-state.js)" ]'
|
|
244
|
+
mv "$TMP/state-aside" "$NEST/docs/state"
|
|
245
|
+
rm -rf "$NEST/docs/fixtures"
|
|
246
|
+
|
|
169
247
|
$HELPER backlog-reconcile --run --cwd "$NEST/docs/state" --json > "$TMP/nest-backlog.json"
|
|
170
248
|
check "nested backlog-reconcile fecha B-014 (comando roda no topo do git)" \
|
|
171
249
|
'grep -q "\"closed\":\[\"B-014\"\]" "$TMP/nest-backlog.json"'
|
|
@@ -181,10 +259,96 @@ check "state do topo reporta git_top correto" \
|
|
|
181
259
|
$HELPER spot-check M1 --files src/a.ts,test/a.test.ts --cwd "$NEST" --json > "$TMP/nest-top-spot.json"
|
|
182
260
|
check "spot-check do topo acha a raiz deslocada" \
|
|
183
261
|
'grep -q "\"verdict\":\"pass\"" "$TMP/nest-top-spot.json"'
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
# ---------------------------------------------------------------------------
|
|
265
|
+
# 4c. helper ll-auto: detect
|
|
266
|
+
# ---------------------------------------------------------------------------
|
|
267
|
+
if section 4c; then
|
|
268
|
+
# ids e status exatos de um detect --json: renomear um status ou perder um estágio reprova
|
|
269
|
+
auto_ids() { node -e 'const o=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));console.log(o.stages.map((s)=>s.id).join(","))' "$1"; }
|
|
270
|
+
auto_status_set() { node -e 'const o=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));console.log([...new Set(o.stages.map((s)=>s.status))].sort().join(","))' "$1"; }
|
|
271
|
+
check "ll-auto detect: fixture project → decide done, 07 half" \
|
|
272
|
+
'$AUTO detect --cwd "$ROOT/scripts/fixtures/project" --json > "$TMP/auto-project.json" \
|
|
273
|
+
&& grep -q "\"id\":\"decide\",\"status\":\"done\"" "$TMP/auto-project.json" \
|
|
274
|
+
&& grep -q "\"id\":\"phase-07\",\"status\":\"half\"" "$TMP/auto-project.json"'
|
|
275
|
+
check "ll-auto detect: fixture empty → os 4 estágios exatos, todos todo" \
|
|
276
|
+
'$AUTO detect --cwd "$ROOT/scripts/fixtures/empty" --json > "$TMP/auto-empty.json" \
|
|
277
|
+
&& [ "$(auto_ids "$TMP/auto-empty.json")" = "research,brainstorm,decide,close" ] \
|
|
278
|
+
&& [ "$(auto_status_set "$TMP/auto-empty.json")" = "todo" ]'
|
|
279
|
+
check "ll-auto detect: sem ROADMAP.md as fases saem da tabela do §8 do PLAN" \
|
|
280
|
+
'$AUTO detect --cwd "$ROOT/scripts/fixtures/auto-noroadmap" --json > "$TMP/auto-noroadmap.json" \
|
|
281
|
+
&& [ "$(auto_ids "$TMP/auto-noroadmap.json")" = "research,brainstorm,decide,phase-01,phase-02,verify-01,verify-02,close" ] \
|
|
282
|
+
&& grep -q "\"id\":\"phase-01\",\"status\":\"done\"" "$TMP/auto-noroadmap.json" \
|
|
283
|
+
&& grep -q "\"id\":\"phase-02\",\"status\":\"todo\"" "$TMP/auto-noroadmap.json"'
|
|
284
|
+
check "ll-auto detect: DELIVERY.md + epílogo da última fase → close done" \
|
|
285
|
+
'$AUTO detect --cwd "$ROOT/scripts/fixtures/auto-closed" --json > "$TMP/auto-closed.json" \
|
|
286
|
+
&& grep -q "\"id\":\"close\",\"status\":\"done\",\"evidence\":\"docs/DELIVERY.md + epilogue for phase 02\"" "$TMP/auto-closed.json" \
|
|
287
|
+
&& grep -q "\"id\":\"phase-02\",\"status\":\"done\"" "$TMP/auto-closed.json" \
|
|
288
|
+
&& [ "$(auto_status_set "$TMP/auto-closed.json")" = "done,todo" ]'
|
|
289
|
+
check "ll-auto detect: diretório inexistente → ok:false e exit 0" \
|
|
290
|
+
'$AUTO detect --cwd /nonexistent --json > "$TMP/auto-nodir.json" 2>/dev/null; [ $? -eq 0 ] \
|
|
291
|
+
&& grep -q "\"ok\":false" "$TMP/auto-nodir.json" && grep -q "not a directory" "$TMP/auto-nodir.json"'
|
|
292
|
+
check "ll-auto detect --json é JSON válido" \
|
|
293
|
+
'$AUTO detect --cwd "$ROOT/scripts/fixtures/project" --json \
|
|
294
|
+
| node -e "JSON.parse(require(\"fs\").readFileSync(0,\"utf8\"))"'
|
|
295
|
+
fi
|
|
296
|
+
|
|
297
|
+
# ---------------------------------------------------------------------------
|
|
298
|
+
# 4d. helper ll-auto: roteiro, next-cmd, report, auto-md
|
|
299
|
+
# ---------------------------------------------------------------------------
|
|
300
|
+
if section 4d; then
|
|
301
|
+
check "ll-auto roteiro: --verify all → 07, verify-07, 08, verify-08, close" \
|
|
302
|
+
'$AUTO roteiro --cwd "$ROOT/scripts/fixtures/project" --flags "--verify all" --json > "$TMP/auto-verify-all.json" \
|
|
303
|
+
&& grep -q "\"stage\":\"phase-07\".*\"stage\":\"verify-07\".*\"stage\":\"phase-08\".*\"stage\":\"verify-08\".*\"stage\":\"close\"" "$TMP/auto-verify-all.json" \
|
|
304
|
+
&& ! grep -qE "\"stage\":\"(research|brainstorm|decide|phase-05|phase-06)\"" "$TMP/auto-verify-all.json"'
|
|
305
|
+
check "ll-auto roteiro: --only 8 corta o close e as outras fases" \
|
|
306
|
+
'$AUTO roteiro --cwd "$ROOT/scripts/fixtures/project" --flags "--only 8" --json > "$TMP/auto-only8.json" \
|
|
307
|
+
&& grep -q "\"stage\":\"phase-08\",\"command\":\"ll-implement 08 --no-talk\"" "$TMP/auto-only8.json" \
|
|
308
|
+
&& ! grep -qE "\"stage\":\"(phase-07|close)\"" "$TMP/auto-only8.json"'
|
|
309
|
+
check "ll-auto roteiro: repo vazio sem objetivo pede o objetivo" \
|
|
310
|
+
'$AUTO roteiro --cwd "$ROOT/scripts/fixtures/empty" --flags "" --json > "$TMP/auto-vazio.json" \
|
|
311
|
+
&& grep -q "\"needs_objective\":true" "$TMP/auto-vazio.json" \
|
|
312
|
+
&& grep -q "\"roteiro\":\[\]" "$TMP/auto-vazio.json"'
|
|
313
|
+
check "ll-auto roteiro: repo vazio com objetivo → research, brainstorm, decide, close" \
|
|
314
|
+
'$AUTO roteiro --cwd "$ROOT/scripts/fixtures/empty" --objective "um objetivo" \
|
|
315
|
+
--flags "--research --brainstorm" --json > "$TMP/auto-vazio2.json" \
|
|
316
|
+
&& grep -q "\"stage\":\"research\".*\"stage\":\"brainstorm\".*\"stage\":\"decide\".*\"stage\":\"close\"" "$TMP/auto-vazio2.json" \
|
|
317
|
+
&& grep -q "\"needs_objective\":false" "$TMP/auto-vazio2.json"'
|
|
318
|
+
check "ll-auto roteiro: --interactive --redo phase-05 --pause-at 8" \
|
|
319
|
+
'$AUTO roteiro --cwd "$ROOT/scripts/fixtures/project" \
|
|
320
|
+
--flags "--interactive --redo phase-05 --pause-at 8" --json > "$TMP/auto-redo.json" \
|
|
321
|
+
&& grep -q "\"stage\":\"phase-05\",\"command\":\"ll-implement 05\",\"status\":\"done\"" "$TMP/auto-redo.json" \
|
|
322
|
+
&& ! grep -q "ll-implement 05 --no-talk" "$TMP/auto-redo.json" \
|
|
323
|
+
&& grep -q "\"stage\":\"phase-08\",\"command\":\"ll-implement 08\",\"status\":\"todo\",\"pause_after\":true" "$TMP/auto-redo.json"'
|
|
324
|
+
check "ll-auto roteiro: epílogo que pede ll-verify 01 insere verify-01 sem --verify all" \
|
|
325
|
+
'$AUTO roteiro --cwd "$ROOT/scripts/fixtures/auto-verify-next" --flags "" --json > "$TMP/auto-vnext.json" \
|
|
326
|
+
&& grep -q "\"stage\":\"phase-01\",\"command\":\"ll-implement 01 --no-talk\".*\"stage\":\"verify-01\",\"command\":\"ll-verify 01\"" "$TMP/auto-vnext.json" \
|
|
327
|
+
&& $AUTO roteiro --cwd "$ROOT/scripts/fixtures/project" --flags "" --json > "$TMP/auto-noverify.json" \
|
|
328
|
+
&& ! grep -q "\"stage\":\"verify-" "$TMP/auto-noverify.json"'
|
|
329
|
+
check "ll-auto next-cmd: o epílogo da fixture aponta ll-implement 8" \
|
|
330
|
+
'[ "$($AUTO next-cmd "$ROOT/scripts/fixtures/project/PROGRESS.md")" = "ll-implement 8" ]'
|
|
331
|
+
check "ll-auto next-cmd: arquivo sem ▶ Next → saída vazia, exit 0" \
|
|
332
|
+
'[ -z "$($AUTO next-cmd "$ROOT/scripts/fixtures/project/ROADMAP.md")" ]'
|
|
333
|
+
check "ll-auto report: só o DEC com a marca de decidido sozinho" \
|
|
334
|
+
'$AUTO report --cwd "$ROOT/scripts/fixtures/auto-decisions" --json > "$TMP/auto-report.json" \
|
|
335
|
+
&& grep -q "DEC-0001-taken-alone.md" "$TMP/auto-report.json" \
|
|
336
|
+
&& ! grep -q "DEC-0002-owner.md" "$TMP/auto-report.json"'
|
|
337
|
+
check "ll-auto auto-md: as cinco seções, o objetivo e as flags" \
|
|
338
|
+
'$AUTO auto-md --cwd "$ROOT/scripts/fixtures/project" --objective "um objetivo" --flags "--only 8" \
|
|
339
|
+
> "$TMP/auto-md.txt" \
|
|
340
|
+
&& grep -q "^## Objective" "$TMP/auto-md.txt" && grep -q "^## Flags" "$TMP/auto-md.txt" \
|
|
341
|
+
&& grep -q "^## Roteiro" "$TMP/auto-md.txt" && grep -q "^## Decisions taken alone" "$TMP/auto-md.txt" \
|
|
342
|
+
&& grep -q "^## Log" "$TMP/auto-md.txt" \
|
|
343
|
+
&& grep -q "^um objetivo$" "$TMP/auto-md.txt" && grep -q -e "^--only 8$" "$TMP/auto-md.txt" \
|
|
344
|
+
&& grep -q "^| # | stage | command | status | evidence |$" "$TMP/auto-md.txt" \
|
|
345
|
+
&& grep -q "^| 1 | phase-08 | ll-implement 08 --no-talk | todo |" "$TMP/auto-md.txt"'
|
|
346
|
+
fi
|
|
184
347
|
|
|
185
348
|
# ---------------------------------------------------------------------------
|
|
186
349
|
# 5. instalador
|
|
187
350
|
# ---------------------------------------------------------------------------
|
|
351
|
+
if section 5; then
|
|
188
352
|
export CLAUDE_CONFIG_DIR="$TMP/cfg" XDG_CACHE_HOME="$TMP/cache"
|
|
189
353
|
mkdir -p "$CLAUDE_CONFIG_DIR"
|
|
190
354
|
|
|
@@ -208,7 +372,7 @@ echo '{"version":"0.0.1","files":{"skills/ll-antiga/SKILL.md":"a","skills/alheia
|
|
|
208
372
|
|
|
209
373
|
node bin/install.js < /dev/null > "$TMP/install1.log"
|
|
210
374
|
|
|
211
|
-
check "
|
|
375
|
+
check "12 skills ll-* instaladas" '[ "$(ls "$CLAUDE_CONFIG_DIR/skills" | grep -c "^ll-")" -eq 12 ]'
|
|
212
376
|
for s in ll-brainstorm ll-research ll-decide ll-goal ll-implement ll-verify ll-close ll-resume ll-refine ll-oncall ll-update; do
|
|
213
377
|
check "skill $s instalada" '[ -f "$CLAUDE_CONFIG_DIR/skills/'"$s"'/SKILL.md" ]'
|
|
214
378
|
done
|
|
@@ -240,6 +404,11 @@ for s in ll-implement ll-verify ll-close; do
|
|
|
240
404
|
'[ "$(sha256sum "$CLAUDE_CONFIG_DIR/skills/'"$s"'/scripts/ll-tools.js" | cut -d" " -f1)" = "$SHA_SRC" ]'
|
|
241
405
|
done
|
|
242
406
|
|
|
407
|
+
SHA_AUTO_SRC="$(sha256sum skills/ll-auto/scripts/ll-auto.js | cut -d" " -f1)"
|
|
408
|
+
check "ll-auto.js instalado executável" '[ -x "$CLAUDE_CONFIG_DIR/skills/ll-auto/scripts/ll-auto.js" ]'
|
|
409
|
+
check "ll-auto.js instalado com sha idêntico" \
|
|
410
|
+
'[ "$(sha256sum "$CLAUDE_CONFIG_DIR/skills/ll-auto/scripts/ll-auto.js" | cut -d" " -f1)" = "$SHA_AUTO_SRC" ]'
|
|
411
|
+
|
|
243
412
|
cp "$CLAUDE_CONFIG_DIR/settings.json" "$TMP/s1.json"
|
|
244
413
|
node bin/install.js < /dev/null > /dev/null
|
|
245
414
|
check "segunda instalação sem diff no settings" 'cmp -s "$CLAUDE_CONFIG_DIR/settings.json" "$TMP/s1.json"'
|
|
@@ -260,10 +429,12 @@ mkdir -p "$XDG_CACHE_HOME/ll-skills"
|
|
|
260
429
|
V="$(cat "$CLAUDE_CONFIG_DIR/ll-skills/VERSION")"
|
|
261
430
|
printf '{"checked":%s,"installed":"%s","latest":"99.0.0","update_available":true,"method":"npm"}' "$(date +%s)" "$V" > "$XDG_CACHE_HOME/ll-skills/update-check.json"
|
|
262
431
|
check "leitor avisa versão nova" 'node "$CLAUDE_CONFIG_DIR/hooks/ll-skills-check-update.js" | grep -q "/ll-update"'
|
|
432
|
+
fi
|
|
263
433
|
|
|
264
434
|
# ---------------------------------------------------------------------------
|
|
265
435
|
# 6. preâmbulo
|
|
266
436
|
# ---------------------------------------------------------------------------
|
|
437
|
+
if section 6; then
|
|
267
438
|
check "não-TTY sem --yes não escreve CLAUDE.md" '[ ! -e "$CLAUDE_CONFIG_DIR/CLAUDE.md" ]'
|
|
268
439
|
check "não-TTY imprime o diff e a dica --yes" 'grep -q -- "--yes" "$TMP/install1.log"'
|
|
269
440
|
|
|
@@ -284,10 +455,12 @@ sed -i 's/^## Skills$/## Skills MEXIDO A MAO/' "$CMD"
|
|
|
284
455
|
node bin/install.js --yes < /dev/null > /dev/null
|
|
285
456
|
check "corpo alterado à mão é restaurado" 'cmp -s "$CMD" "$TMP/claudemd-1"'
|
|
286
457
|
check "backup .ll-skills.bak existe" '[ -f "$CMD.ll-skills.bak" ]'
|
|
458
|
+
fi
|
|
287
459
|
|
|
288
460
|
# ---------------------------------------------------------------------------
|
|
289
461
|
# 7. poda das skills 1.x (com e sem manifesto)
|
|
290
462
|
# ---------------------------------------------------------------------------
|
|
463
|
+
if section 7; then
|
|
291
464
|
LEGACY_SKILLS="ll-atualizar ll-decidir-antes ll-desarmar ll-orquestrar ll-pesquisar ll-pesquisar-mercado ll-verificar-entrega ll-voltar-do-futuro"
|
|
292
465
|
CFG_L="$TMP/cfg-legacy"
|
|
293
466
|
seed_legacy() {
|
|
@@ -318,10 +491,12 @@ assert_pruned "com manifesto"
|
|
|
318
491
|
seed_legacy # agora o manifesto atual (2.x) não lista nada disso: só KNOWN_LEGACY resolve
|
|
319
492
|
CLAUDE_CONFIG_DIR="$CFG_L" node bin/install.js --no-preamble < /dev/null > /dev/null
|
|
320
493
|
assert_pruned "sem manifesto"
|
|
494
|
+
fi
|
|
321
495
|
|
|
322
496
|
# ---------------------------------------------------------------------------
|
|
323
497
|
# 8. uninstall
|
|
324
498
|
# ---------------------------------------------------------------------------
|
|
499
|
+
if section 8; then
|
|
325
500
|
node bin/install.js --uninstall < /dev/null > /dev/null
|
|
326
501
|
check "uninstall removeu skills" '[ -z "$(ls "$CLAUDE_CONFIG_DIR/skills" | grep "^ll-" || true)" ]'
|
|
327
502
|
check "uninstall removeu as cópias do helper" '[ ! -e "$CLAUDE_CONFIG_DIR/skills/ll-implement" ]'
|
|
@@ -331,10 +506,312 @@ check "uninstall removeu o check-update" '! grep -q "ll-skills-check-update" "$C
|
|
|
331
506
|
check "uninstall preservou alheios" 'grep -q "echo alheio" "$CLAUDE_CONFIG_DIR/settings.json" && [ -f "$CLAUDE_CONFIG_DIR/skills/alheia/SKILL.md" ]'
|
|
332
507
|
check "uninstall removeu o preâmbulo" '! grep -q "ll-skills:preamble" "$CMD"'
|
|
333
508
|
check "uninstall preservou as sentinelas" 'grep -q "SENTINEL-TOP" "$CMD" && grep -q "SENTINEL-BOTTOM" "$CMD"'
|
|
509
|
+
fi
|
|
510
|
+
|
|
511
|
+
# ---------------------------------------------------------------------------
|
|
512
|
+
# 9. lint dos prompts (uma checagem por regra de scripts/lint-prompts.sh)
|
|
513
|
+
# ---------------------------------------------------------------------------
|
|
514
|
+
if section 9; then
|
|
515
|
+
lint() { # lint <n>: roda uma regra e só imprime a saída quando ela falha
|
|
516
|
+
bash "$ROOT/scripts/lint-prompts.sh" --rule "$1" > "$TMP/lint-$1.txt" 2>&1 \
|
|
517
|
+
|| { cat "$TMP/lint-$1.txt"; return 1; }
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
check "lint 1: frontmatter das skills" 'lint 1'
|
|
521
|
+
check "lint 2: frontmatter dos agentes" 'lint 2'
|
|
522
|
+
check "lint 3: tetos de linhas" 'lint 3'
|
|
523
|
+
printf '[{"unpackedSize":999999999}]' > "$TMP/pack-list-big.json"
|
|
524
|
+
check "lint 3: honra LL_PACK_JSON na forma lista (npm ≤ 11) e reprova no teto sintético" \
|
|
525
|
+
'! LL_PACK_JSON="$TMP/pack-list-big.json" bash "$ROOT/scripts/lint-prompts.sh" --rule 3 > "$TMP/lint-pack-list.out" 2>&1; \
|
|
526
|
+
grep -q "unpackedSize 999999999 bytes, ceiling 921600" "$TMP/lint-pack-list.out"'
|
|
527
|
+
printf '{"ll-skills":{"unpackedSize":999999999}}' > "$TMP/pack-object-big.json"
|
|
528
|
+
check "lint 3: honra LL_PACK_JSON na forma objeto (npm 12) e reprova no teto sintético" \
|
|
529
|
+
'! LL_PACK_JSON="$TMP/pack-object-big.json" bash "$ROOT/scripts/lint-prompts.sh" --rule 3 > "$TMP/lint-pack-object.out" 2>&1; \
|
|
530
|
+
grep -q "unpackedSize 999999999 bytes, ceiling 921600" "$TMP/lint-pack-object.out"'
|
|
531
|
+
check "lint 4: forma das SKILL.md" 'lint 4'
|
|
532
|
+
check "lint 5: strings proibidas" 'lint 5'
|
|
533
|
+
check "lint 6: cópias idênticas" 'lint 6'
|
|
534
|
+
check "lint 7: idioma" 'lint 7'
|
|
535
|
+
check "lint 8: lista privada" 'lint 8'
|
|
536
|
+
check "lint 9: perguntas sem jargão" 'lint 9 && grep -q "^ok 9 plain questions" "$TMP/lint-9.txt"'
|
|
537
|
+
fi
|
|
334
538
|
|
|
539
|
+
# 10. lint de contrato entre as peças (uma checagem por regra de scripts/lint-contract.cjs)
|
|
540
|
+
if section 10; then
|
|
541
|
+
contract() { node "$ROOT/scripts/lint-contract.cjs" --rule "$1" >/dev/null 2>&1; }
|
|
542
|
+
check "contrato 1: references citadas existem e são citadas" 'contract 1'
|
|
543
|
+
check "contrato 2: comandos do helper definidos e citados" 'contract 2'
|
|
544
|
+
check "contrato 3: agentes, modelos e campos do brief" 'contract 3'
|
|
545
|
+
check "contrato 4: vocabulário de veredito e estados" 'contract 4'
|
|
546
|
+
check "contrato 5: arquivos de estado nas tabelas de entregáveis" 'contract 5'
|
|
547
|
+
check "contrato 6: alvos do ▶ Next existem" 'contract 6'
|
|
548
|
+
check "contrato 7: instalador × pacote" 'contract 7'
|
|
549
|
+
|
|
550
|
+
# regra 6 contra as fixtures da gramática do ▶ Next (bad falha, good passa, raiz sem skills falha)
|
|
551
|
+
contract_root() { node "$ROOT/scripts/lint-contract.cjs" --rule 6 --root "$1" >/dev/null 2>&1; }
|
|
552
|
+
contract_fails() { node "$ROOT/scripts/lint-contract.cjs" --rule 6 --root "$1" 2>/dev/null | grep -c "^FAIL 6 " || true; }
|
|
553
|
+
check "contrato 6: next-bad falha" '! contract_root scripts/fixtures/next-bad'
|
|
554
|
+
# a contagem é fixa: afrouxar uma das formas rejeitadas reprova aqui, não só o exit
|
|
555
|
+
check "contrato 6: next-bad com 6 FAIL" '[ "$(contract_fails scripts/fixtures/next-bad)" -eq 6 ]'
|
|
556
|
+
check "contrato 6: duas skills diferentes num ▶ Next fora de parêntese reprovam" \
|
|
557
|
+
'node "$ROOT/scripts/lint-contract.cjs" --rule 6 --root scripts/fixtures/next-bad > "$TMP/next-bad.out" 2>/dev/null; \
|
|
558
|
+
grep -q "lists alternatives outside a parenthetical —ll-bad or ll-nope" "$TMP/next-bad.out"'
|
|
559
|
+
check "contrato 6: next-good passa" 'contract_root scripts/fixtures/next-good'
|
|
560
|
+
check "contrato 6: raiz sem skills falha" '! contract_root scripts/fixtures/empty'
|
|
335
561
|
|
|
336
562
|
# --- optional private word list (never shipped): LL_FORBIDDEN_FILE=<path> enables the check
|
|
337
563
|
if [ -n "${LL_FORBIDDEN_FILE:-}" ] && [ -f "$LL_FORBIDDEN_FILE" ]; then
|
|
338
564
|
check "no private terms in tracked files" '! git ls-files | grep -v "^\.gitignore$" | xargs grep -n -i -w -E -f "$LL_FORBIDDEN_FILE" 2>/dev/null | grep -q .'
|
|
339
565
|
fi
|
|
566
|
+
fi
|
|
567
|
+
|
|
568
|
+
# ---------------------------------------------------------------------------
|
|
569
|
+
# lint-orquestrador. as exceções de ll-auto valem só para ll-auto
|
|
570
|
+
# ---------------------------------------------------------------------------
|
|
571
|
+
if section lint-orquestrador; then
|
|
572
|
+
cd "$ROOT"
|
|
573
|
+
SCR="$TMP/lint-scratch"
|
|
574
|
+
mkdir -p "$SCR"
|
|
575
|
+
git ls-files -z | xargs -0 cp --parents -t "$SCR"
|
|
576
|
+
git -C "$SCR" init -q
|
|
577
|
+
git -C "$SCR" add -A
|
|
578
|
+
mkdir -p "$SCR/skills/ll-fake"
|
|
579
|
+
cat > "$SCR/skills/ll-fake/SKILL.md" <<'FAKE'
|
|
580
|
+
---
|
|
581
|
+
name: ll-fake
|
|
582
|
+
description: Pretends to be an orchestrator so the lint can prove the ll-auto exceptions do not leak to any other skill on the tree.
|
|
583
|
+
argument-hint: "[--flag]"
|
|
584
|
+
disable-model-invocation: true
|
|
585
|
+
allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/ll-auto.js *)
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
# Fake
|
|
589
|
+
|
|
590
|
+
## Deliverables
|
|
591
|
+
|
|
592
|
+
| File | Role | Mutability |
|
|
593
|
+
|---|---|---|
|
|
594
|
+
| `docs/FAKE.md` | nothing | never |
|
|
595
|
+
|
|
596
|
+
## Flow
|
|
597
|
+
|
|
598
|
+
/ll-research x
|
|
599
|
+
|
|
600
|
+
## Completion criterion
|
|
601
|
+
|
|
602
|
+
▶ Next — /clear, then ll-resume
|
|
603
|
+
FAKE
|
|
604
|
+
git -C "$SCR" add -A
|
|
605
|
+
lint_scr() { bash "$SCR/scripts/lint-prompts.sh" --rule "$1" > "$TMP/scr-rule$1.out" 2>&1; }
|
|
606
|
+
lint_real() { bash "$ROOT/scripts/lint-prompts.sh" --rule "$1" >/dev/null 2>&1; }
|
|
607
|
+
|
|
608
|
+
check "orquestrador: skills/ll-auto/SKILL.md existe" '[ -f "$ROOT/skills/ll-auto/SKILL.md" ]'
|
|
609
|
+
check "orquestrador: regra 1 reprova o allowed-tools do ll-auto em outra skill" \
|
|
610
|
+
'! lint_scr 1 && grep -q "^FAIL 1 .*skills/ll-fake/SKILL.md: allowed-tools" "$TMP/scr-rule1.out"'
|
|
611
|
+
check "orquestrador: regra 5 reprova a linha /ll- em outra skill" \
|
|
612
|
+
'! lint_scr 5 && grep -q "^FAIL 5 .*skills/ll-fake/SKILL.md: line .* invokes a skill as a command" "$TMP/scr-rule5.out"'
|
|
613
|
+
check "orquestrador: regras 1 e 5 passam na árvore real (com ll-auto)" \
|
|
614
|
+
'lint_real 1 && lint_real 5'
|
|
615
|
+
fi
|
|
616
|
+
|
|
617
|
+
# ---------------------------------------------------------------------------
|
|
618
|
+
# goal-autonomo. o modo --autonomous do ll-goal: hint, template e exemplo
|
|
619
|
+
# ---------------------------------------------------------------------------
|
|
620
|
+
if section goal-autonomo; then
|
|
621
|
+
cd "$ROOT"
|
|
622
|
+
GOAL_SKILL="skills/ll-goal/SKILL.md"
|
|
623
|
+
GOAL_TPL="skills/ll-goal/references/goal-template.md"
|
|
624
|
+
# primeiro bloco cercado sob "## Autonomous example", sem as cercas
|
|
625
|
+
goal_example() { awk '/^## Autonomous example$/{f=1;next} f&&/^```/{if(b){exit}b=1;next} f&&b' "$GOAL_TPL"; }
|
|
626
|
+
|
|
627
|
+
check "ll-goal argument-hint aceita --autonomous" \
|
|
628
|
+
'sed -n "/^argument-hint:/p" "$GOAL_SKILL" | grep -q -- "--autonomous"'
|
|
629
|
+
check "goal-template cita ll-auto --auto-decision" \
|
|
630
|
+
'[ "$(grep -c "ll-auto --auto-decision" "$GOAL_TPL")" -ge 1 ]'
|
|
631
|
+
check "exemplo autônomo entre 500 e 4000 bytes" \
|
|
632
|
+
'n=$(goal_example | wc -c); [ "$n" -ge 500 ] && [ "$n" -le 4000 ]'
|
|
633
|
+
check "goal-template ≤ 150 linhas" \
|
|
634
|
+
'[ "$(wc -l < "$GOAL_TPL")" -le 150 ]'
|
|
635
|
+
fi
|
|
636
|
+
|
|
637
|
+
# ---------------------------------------------------------------------------
|
|
638
|
+
# evals-auto. os asserts dos casos autônomos provados offline, sem nenhuma chamada paga
|
|
639
|
+
# ---------------------------------------------------------------------------
|
|
640
|
+
if section evals-auto; then
|
|
641
|
+
cd "$ROOT"
|
|
642
|
+
EV="$TMP/evals-auto"
|
|
643
|
+
mkdir -p "$EV"
|
|
644
|
+
# a resposta errada: não carrega nenhuma linha que os asserts exigem
|
|
645
|
+
printf 'I ran nothing and wrote nothing.\n' > "$EV/wrong.txt"
|
|
646
|
+
|
|
647
|
+
# a captura limpa: um evento assistant de texto (para o no_tool_use varrer algo) e o result.
|
|
648
|
+
CAP_CLEAN='[{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"Rodei o dry run e nao escrevi nada."}]}},{"type":"result","subtype":"success","is_error":false,"result":""}]'
|
|
649
|
+
# a mesma captura com uma chamada de skill por tool_use: o assert tem de reprovar.
|
|
650
|
+
CAP_SKILL='[{"type":"assistant","message":{"role":"assistant","content":[{"type":"tool_use","id":"t1","name":"Skill","input":{"command":"ll-implement"}}]}},{"type":"result","subtype":"success","is_error":false,"result":""}]'
|
|
651
|
+
|
|
652
|
+
# uma árvore de trabalho limpa por chamada (os asserts leem `git status --porcelain` dela)
|
|
653
|
+
auto_assert() { # auto_assert <caso> <arquivo de resposta> [captura]
|
|
654
|
+
local w="$EV/$1"
|
|
655
|
+
rm -rf "$w"; mkdir -p "$w"
|
|
656
|
+
git -C "$w" init -q -b main
|
|
657
|
+
printf '%s' "${3:-$CAP_CLEAN}" > "$w/out.json"
|
|
658
|
+
bash "$ROOT/scripts/evals/cases/$1/assert.sh" "$w" "$w/out.json" "$2" > "$EV/$1.log" 2>&1
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
check "a captura offline tem ao menos um evento assistant" \
|
|
662
|
+
'node -e "const a=JSON.parse(process.argv[1]);process.exit(a.filter(e=>e.type===\"assistant\").length?0:1)" "$CAP_CLEAN"'
|
|
663
|
+
|
|
664
|
+
check "assert auto-dry-run aceita a resposta boa" \
|
|
665
|
+
'auto_assert auto-dry-run "$ROOT/scripts/fixtures/evals-auto/auto-dry-run/pass.txt"'
|
|
666
|
+
check "assert auto-dry-run rejeita resposta errada" \
|
|
667
|
+
'! auto_assert auto-dry-run "$EV/wrong.txt"'
|
|
668
|
+
check "assert auto-empty-repo aceita a resposta boa" \
|
|
669
|
+
'auto_assert auto-empty-repo "$ROOT/scripts/fixtures/evals-auto/auto-empty-repo/pass.txt"'
|
|
670
|
+
check "assert auto-empty-repo rejeita resposta errada" \
|
|
671
|
+
'! auto_assert auto-empty-repo "$EV/wrong.txt"'
|
|
672
|
+
# o no_tool_use não é vazio: uma captura com tool_use Skill reprova o mesmo assert
|
|
673
|
+
check "assert auto-dry-run rejeita captura com tool_use Skill" \
|
|
674
|
+
'! auto_assert auto-dry-run "$ROOT/scripts/fixtures/evals-auto/auto-dry-run/pass.txt" "$CAP_SKILL"'
|
|
675
|
+
check "assert auto-empty-repo rejeita captura com tool_use Skill" \
|
|
676
|
+
'! auto_assert auto-empty-repo "$ROOT/scripts/fixtures/evals-auto/auto-empty-repo/pass.txt" "$CAP_SKILL"'
|
|
677
|
+
|
|
678
|
+
# goal-autonomous: o assert espera docs/GOAL.md já no lugar (um `ll-goal --autonomous` real
|
|
679
|
+
# escreve e comita o arquivo antes de imprimir o texto colável), não só o out.json do harness.
|
|
680
|
+
goal_autonomous_assert() { # goal_autonomous_assert <arquivo de resposta> [captura]
|
|
681
|
+
local w="$EV/goal-autonomous"
|
|
682
|
+
rm -rf "$w"; mkdir -p "$w/docs"
|
|
683
|
+
git -C "$w" init -q -b main
|
|
684
|
+
printf 'mode: autonomous\nphase: all\n' > "$w/docs/GOAL.md"
|
|
685
|
+
git -C "$w" add -A && git -C "$w" -c user.email=t@t -c user.name=t commit -q -m "docs: GOAL.md"
|
|
686
|
+
printf '%s' "${2:-$CAP_CLEAN}" > "$w/out.json"
|
|
687
|
+
bash "$ROOT/scripts/evals/cases/goal-autonomous/assert.sh" "$w" "$w/out.json" "$1" > "$EV/goal-autonomous.log" 2>&1
|
|
688
|
+
}
|
|
689
|
+
check "assert goal-autonomous aceita a resposta boa" \
|
|
690
|
+
'goal_autonomous_assert "$ROOT/scripts/fixtures/evals-auto/goal-autonomous/pass.txt"'
|
|
691
|
+
check "assert goal-autonomous rejeita resposta vazia" \
|
|
692
|
+
'! goal_autonomous_assert "$EV/wrong.txt"'
|
|
693
|
+
echo "evals-auto: goal-autonomous pair asserted"
|
|
694
|
+
|
|
695
|
+
# router-no-skill: par offline (captura limpa + resposta boa/ruim), nenhuma chamada paga.
|
|
696
|
+
# Skill só por comando explícito: um pedido comum é atendido na hora, mesmo com estado ll no repo.
|
|
697
|
+
rns_assert() { # rns_assert <arquivo de resposta> [captura inline]
|
|
698
|
+
local w="$EV/router-no-skill"
|
|
699
|
+
rm -rf "$w"; mkdir -p "$w"
|
|
700
|
+
git -C "$w" init -q -b main
|
|
701
|
+
local cap="$ROOT/scripts/evals/fixtures/router-no-skill/out.json"
|
|
702
|
+
if [ $# -ge 2 ]; then printf '%s' "$2" > "$w/out.json"; cap="$w/out.json"; fi
|
|
703
|
+
bash "$ROOT/scripts/evals/cases/router-no-skill/assert.sh" "$w" "$cap" "$1" \
|
|
704
|
+
> "$EV/router-no-skill.log" 2>&1
|
|
705
|
+
}
|
|
706
|
+
check "assert router-no-skill aceita a pesquisa entregue na hora" \
|
|
707
|
+
'rns_assert "$ROOT/scripts/evals/fixtures/router-no-skill/pass.txt"'
|
|
708
|
+
check "assert router-no-skill rejeita a resposta que só manda colar o comando" \
|
|
709
|
+
'! rns_assert "$ROOT/scripts/evals/fixtures/router-no-skill/fail.txt"'
|
|
710
|
+
check "assert router-no-skill rejeita captura com tool_use Skill" \
|
|
711
|
+
'! rns_assert "$ROOT/scripts/evals/fixtures/router-no-skill/pass.txt" "$CAP_SKILL"'
|
|
712
|
+
|
|
713
|
+
# implement-stops-at-next: a linha de onda antes do epílogo e o helper chamado, nunca lido
|
|
714
|
+
CAP_ONDA='[{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"onda 1/2 — M1, M2 rodando (opus, sonnet)"}]}},{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"▶ Next — /clear, then /ll-implement 8"}]}},{"type":"result","subtype":"success","is_error":false,"result":""}]'
|
|
715
|
+
CAP_LE_HELPER='[{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"onda 1/2 — M1, M2 rodando (opus, sonnet)"}]}},{"type":"assistant","message":{"role":"assistant","content":[{"type":"tool_use","id":"t1","name":"Bash","input":{"command":"head -40 scripts/ll-tools.js"}}]}},{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"▶ Next — /clear, then /ll-implement 8"}]}},{"type":"result","subtype":"success","is_error":false,"result":""}]'
|
|
716
|
+
printf 'onda 1/2 — M1, M2 rodando (opus, sonnet)\nonda 1/2 — M1 ok (smoke test OK)\n## Epilogue — phase 07 — 2026-09-11\n▶ Next — `/clear`, then `/ll-implement 8`\n' > "$EV/onda.txt"
|
|
717
|
+
printf '## Epilogue — phase 07 — 2026-09-11\n▶ Next — `/clear`, then `/ll-implement 8`\n' > "$EV/sem-onda.txt"
|
|
718
|
+
isn_assert() { # isn_assert <arquivo de resposta> [captura]
|
|
719
|
+
local w="$EV/implement-stops-at-next"
|
|
720
|
+
rm -rf "$w"; mkdir -p "$w"
|
|
721
|
+
printf '%s' "${2:-$CAP_ONDA}" > "$w/out.json"
|
|
722
|
+
bash "$ROOT/scripts/evals/cases/implement-stops-at-next/assert.sh" "$w" "$w/out.json" "$1" \
|
|
723
|
+
> "$EV/implement-stops-at-next.log" 2>&1
|
|
724
|
+
}
|
|
725
|
+
check "assert implement-stops-at-next aceita onda antes do epílogo" \
|
|
726
|
+
'isn_assert "$EV/onda.txt"'
|
|
727
|
+
CAP_SEM_ONDA='[{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"scout rodando (sonnet)"}]}},{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"▶ Next — /clear, then /ll-implement 8"}]}},{"type":"result","subtype":"success","is_error":false,"result":""}]'
|
|
728
|
+
check "assert implement-stops-at-next rejeita captura sem linha de onda" \
|
|
729
|
+
'! isn_assert "$EV/sem-onda.txt" "$CAP_SEM_ONDA"'
|
|
730
|
+
check "assert implement-stops-at-next rejeita captura que lê o helper com head" \
|
|
731
|
+
'! isn_assert "$EV/onda.txt" "$CAP_LE_HELPER"'
|
|
732
|
+
|
|
733
|
+
# decide-final-round: contagem em palavras simples e o caminho das opções antes da 1ª pergunta
|
|
734
|
+
printf 'docs/decide/OPTIONS.html — as opções lado a lado\nquestions asked 2 / assumptions 3 / owner decisions open 1\n**Pergunta 1/2 — limite de título (impacto MÉDIO · desfazer: barato)**\n' > "$EV/round-ok.txt"
|
|
735
|
+
printf 'questions asked 2 / assumptions 3 / owner decisions open 1\n**Pergunta 1/2 — limite de título (impacto MÉDIO · desfazer: barato)**\ndocs/decide/OPTIONS.html — as opções lado a lado\n' > "$EV/round-tarde.txt"
|
|
736
|
+
printf 'questions asked 2 / assumptions 3 / band-1 open 1\n**Pergunta 1/2 — limite de título**\n' > "$EV/round-jargao.txt"
|
|
737
|
+
dfr_assert() { # dfr_assert <arquivo de resposta>
|
|
738
|
+
local w="$EV/decide-final-round"
|
|
739
|
+
rm -rf "$w"; mkdir -p "$w"
|
|
740
|
+
git -C "$w" init -q -b main
|
|
741
|
+
printf '%s' "$CAP_CLEAN" > "$w/out.json"
|
|
742
|
+
bash "$ROOT/scripts/evals/cases/decide-final-round/assert.sh" "$w" "$w/out.json" "$1" \
|
|
743
|
+
> "$EV/decide-final-round.log" 2>&1
|
|
744
|
+
}
|
|
745
|
+
check "assert decide-final-round aceita a rodada com o caminho antes da pergunta" \
|
|
746
|
+
'dfr_assert "$EV/round-ok.txt"'
|
|
747
|
+
check "assert decide-final-round rejeita o caminho depois da primeira pergunta" \
|
|
748
|
+
'! dfr_assert "$EV/round-tarde.txt"'
|
|
749
|
+
check "assert decide-final-round rejeita a contagem com o rótulo antigo" \
|
|
750
|
+
'! dfr_assert "$EV/round-jargao.txt"'
|
|
751
|
+
fi
|
|
752
|
+
|
|
753
|
+
# ---------------------------------------------------------------------------
|
|
754
|
+
# lint-scratch. a regra 1 do lint-prompts contra SKILL.md ruins, numa árvore de rascunho
|
|
755
|
+
# ---------------------------------------------------------------------------
|
|
756
|
+
if section lint-scratch; then
|
|
757
|
+
cd "$ROOT"
|
|
758
|
+
LSCR="$TMP/lint-bad-scratch"
|
|
759
|
+
mkdir -p "$LSCR"
|
|
760
|
+
git ls-files -z | xargs -0 cp --parents -t "$LSCR"
|
|
761
|
+
git -C "$LSCR" init -q
|
|
762
|
+
mkdir -p "$LSCR/skills/ll-fake"
|
|
763
|
+
# lint_bad <caso> <trecho esperado no FAIL>: planta a fixture como skills/ll-fake e roda a regra 1
|
|
764
|
+
lint_bad() {
|
|
765
|
+
cp "$ROOT/scripts/fixtures/lint-bad/$1/SKILL.md" "$LSCR/skills/ll-fake/SKILL.md"
|
|
766
|
+
git -C "$LSCR" add -A
|
|
767
|
+
bash "$LSCR/scripts/lint-prompts.sh" --rule 1 > "$TMP/lint-bad-$1.out" 2>&1 && return 1
|
|
768
|
+
grep -q "^FAIL 1 .*skills/ll-fake/SKILL.md: $2" "$TMP/lint-bad-$1.out"
|
|
769
|
+
}
|
|
770
|
+
check "lint 1: descrição dobrada (>) em mais de uma linha reprova" \
|
|
771
|
+
'lint_bad folded-description "description is not one line"'
|
|
772
|
+
check "lint 1: disable-model-invocation: false reprova" \
|
|
773
|
+
'lint_bad model-invocation-false "disable-model-invocation: true missing"'
|
|
774
|
+
# lint_bad9 <caso> <trecho esperado no FAIL>: a mesma fixture contra a regra 9
|
|
775
|
+
lint_bad9() {
|
|
776
|
+
cp "$ROOT/scripts/fixtures/lint-bad/$1/SKILL.md" "$LSCR/skills/ll-fake/SKILL.md"
|
|
777
|
+
git -C "$LSCR" add -A
|
|
778
|
+
bash "$LSCR/scripts/lint-prompts.sh" --rule 9 > "$TMP/lint-bad9-$1.out" 2>&1 && return 1
|
|
779
|
+
grep -q "^FAIL 9 .*skills/ll-fake/SKILL.md: line [0-9]*: .*$2" "$TMP/lint-bad9-$1.out"
|
|
780
|
+
}
|
|
781
|
+
check "lint 9: id de decisão no cabeçalho da pergunta reprova" \
|
|
782
|
+
'lint_bad9 jargon-in-questions "\[DEC-"'
|
|
783
|
+
check "lint 9: a linha de contagem com o rótulo antigo reprova" \
|
|
784
|
+
'lint_bad9 jargon-in-questions "band-1 open"'
|
|
785
|
+
check "lint 9: id de pergunta [PG- no cabeçalho reprova" \
|
|
786
|
+
'lint_bad9 jargon-in-questions "\[PG-"'
|
|
787
|
+
check "lint 9: a palavra banda 1 na prosa reprova" \
|
|
788
|
+
'lint_bad9 jargon-in-questions "banda 1"'
|
|
789
|
+
# sem a fixture a mesma árvore passa: o FAIL vem da SKILL.md ruim, não da cópia
|
|
790
|
+
rm -rf "$LSCR/skills/ll-fake"
|
|
791
|
+
git -C "$LSCR" add -A
|
|
792
|
+
check "lint 1: a árvore de rascunho sem a fixture passa" \
|
|
793
|
+
'bash "$LSCR/scripts/lint-prompts.sh" --rule 1 > "$TMP/lint-bad-clean.out" 2>&1'
|
|
794
|
+
check "lint 9: a árvore de rascunho sem a fixture passa" \
|
|
795
|
+
'bash "$LSCR/scripts/lint-prompts.sh" --rule 9 > "$TMP/lint-bad9-clean.out" 2>&1'
|
|
796
|
+
fi
|
|
797
|
+
|
|
798
|
+
# ---------------------------------------------------------------------------
|
|
799
|
+
# no-talk. o modo silencioso continua declarado no argument-hint de quem o implementa
|
|
800
|
+
# ---------------------------------------------------------------------------
|
|
801
|
+
if section no-talk; then
|
|
802
|
+
cd "$ROOT"
|
|
803
|
+
hint_has_no_talk() { # hint_has_no_talk <SKILL.md>
|
|
804
|
+
sed -n '/^argument-hint:/p' "$1" > "$TMP/hint.txt"
|
|
805
|
+
grep -q -- "--no-talk" "$TMP/hint.txt"
|
|
806
|
+
}
|
|
807
|
+
for s in ll-decide ll-close; do
|
|
808
|
+
check "argument-hint de $s declara --no-talk" 'hint_has_no_talk "skills/'"$s"'/SKILL.md"'
|
|
809
|
+
done
|
|
810
|
+
# a checagem não é vazia: sem a flag na linha, ela reprova
|
|
811
|
+
NT="$TMP/no-talk"
|
|
812
|
+
mkdir -p "$NT"
|
|
813
|
+
sed 's/ \[--no-talk\]//' skills/ll-decide/SKILL.md > "$NT/SKILL.md"
|
|
814
|
+
check "a checagem reprova quando --no-talk some do hint" '! hint_has_no_talk "$NT/SKILL.md"'
|
|
815
|
+
fi
|
|
816
|
+
|
|
340
817
|
echo "smoke test OK — $N checks"
|