autonomous-coding-toolkit 1.0.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/.claude-plugin/marketplace.json +22 -0
- package/.claude-plugin/plugin.json +13 -0
- package/LICENSE +21 -0
- package/Makefile +21 -0
- package/README.md +140 -0
- package/SECURITY.md +28 -0
- package/agents/bash-expert.md +113 -0
- package/agents/dependency-auditor.md +138 -0
- package/agents/integration-tester.md +120 -0
- package/agents/lesson-scanner.md +149 -0
- package/agents/python-expert.md +179 -0
- package/agents/service-monitor.md +141 -0
- package/agents/shell-expert.md +147 -0
- package/benchmarks/runner.sh +147 -0
- package/benchmarks/tasks/01-rest-endpoint/rubric.sh +29 -0
- package/benchmarks/tasks/01-rest-endpoint/task.md +17 -0
- package/benchmarks/tasks/02-refactor-module/task.md +8 -0
- package/benchmarks/tasks/03-fix-integration-bug/task.md +8 -0
- package/benchmarks/tasks/04-add-test-coverage/task.md +8 -0
- package/benchmarks/tasks/05-multi-file-feature/task.md +8 -0
- package/bin/act.js +238 -0
- package/commands/autocode.md +6 -0
- package/commands/cancel-ralph.md +18 -0
- package/commands/code-factory.md +53 -0
- package/commands/create-prd.md +55 -0
- package/commands/ralph-loop.md +18 -0
- package/commands/run-plan.md +117 -0
- package/commands/submit-lesson.md +122 -0
- package/docs/ARCHITECTURE.md +630 -0
- package/docs/CONTRIBUTING.md +125 -0
- package/docs/lessons/0001-bare-exception-swallowing.md +34 -0
- package/docs/lessons/0002-async-def-without-await.md +28 -0
- package/docs/lessons/0003-create-task-without-callback.md +28 -0
- package/docs/lessons/0004-hardcoded-test-counts.md +28 -0
- package/docs/lessons/0005-sqlite-without-closing.md +33 -0
- package/docs/lessons/0006-venv-pip-path.md +27 -0
- package/docs/lessons/0007-runner-state-self-rejection.md +35 -0
- package/docs/lessons/0008-quality-gate-blind-spot.md +33 -0
- package/docs/lessons/0009-parser-overcount-empty-batches.md +36 -0
- package/docs/lessons/0010-local-outside-function-bash.md +33 -0
- package/docs/lessons/0011-batch-tests-for-unimplemented-code.md +36 -0
- package/docs/lessons/0012-api-markdown-unescaped-chars.md +33 -0
- package/docs/lessons/0013-export-prefix-env-parsing.md +33 -0
- package/docs/lessons/0014-decorator-registry-import-side-effect.md +43 -0
- package/docs/lessons/0015-frontend-backend-schema-drift.md +43 -0
- package/docs/lessons/0016-event-driven-cold-start-seeding.md +44 -0
- package/docs/lessons/0017-copy-paste-logic-diverges.md +43 -0
- package/docs/lessons/0018-layer-passes-pipeline-broken.md +45 -0
- package/docs/lessons/0019-systemd-envfile-ignores-export.md +41 -0
- package/docs/lessons/0020-persist-state-incrementally.md +44 -0
- package/docs/lessons/0021-dual-axis-testing.md +48 -0
- package/docs/lessons/0022-jsx-factory-shadowing.md +43 -0
- package/docs/lessons/0023-static-analysis-spiral.md +51 -0
- package/docs/lessons/0024-shared-pipeline-implementation.md +55 -0
- package/docs/lessons/0025-defense-in-depth-all-entry-points.md +65 -0
- package/docs/lessons/0026-linter-no-rules-false-enforcement.md +54 -0
- package/docs/lessons/0027-jsx-silent-prop-drop.md +64 -0
- package/docs/lessons/0028-no-infrastructure-in-client-code.md +49 -0
- package/docs/lessons/0029-never-write-secrets-to-files.md +61 -0
- package/docs/lessons/0030-cache-merge-not-replace.md +62 -0
- package/docs/lessons/0031-verify-units-at-boundaries.md +66 -0
- package/docs/lessons/0032-module-lifecycle-subscribe-unsubscribe.md +89 -0
- package/docs/lessons/0033-async-iteration-mutable-snapshot.md +72 -0
- package/docs/lessons/0034-caller-missing-await-silent-discard.md +65 -0
- package/docs/lessons/0035-duplicate-registration-silent-overwrite.md +85 -0
- package/docs/lessons/0036-websocket-dirty-disconnect.md +33 -0
- package/docs/lessons/0037-parallel-agents-worktree-corruption.md +31 -0
- package/docs/lessons/0038-subscribe-no-stored-ref.md +36 -0
- package/docs/lessons/0039-fallback-or-default-hides-bugs.md +34 -0
- package/docs/lessons/0040-event-firehose-filter-first.md +36 -0
- package/docs/lessons/0041-ambiguous-base-dir-path-nesting.md +32 -0
- package/docs/lessons/0042-spec-compliance-insufficient.md +36 -0
- package/docs/lessons/0043-exact-count-extensible-collections.md +32 -0
- package/docs/lessons/0044-relative-file-deps-worktree.md +39 -0
- package/docs/lessons/0045-iterative-design-improvement.md +33 -0
- package/docs/lessons/0046-plan-assertion-math-bugs.md +38 -0
- package/docs/lessons/0047-pytest-single-threaded-default.md +37 -0
- package/docs/lessons/0048-integration-wiring-batch.md +40 -0
- package/docs/lessons/0049-ab-verification.md +41 -0
- package/docs/lessons/0050-editing-sourced-files-during-execution.md +33 -0
- package/docs/lessons/0051-infrastructure-fixes-cant-self-heal.md +30 -0
- package/docs/lessons/0052-uncommitted-changes-poison-quality-gates.md +31 -0
- package/docs/lessons/0053-jq-compact-flag-inconsistency.md +31 -0
- package/docs/lessons/0054-parser-matches-inside-code-blocks.md +30 -0
- package/docs/lessons/0055-agents-compensate-for-garbled-prompts.md +31 -0
- package/docs/lessons/0056-grep-count-exit-code-on-zero.md +42 -0
- package/docs/lessons/0057-new-artifacts-break-git-clean-gates.md +42 -0
- package/docs/lessons/0058-dead-config-keys-never-consumed.md +49 -0
- package/docs/lessons/0059-contract-test-shared-structures.md +53 -0
- package/docs/lessons/0060-set-e-silent-death-in-runners.md +53 -0
- package/docs/lessons/0061-context-injection-dirty-state.md +50 -0
- package/docs/lessons/0062-sibling-bug-neighborhood-scan.md +29 -0
- package/docs/lessons/0063-one-flag-two-lifetimes.md +31 -0
- package/docs/lessons/0064-test-passes-wrong-reason.md +31 -0
- package/docs/lessons/0065-pipefail-grep-count-double-output.md +39 -0
- package/docs/lessons/0066-local-keyword-outside-function.md +37 -0
- package/docs/lessons/0067-stdin-hang-non-interactive-shell.md +36 -0
- package/docs/lessons/0068-agent-builds-wrong-thing-correctly.md +31 -0
- package/docs/lessons/0069-plan-quality-dominates-execution.md +30 -0
- package/docs/lessons/0070-spec-echo-back-prevents-drift.md +31 -0
- package/docs/lessons/0071-positive-instructions-outperform-negative.md +30 -0
- package/docs/lessons/0072-lost-in-the-middle-context-placement.md +30 -0
- package/docs/lessons/0073-unscoped-lessons-cause-false-positives.md +30 -0
- package/docs/lessons/0074-stale-context-injection-wrong-batch.md +32 -0
- package/docs/lessons/0075-research-artifacts-must-persist.md +32 -0
- package/docs/lessons/0076-wrong-decomposition-contaminates-downstream.md +30 -0
- package/docs/lessons/0077-cherry-pick-merges-need-manual-resolution.md +30 -0
- package/docs/lessons/0078-static-review-without-live-test.md +30 -0
- package/docs/lessons/0079-integration-wiring-batch-required.md +32 -0
- package/docs/lessons/FRAMEWORK.md +161 -0
- package/docs/lessons/SUMMARY.md +201 -0
- package/docs/lessons/TEMPLATE.md +85 -0
- package/docs/plans/2026-02-21-code-factory-v2-design.md +204 -0
- package/docs/plans/2026-02-21-code-factory-v2-implementation-plan.md +2189 -0
- package/docs/plans/2026-02-21-code-factory-v2-phase4-design.md +537 -0
- package/docs/plans/2026-02-21-code-factory-v2-phase4-implementation-plan.md +2012 -0
- package/docs/plans/2026-02-21-hardening-pass-design.md +108 -0
- package/docs/plans/2026-02-21-hardening-pass-plan.md +1378 -0
- package/docs/plans/2026-02-21-mab-research-report.md +406 -0
- package/docs/plans/2026-02-21-marketplace-restructure-design.md +240 -0
- package/docs/plans/2026-02-21-marketplace-restructure-plan.md +832 -0
- package/docs/plans/2026-02-21-phase4-completion-plan.md +697 -0
- package/docs/plans/2026-02-21-validator-suite-design.md +148 -0
- package/docs/plans/2026-02-21-validator-suite-plan.md +540 -0
- package/docs/plans/2026-02-22-mab-research-round2.md +556 -0
- package/docs/plans/2026-02-22-mab-run-design.md +462 -0
- package/docs/plans/2026-02-22-mab-run-plan.md +2046 -0
- package/docs/plans/2026-02-22-operations-design-methodology-research.md +681 -0
- package/docs/plans/2026-02-22-research-agent-failure-taxonomy.md +532 -0
- package/docs/plans/2026-02-22-research-code-guideline-policies.md +886 -0
- package/docs/plans/2026-02-22-research-codebase-audit-refactoring.md +908 -0
- package/docs/plans/2026-02-22-research-coding-standards-documentation.md +541 -0
- package/docs/plans/2026-02-22-research-competitive-landscape.md +687 -0
- package/docs/plans/2026-02-22-research-comprehensive-testing.md +1076 -0
- package/docs/plans/2026-02-22-research-context-utilization.md +459 -0
- package/docs/plans/2026-02-22-research-cost-quality-tradeoff.md +548 -0
- package/docs/plans/2026-02-22-research-lesson-transferability.md +508 -0
- package/docs/plans/2026-02-22-research-multi-agent-coordination.md +312 -0
- package/docs/plans/2026-02-22-research-phase-integration.md +602 -0
- package/docs/plans/2026-02-22-research-plan-quality.md +428 -0
- package/docs/plans/2026-02-22-research-prompt-engineering.md +558 -0
- package/docs/plans/2026-02-22-research-unconventional-perspectives.md +528 -0
- package/docs/plans/2026-02-22-research-user-adoption.md +638 -0
- package/docs/plans/2026-02-22-research-verification-effectiveness.md +433 -0
- package/docs/plans/2026-02-23-agent-suite-design.md +299 -0
- package/docs/plans/2026-02-23-agent-suite-plan.md +578 -0
- package/docs/plans/2026-02-23-phase3-cost-infrastructure-design.md +148 -0
- package/docs/plans/2026-02-23-phase3-cost-infrastructure-plan.md +1062 -0
- package/docs/plans/2026-02-23-research-bash-expert-agent.md +543 -0
- package/docs/plans/2026-02-23-research-dependency-auditor-agent.md +564 -0
- package/docs/plans/2026-02-23-research-improving-existing-agents.md +503 -0
- package/docs/plans/2026-02-23-research-integration-tester-agent.md +454 -0
- package/docs/plans/2026-02-23-research-python-expert-agent.md +429 -0
- package/docs/plans/2026-02-23-research-service-monitor-agent.md +425 -0
- package/docs/plans/2026-02-23-research-shell-expert-agent.md +533 -0
- package/docs/plans/2026-02-23-roadmap-to-completion.md +530 -0
- package/docs/plans/2026-02-24-headless-module-split-design.md +98 -0
- package/docs/plans/2026-02-24-headless-module-split.md +443 -0
- package/docs/plans/2026-02-24-lesson-scope-metadata-design.md +228 -0
- package/docs/plans/2026-02-24-lesson-scope-metadata-plan.md +968 -0
- package/docs/plans/2026-02-24-npm-packaging-design.md +841 -0
- package/docs/plans/2026-02-24-npm-packaging-plan.md +1965 -0
- package/docs/plans/audit-findings.md +186 -0
- package/docs/telegram-notification-format.md +98 -0
- package/examples/example-plan.md +51 -0
- package/examples/example-prd.json +72 -0
- package/examples/example-roadmap.md +33 -0
- package/examples/quickstart-plan.md +63 -0
- package/hooks/hooks.json +26 -0
- package/hooks/setup-symlinks.sh +48 -0
- package/hooks/stop-hook.sh +135 -0
- package/package.json +47 -0
- package/policies/bash.md +71 -0
- package/policies/python.md +71 -0
- package/policies/testing.md +61 -0
- package/policies/universal.md +60 -0
- package/scripts/analyze-report.sh +97 -0
- package/scripts/architecture-map.sh +145 -0
- package/scripts/auto-compound.sh +273 -0
- package/scripts/batch-audit.sh +42 -0
- package/scripts/batch-test.sh +101 -0
- package/scripts/entropy-audit.sh +221 -0
- package/scripts/failure-digest.sh +51 -0
- package/scripts/generate-ast-rules.sh +96 -0
- package/scripts/init.sh +112 -0
- package/scripts/lesson-check.sh +428 -0
- package/scripts/lib/common.sh +61 -0
- package/scripts/lib/cost-tracking.sh +153 -0
- package/scripts/lib/ollama.sh +60 -0
- package/scripts/lib/progress-writer.sh +128 -0
- package/scripts/lib/run-plan-context.sh +215 -0
- package/scripts/lib/run-plan-echo-back.sh +231 -0
- package/scripts/lib/run-plan-headless.sh +396 -0
- package/scripts/lib/run-plan-notify.sh +57 -0
- package/scripts/lib/run-plan-parser.sh +81 -0
- package/scripts/lib/run-plan-prompt.sh +215 -0
- package/scripts/lib/run-plan-quality-gate.sh +132 -0
- package/scripts/lib/run-plan-routing.sh +315 -0
- package/scripts/lib/run-plan-sampling.sh +170 -0
- package/scripts/lib/run-plan-scoring.sh +146 -0
- package/scripts/lib/run-plan-state.sh +142 -0
- package/scripts/lib/run-plan-team.sh +199 -0
- package/scripts/lib/telegram.sh +54 -0
- package/scripts/lib/thompson-sampling.sh +176 -0
- package/scripts/license-check.sh +74 -0
- package/scripts/mab-run.sh +575 -0
- package/scripts/module-size-check.sh +146 -0
- package/scripts/patterns/async-no-await.yml +5 -0
- package/scripts/patterns/bare-except.yml +6 -0
- package/scripts/patterns/empty-catch.yml +6 -0
- package/scripts/patterns/hardcoded-localhost.yml +9 -0
- package/scripts/patterns/retry-loop-no-backoff.yml +12 -0
- package/scripts/pipeline-status.sh +197 -0
- package/scripts/policy-check.sh +226 -0
- package/scripts/prior-art-search.sh +133 -0
- package/scripts/promote-mab-lessons.sh +126 -0
- package/scripts/prompts/agent-a-superpowers.md +29 -0
- package/scripts/prompts/agent-b-ralph.md +29 -0
- package/scripts/prompts/judge-agent.md +61 -0
- package/scripts/prompts/planner-agent.md +44 -0
- package/scripts/pull-community-lessons.sh +90 -0
- package/scripts/quality-gate.sh +266 -0
- package/scripts/research-gate.sh +90 -0
- package/scripts/run-plan.sh +329 -0
- package/scripts/scope-infer.sh +159 -0
- package/scripts/setup-ralph-loop.sh +155 -0
- package/scripts/telemetry.sh +230 -0
- package/scripts/tests/run-all-tests.sh +52 -0
- package/scripts/tests/test-act-cli.sh +46 -0
- package/scripts/tests/test-agents-md.sh +87 -0
- package/scripts/tests/test-analyze-report.sh +114 -0
- package/scripts/tests/test-architecture-map.sh +89 -0
- package/scripts/tests/test-auto-compound.sh +169 -0
- package/scripts/tests/test-batch-test.sh +65 -0
- package/scripts/tests/test-benchmark-runner.sh +25 -0
- package/scripts/tests/test-common.sh +168 -0
- package/scripts/tests/test-cost-tracking.sh +158 -0
- package/scripts/tests/test-echo-back.sh +180 -0
- package/scripts/tests/test-entropy-audit.sh +146 -0
- package/scripts/tests/test-failure-digest.sh +66 -0
- package/scripts/tests/test-generate-ast-rules.sh +145 -0
- package/scripts/tests/test-helpers.sh +82 -0
- package/scripts/tests/test-init.sh +47 -0
- package/scripts/tests/test-lesson-check.sh +278 -0
- package/scripts/tests/test-lesson-local.sh +55 -0
- package/scripts/tests/test-license-check.sh +109 -0
- package/scripts/tests/test-mab-run.sh +182 -0
- package/scripts/tests/test-ollama-lib.sh +49 -0
- package/scripts/tests/test-ollama.sh +60 -0
- package/scripts/tests/test-pipeline-status.sh +198 -0
- package/scripts/tests/test-policy-check.sh +124 -0
- package/scripts/tests/test-prior-art-search.sh +96 -0
- package/scripts/tests/test-progress-writer.sh +140 -0
- package/scripts/tests/test-promote-mab-lessons.sh +110 -0
- package/scripts/tests/test-pull-community-lessons.sh +149 -0
- package/scripts/tests/test-quality-gate.sh +241 -0
- package/scripts/tests/test-research-gate.sh +132 -0
- package/scripts/tests/test-run-plan-cli.sh +86 -0
- package/scripts/tests/test-run-plan-context.sh +305 -0
- package/scripts/tests/test-run-plan-e2e.sh +153 -0
- package/scripts/tests/test-run-plan-headless.sh +424 -0
- package/scripts/tests/test-run-plan-notify.sh +124 -0
- package/scripts/tests/test-run-plan-parser.sh +217 -0
- package/scripts/tests/test-run-plan-prompt.sh +254 -0
- package/scripts/tests/test-run-plan-quality-gate.sh +222 -0
- package/scripts/tests/test-run-plan-routing.sh +178 -0
- package/scripts/tests/test-run-plan-scoring.sh +148 -0
- package/scripts/tests/test-run-plan-state.sh +261 -0
- package/scripts/tests/test-run-plan-team.sh +157 -0
- package/scripts/tests/test-scope-infer.sh +150 -0
- package/scripts/tests/test-setup-ralph-loop.sh +63 -0
- package/scripts/tests/test-telegram-env.sh +38 -0
- package/scripts/tests/test-telegram.sh +121 -0
- package/scripts/tests/test-telemetry.sh +46 -0
- package/scripts/tests/test-thompson-sampling.sh +139 -0
- package/scripts/tests/test-validate-all.sh +60 -0
- package/scripts/tests/test-validate-commands.sh +89 -0
- package/scripts/tests/test-validate-hooks.sh +98 -0
- package/scripts/tests/test-validate-lessons.sh +150 -0
- package/scripts/tests/test-validate-plan-quality.sh +235 -0
- package/scripts/tests/test-validate-plans.sh +187 -0
- package/scripts/tests/test-validate-plugin.sh +106 -0
- package/scripts/tests/test-validate-prd.sh +184 -0
- package/scripts/tests/test-validate-skills.sh +134 -0
- package/scripts/validate-all.sh +57 -0
- package/scripts/validate-commands.sh +67 -0
- package/scripts/validate-hooks.sh +89 -0
- package/scripts/validate-lessons.sh +98 -0
- package/scripts/validate-plan-quality.sh +369 -0
- package/scripts/validate-plans.sh +120 -0
- package/scripts/validate-plugin.sh +86 -0
- package/scripts/validate-policies.sh +42 -0
- package/scripts/validate-prd.sh +118 -0
- package/scripts/validate-skills.sh +96 -0
- package/skills/autocode/SKILL.md +285 -0
- package/skills/autocode/ab-verification.md +51 -0
- package/skills/autocode/code-quality-standards.md +37 -0
- package/skills/autocode/competitive-mode.md +364 -0
- package/skills/brainstorming/SKILL.md +97 -0
- package/skills/capture-lesson/SKILL.md +187 -0
- package/skills/check-lessons/SKILL.md +116 -0
- package/skills/dispatching-parallel-agents/SKILL.md +110 -0
- package/skills/executing-plans/SKILL.md +85 -0
- package/skills/finishing-a-development-branch/SKILL.md +201 -0
- package/skills/receiving-code-review/SKILL.md +72 -0
- package/skills/requesting-code-review/SKILL.md +59 -0
- package/skills/requesting-code-review/code-reviewer.md +82 -0
- package/skills/research/SKILL.md +145 -0
- package/skills/roadmap/SKILL.md +115 -0
- package/skills/subagent-driven-development/SKILL.md +98 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +18 -0
- package/skills/subagent-driven-development/implementer-prompt.md +73 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +57 -0
- package/skills/systematic-debugging/SKILL.md +134 -0
- package/skills/systematic-debugging/condition-based-waiting.md +64 -0
- package/skills/systematic-debugging/defense-in-depth.md +32 -0
- package/skills/systematic-debugging/root-cause-tracing.md +55 -0
- package/skills/test-driven-development/SKILL.md +167 -0
- package/skills/using-git-worktrees/SKILL.md +219 -0
- package/skills/using-superpowers/SKILL.md +54 -0
- package/skills/verification-before-completion/SKILL.md +140 -0
- package/skills/verify/SKILL.md +82 -0
- package/skills/writing-plans/SKILL.md +128 -0
- package/skills/writing-skills/SKILL.md +93 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
source "$SCRIPT_DIR/test-helpers.sh"
|
|
6
|
+
|
|
7
|
+
# Source the library under test
|
|
8
|
+
source "$SCRIPT_DIR/../lib/thompson-sampling.sh"
|
|
9
|
+
|
|
10
|
+
# --- Test: thompson_sample returns float in [0,1] ---
|
|
11
|
+
sample=$(thompson_sample 10 5)
|
|
12
|
+
TESTS=$((TESTS + 1))
|
|
13
|
+
# Check it's a valid float between 0 and 1 using bc
|
|
14
|
+
is_valid=$(echo "$sample >= 0 && $sample <= 1" | bc -l 2>/dev/null || echo "0")
|
|
15
|
+
if [[ "$is_valid" == "1" ]]; then
|
|
16
|
+
echo "PASS: thompson_sample 10 5 returns float in [0,1] (got $sample)"
|
|
17
|
+
else
|
|
18
|
+
echo "FAIL: thompson_sample 10 5 returned '$sample' (expected float in [0,1])"
|
|
19
|
+
FAILURES=$((FAILURES + 1))
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# --- Test: thompson_route returns "mab" when perf file missing ---
|
|
23
|
+
route_missing=$(thompson_route "new-file" "/tmp/nonexistent-perf-$$.json")
|
|
24
|
+
assert_eq "missing perf file → mab" "mab" "$route_missing"
|
|
25
|
+
|
|
26
|
+
# --- Test: thompson_route returns "mab" when type has < 5 data points ---
|
|
27
|
+
TEST_TMPDIR=$(mktemp -d)
|
|
28
|
+
trap 'rm -rf "$TEST_TMPDIR"' EXIT
|
|
29
|
+
|
|
30
|
+
cat > "$TEST_TMPDIR/perf.json" <<'JSON'
|
|
31
|
+
{
|
|
32
|
+
"new-file": {"superpowers": {"wins": 2, "losses": 1}, "ralph": {"wins": 1, "losses": 0}},
|
|
33
|
+
"refactoring": {"superpowers": {"wins": 0, "losses": 0}, "ralph": {"wins": 0, "losses": 0}},
|
|
34
|
+
"integration": {"superpowers": {"wins": 5, "losses": 2}, "ralph": {"wins": 3, "losses": 4}},
|
|
35
|
+
"test-only": {"superpowers": {"wins": 0, "losses": 0}, "ralph": {"wins": 0, "losses": 0}},
|
|
36
|
+
"calibration_count": 0,
|
|
37
|
+
"calibration_complete": false
|
|
38
|
+
}
|
|
39
|
+
JSON
|
|
40
|
+
|
|
41
|
+
route_few=$(thompson_route "new-file" "$TEST_TMPDIR/perf.json")
|
|
42
|
+
assert_eq "< 5 data points → mab" "mab" "$route_few"
|
|
43
|
+
|
|
44
|
+
# --- Test: thompson_route returns "mab" for integration type (always explore) ---
|
|
45
|
+
cat > "$TEST_TMPDIR/perf-int.json" <<'JSON'
|
|
46
|
+
{
|
|
47
|
+
"new-file": {"superpowers": {"wins": 10, "losses": 2}, "ralph": {"wins": 3, "losses": 9}},
|
|
48
|
+
"refactoring": {"superpowers": {"wins": 10, "losses": 2}, "ralph": {"wins": 3, "losses": 9}},
|
|
49
|
+
"integration": {"superpowers": {"wins": 10, "losses": 2}, "ralph": {"wins": 3, "losses": 9}},
|
|
50
|
+
"test-only": {"superpowers": {"wins": 10, "losses": 2}, "ralph": {"wins": 3, "losses": 9}},
|
|
51
|
+
"calibration_count": 10,
|
|
52
|
+
"calibration_complete": true
|
|
53
|
+
}
|
|
54
|
+
JSON
|
|
55
|
+
|
|
56
|
+
route_int=$(thompson_route "integration" "$TEST_TMPDIR/perf-int.json")
|
|
57
|
+
assert_eq "integration type → always mab" "mab" "$route_int"
|
|
58
|
+
|
|
59
|
+
# --- Test: thompson_route returns winning strategy with strong signal ---
|
|
60
|
+
# superpowers: 15 wins / 3 losses = 83% win rate, 18 total points
|
|
61
|
+
# ralph: 3 wins / 15 losses = 17% win rate
|
|
62
|
+
# Run 10 times and check majority goes to superpowers
|
|
63
|
+
superpowers_count=0
|
|
64
|
+
for i in $(seq 1 10); do
|
|
65
|
+
result=$(thompson_route "new-file" "$TEST_TMPDIR/perf-int.json")
|
|
66
|
+
if [[ "$result" == "superpowers" ]]; then
|
|
67
|
+
superpowers_count=$((superpowers_count + 1))
|
|
68
|
+
fi
|
|
69
|
+
done
|
|
70
|
+
|
|
71
|
+
TESTS=$((TESTS + 1))
|
|
72
|
+
if [[ "$superpowers_count" -ge 7 ]]; then
|
|
73
|
+
echo "PASS: strong signal routes to winner (superpowers won $superpowers_count/10)"
|
|
74
|
+
else
|
|
75
|
+
echo "FAIL: strong signal should route to superpowers ≥7/10 times (got $superpowers_count/10)"
|
|
76
|
+
FAILURES=$((FAILURES + 1))
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# --- Test: init_strategy_perf creates valid JSON ---
|
|
80
|
+
init_file="$TEST_TMPDIR/init-perf.json"
|
|
81
|
+
init_strategy_perf "$init_file"
|
|
82
|
+
|
|
83
|
+
TESTS=$((TESTS + 1))
|
|
84
|
+
if [[ -f "$init_file" ]] && jq . "$init_file" > /dev/null 2>&1; then
|
|
85
|
+
echo "PASS: init_strategy_perf creates valid JSON"
|
|
86
|
+
else
|
|
87
|
+
echo "FAIL: init_strategy_perf did not create valid JSON"
|
|
88
|
+
FAILURES=$((FAILURES + 1))
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
# Check all 4 batch types present
|
|
92
|
+
for bt in "new-file" "refactoring" "integration" "test-only"; do
|
|
93
|
+
has_type=$(jq --arg bt "$bt" 'has($bt)' "$init_file" 2>/dev/null)
|
|
94
|
+
assert_eq "init has $bt type" "true" "$has_type"
|
|
95
|
+
done
|
|
96
|
+
|
|
97
|
+
# Check calibration fields
|
|
98
|
+
has_cal_count=$(jq 'has("calibration_count")' "$init_file" 2>/dev/null)
|
|
99
|
+
assert_eq "init has calibration_count" "true" "$has_cal_count"
|
|
100
|
+
|
|
101
|
+
has_cal_complete=$(jq 'has("calibration_complete")' "$init_file" 2>/dev/null)
|
|
102
|
+
assert_eq "init has calibration_complete" "true" "$has_cal_complete"
|
|
103
|
+
|
|
104
|
+
# --- Test: update_strategy_perf increments wins/losses ---
|
|
105
|
+
update_file="$TEST_TMPDIR/update-perf.json"
|
|
106
|
+
init_strategy_perf "$update_file"
|
|
107
|
+
|
|
108
|
+
# Superpowers wins a new-file batch
|
|
109
|
+
update_strategy_perf "$update_file" "new-file" "superpowers"
|
|
110
|
+
sp_wins=$(jq '."new-file".superpowers.wins' "$update_file" 2>/dev/null)
|
|
111
|
+
assert_eq "superpowers wins incremented" "1" "$sp_wins"
|
|
112
|
+
|
|
113
|
+
# Ralph loses (the other side)
|
|
114
|
+
ralph_losses=$(jq '."new-file".ralph.losses' "$update_file" 2>/dev/null)
|
|
115
|
+
assert_eq "ralph losses incremented" "1" "$ralph_losses"
|
|
116
|
+
|
|
117
|
+
# Do it again
|
|
118
|
+
update_strategy_perf "$update_file" "new-file" "superpowers"
|
|
119
|
+
sp_wins2=$(jq '."new-file".superpowers.wins' "$update_file" 2>/dev/null)
|
|
120
|
+
assert_eq "superpowers wins incremented again" "2" "$sp_wins2"
|
|
121
|
+
|
|
122
|
+
# --- Test: thompson_route spread-too-close returns "mab" ---
|
|
123
|
+
# When both strategies have similar win rates (spread < 15%), route to mab.
|
|
124
|
+
# superpowers: 6W/4L = 60%, ralph: 5W/5L = 50% → spread = 10% < 15% → "mab"
|
|
125
|
+
cat > "$TEST_TMPDIR/perf-close.json" <<'JSON'
|
|
126
|
+
{
|
|
127
|
+
"new-file": {"superpowers": {"wins": 6, "losses": 4}, "ralph": {"wins": 5, "losses": 5}},
|
|
128
|
+
"refactoring": {"superpowers": {"wins": 6, "losses": 4}, "ralph": {"wins": 5, "losses": 5}},
|
|
129
|
+
"integration": {"superpowers": {"wins": 6, "losses": 4}, "ralph": {"wins": 5, "losses": 5}},
|
|
130
|
+
"test-only": {"superpowers": {"wins": 6, "losses": 4}, "ralph": {"wins": 5, "losses": 5}},
|
|
131
|
+
"calibration_count": 20,
|
|
132
|
+
"calibration_complete": true
|
|
133
|
+
}
|
|
134
|
+
JSON
|
|
135
|
+
|
|
136
|
+
route_close=$(thompson_route "new-file" "$TEST_TMPDIR/perf-close.json")
|
|
137
|
+
assert_eq "spread < 15% → mab (too close to call)" "mab" "$route_close"
|
|
138
|
+
|
|
139
|
+
report_results
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Test validate-all.sh
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
source "$SCRIPT_DIR/test-helpers.sh"
|
|
7
|
+
|
|
8
|
+
VALIDATOR="$SCRIPT_DIR/../validate-all.sh"
|
|
9
|
+
|
|
10
|
+
# === Test: Runs on actual toolkit with --warn and reports summary ===
|
|
11
|
+
output=$(bash "$VALIDATOR" --warn 2>&1 || echo "EXIT:$?")
|
|
12
|
+
assert_contains "actual toolkit --warn: shows summary" "validators passed" "$output"
|
|
13
|
+
assert_not_contains "actual toolkit --warn: exit 0" "EXIT:" "$output"
|
|
14
|
+
|
|
15
|
+
# === Test: Reports individual validator names ===
|
|
16
|
+
output=$(bash "$VALIDATOR" --warn 2>&1 || echo "EXIT:$?")
|
|
17
|
+
assert_contains "shows lessons" "validate-lessons" "$output"
|
|
18
|
+
assert_contains "shows skills" "validate-skills" "$output"
|
|
19
|
+
assert_contains "shows commands" "validate-commands" "$output"
|
|
20
|
+
assert_contains "shows plugin" "validate-plugin" "$output"
|
|
21
|
+
assert_contains "shows hooks" "validate-hooks" "$output"
|
|
22
|
+
|
|
23
|
+
# === Test: Actual toolkit passes strict (no --warn) ===
|
|
24
|
+
output=$(bash "$VALIDATOR" 2>&1 || echo "EXIT:$?")
|
|
25
|
+
assert_contains "actual toolkit strict: shows summary" "validators passed" "$output"
|
|
26
|
+
assert_not_contains "actual toolkit strict: exit 0" "EXIT:" "$output"
|
|
27
|
+
|
|
28
|
+
# === Test: --help exits 0 ===
|
|
29
|
+
output=$(bash "$VALIDATOR" --help 2>&1 || echo "EXIT:$?")
|
|
30
|
+
assert_contains "--help: shows usage" "Usage:" "$output"
|
|
31
|
+
assert_not_contains "--help: exit 0" "EXIT:" "$output"
|
|
32
|
+
|
|
33
|
+
# === Test: No args (empty PASS_ARGS) works under set -u (#23) ===
|
|
34
|
+
# This specifically tests that empty array expansion doesn't error under set -u
|
|
35
|
+
output=$(bash "$VALIDATOR" --warn 2>&1; echo "EXIT:$?")
|
|
36
|
+
assert_contains "empty PASS_ARGS: succeeds" "EXIT:0" "$output"
|
|
37
|
+
|
|
38
|
+
# === Test: Reports FAIL and exits 1 when a validator fails ===
|
|
39
|
+
# Create a controlled fixture with an invalid skill to trigger failure
|
|
40
|
+
TMP_DIR=$(mktemp -d)
|
|
41
|
+
trap 'rm -rf "$TMP_DIR"' EXIT
|
|
42
|
+
# Create a minimal toolkit structure with a broken skill
|
|
43
|
+
mkdir -p "$TMP_DIR/skills/broken-skill"
|
|
44
|
+
# Empty dir = missing SKILL.md → validate-skills fails
|
|
45
|
+
# Create stub validators that pass, except skills points to our broken fixture
|
|
46
|
+
mkdir -p "$TMP_DIR/scripts"
|
|
47
|
+
for v in validate-lessons validate-commands validate-plugin validate-hooks; do
|
|
48
|
+
echo '#!/usr/bin/env bash' > "$TMP_DIR/scripts/${v}.sh"
|
|
49
|
+
echo 'echo "'"$v"': PASS"; exit 0' >> "$TMP_DIR/scripts/${v}.sh"
|
|
50
|
+
done
|
|
51
|
+
# validate-skills uses SKILLS_DIR env — point to broken fixture
|
|
52
|
+
echo '#!/usr/bin/env bash' > "$TMP_DIR/scripts/validate-skills.sh"
|
|
53
|
+
echo 'echo "broken-skill: Missing SKILL.md"; echo "validate-skills: FAIL"; exit 1' >> "$TMP_DIR/scripts/validate-skills.sh"
|
|
54
|
+
# Copy validate-all.sh to the temp dir
|
|
55
|
+
cp "$VALIDATOR" "$TMP_DIR/scripts/validate-all.sh"
|
|
56
|
+
output=$(bash "$TMP_DIR/scripts/validate-all.sh" 2>&1 || echo "EXIT:$?")
|
|
57
|
+
assert_contains "reports failures: exit 1" "EXIT:1" "$output"
|
|
58
|
+
assert_contains "reports failures: shows Failed" "Failed:" "$output"
|
|
59
|
+
|
|
60
|
+
report_results
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Test validate-commands.sh
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
source "$SCRIPT_DIR/test-helpers.sh"
|
|
7
|
+
|
|
8
|
+
VALIDATOR="$SCRIPT_DIR/../validate-commands.sh"
|
|
9
|
+
WORK=$(mktemp -d)
|
|
10
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
11
|
+
|
|
12
|
+
# Helper: create a command file
|
|
13
|
+
create_command() {
|
|
14
|
+
local name="$1" content="$2"
|
|
15
|
+
mkdir -p "$WORK/commands"
|
|
16
|
+
echo "$content" > "$WORK/commands/$name"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# Helper: run validator against temp commands dir
|
|
20
|
+
run_validator() {
|
|
21
|
+
local exit_code=0
|
|
22
|
+
COMMANDS_DIR="$WORK/commands" bash "$VALIDATOR" "$@" 2>&1 || exit_code=$?
|
|
23
|
+
echo "EXIT:$exit_code"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# === Test: Valid command passes ===
|
|
27
|
+
rm -rf "$WORK/commands"
|
|
28
|
+
create_command "my-command.md" '---
|
|
29
|
+
description: "A test command"
|
|
30
|
+
---
|
|
31
|
+
Body text here.'
|
|
32
|
+
|
|
33
|
+
output=$(run_validator)
|
|
34
|
+
assert_contains "valid command: PASS" "validate-commands: PASS" "$output"
|
|
35
|
+
assert_contains "valid command: exit 0" "EXIT:0" "$output"
|
|
36
|
+
|
|
37
|
+
# === Test: Missing description field fails ===
|
|
38
|
+
rm -rf "$WORK/commands"
|
|
39
|
+
create_command "no-desc.md" '---
|
|
40
|
+
argument-hint: "<thing>"
|
|
41
|
+
---
|
|
42
|
+
Body.'
|
|
43
|
+
|
|
44
|
+
output=$(run_validator)
|
|
45
|
+
assert_contains "missing description: reports violation" "Missing required field: description" "$output"
|
|
46
|
+
assert_contains "missing description: exit 1" "EXIT:1" "$output"
|
|
47
|
+
|
|
48
|
+
# === Test: Missing frontmatter start fails ===
|
|
49
|
+
rm -rf "$WORK/commands"
|
|
50
|
+
create_command "no-front.md" 'description: "No frontmatter markers"
|
|
51
|
+
Some body.'
|
|
52
|
+
|
|
53
|
+
output=$(run_validator)
|
|
54
|
+
assert_contains "missing ---: reports violation" "First line must be '---'" "$output"
|
|
55
|
+
assert_contains "missing ---: exit 1" "EXIT:1" "$output"
|
|
56
|
+
|
|
57
|
+
# === Test: Missing closing --- fails ===
|
|
58
|
+
rm -rf "$WORK/commands"
|
|
59
|
+
create_command "no-close.md" '---
|
|
60
|
+
description: "Unclosed frontmatter"
|
|
61
|
+
Body text without closing delimiter.'
|
|
62
|
+
|
|
63
|
+
output=$(run_validator)
|
|
64
|
+
assert_contains "missing close ---: reports violation" "Frontmatter not closed" "$output"
|
|
65
|
+
assert_contains "missing close ---: exit 1" "EXIT:1" "$output"
|
|
66
|
+
|
|
67
|
+
# === Test: --warn exits 0 even with violations ===
|
|
68
|
+
rm -rf "$WORK/commands"
|
|
69
|
+
create_command "warn-test.md" '---
|
|
70
|
+
argument-hint: "<thing>"
|
|
71
|
+
---
|
|
72
|
+
Body.'
|
|
73
|
+
|
|
74
|
+
output=$(run_validator --warn)
|
|
75
|
+
assert_contains "--warn: still reports violation" "Missing required field: description" "$output"
|
|
76
|
+
assert_contains "--warn: exits 0" "EXIT:0" "$output"
|
|
77
|
+
|
|
78
|
+
# === Test: --help exits 0 ===
|
|
79
|
+
output=$(run_validator --help)
|
|
80
|
+
assert_contains "--help: shows usage" "Usage:" "$output"
|
|
81
|
+
assert_contains "--help: exits 0" "EXIT:0" "$output"
|
|
82
|
+
|
|
83
|
+
# === Test: Missing commands directory fails ===
|
|
84
|
+
rm -rf "$WORK/commands"
|
|
85
|
+
output=$(COMMANDS_DIR="$WORK/nonexistent" bash "$VALIDATOR" 2>&1 || echo "EXIT:$?")
|
|
86
|
+
assert_contains "missing dir: error message" "commands directory not found" "$output"
|
|
87
|
+
assert_contains "missing dir: exit 1" "EXIT:1" "$output"
|
|
88
|
+
|
|
89
|
+
report_results
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Test validate-hooks.sh
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
source "$SCRIPT_DIR/test-helpers.sh"
|
|
7
|
+
|
|
8
|
+
VALIDATOR="$SCRIPT_DIR/../validate-hooks.sh"
|
|
9
|
+
WORK=$(mktemp -d)
|
|
10
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
11
|
+
|
|
12
|
+
# Helper: create hooks dir with hooks.json
|
|
13
|
+
create_hooks() {
|
|
14
|
+
local content="$1"
|
|
15
|
+
mkdir -p "$WORK/hooks"
|
|
16
|
+
echo "$content" > "$WORK/hooks/hooks.json"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# Helper: create a script file (optionally executable)
|
|
20
|
+
create_script() {
|
|
21
|
+
local path="$1" executable="${2:-true}"
|
|
22
|
+
mkdir -p "$(dirname "$WORK/$path")"
|
|
23
|
+
echo '#!/usr/bin/env bash' > "$WORK/$path"
|
|
24
|
+
if [[ "$executable" == "true" ]]; then chmod +x "$WORK/$path"; fi
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Helper: run validator against temp dir
|
|
28
|
+
run_validator() {
|
|
29
|
+
local exit_code=0
|
|
30
|
+
HOOKS_DIR="$WORK/hooks" TOOLKIT_ROOT="$WORK" bash "$VALIDATOR" "$@" 2>&1 || exit_code=$?
|
|
31
|
+
echo "EXIT:$exit_code"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# === Test: Valid hooks.json with existing executable script passes ===
|
|
35
|
+
create_hooks '{
|
|
36
|
+
"hooks": {
|
|
37
|
+
"Stop": [{"hooks": [{"type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh"}]}]
|
|
38
|
+
}
|
|
39
|
+
}'
|
|
40
|
+
create_script "hooks/stop-hook.sh"
|
|
41
|
+
|
|
42
|
+
output=$(run_validator)
|
|
43
|
+
assert_contains "valid hooks: PASS" "validate-hooks: PASS" "$output"
|
|
44
|
+
assert_contains "valid hooks: exit 0" "EXIT:0" "$output"
|
|
45
|
+
|
|
46
|
+
# === Test: Nonexistent script fails ===
|
|
47
|
+
create_hooks '{
|
|
48
|
+
"hooks": {
|
|
49
|
+
"Stop": [{"hooks": [{"type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/missing.sh"}]}]
|
|
50
|
+
}
|
|
51
|
+
}'
|
|
52
|
+
|
|
53
|
+
output=$(run_validator)
|
|
54
|
+
assert_contains "missing script: reports violation" "script not found" "$output"
|
|
55
|
+
assert_contains "missing script: exit 1" "EXIT:1" "$output"
|
|
56
|
+
|
|
57
|
+
# === Test: Non-executable script fails ===
|
|
58
|
+
create_hooks '{
|
|
59
|
+
"hooks": {
|
|
60
|
+
"Stop": [{"hooks": [{"type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/not-exec.sh"}]}]
|
|
61
|
+
}
|
|
62
|
+
}'
|
|
63
|
+
create_script "hooks/not-exec.sh" "false"
|
|
64
|
+
|
|
65
|
+
output=$(run_validator)
|
|
66
|
+
assert_contains "not executable: reports violation" "not executable" "$output"
|
|
67
|
+
assert_contains "not executable: exit 1" "EXIT:1" "$output"
|
|
68
|
+
|
|
69
|
+
# === Test: Invalid JSON fails ===
|
|
70
|
+
create_hooks '{invalid json'
|
|
71
|
+
|
|
72
|
+
output=$(run_validator)
|
|
73
|
+
assert_contains "invalid JSON: error" "hooks.json is not valid JSON" "$output"
|
|
74
|
+
assert_contains "invalid JSON: exit 1" "EXIT:1" "$output"
|
|
75
|
+
|
|
76
|
+
# === Test: Missing hooks.json fails ===
|
|
77
|
+
rm -rf "$WORK/hooks"
|
|
78
|
+
output=$(HOOKS_DIR="$WORK/nonexistent" TOOLKIT_ROOT="$WORK" bash "$VALIDATOR" 2>&1 || echo "EXIT:$?")
|
|
79
|
+
assert_contains "missing dir: error message" "hooks directory not found" "$output"
|
|
80
|
+
assert_contains "missing dir: exit 1" "EXIT:1" "$output"
|
|
81
|
+
|
|
82
|
+
# === Test: --warn exits 0 even with violations ===
|
|
83
|
+
create_hooks '{
|
|
84
|
+
"hooks": {
|
|
85
|
+
"Stop": [{"hooks": [{"type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/missing.sh"}]}]
|
|
86
|
+
}
|
|
87
|
+
}'
|
|
88
|
+
|
|
89
|
+
output=$(run_validator --warn)
|
|
90
|
+
assert_contains "--warn: still reports violation" "script not found" "$output"
|
|
91
|
+
assert_contains "--warn: exits 0" "EXIT:0" "$output"
|
|
92
|
+
|
|
93
|
+
# === Test: --help exits 0 ===
|
|
94
|
+
output=$(run_validator --help)
|
|
95
|
+
assert_contains "--help: shows usage" "Usage:" "$output"
|
|
96
|
+
assert_contains "--help: exits 0" "EXIT:0" "$output"
|
|
97
|
+
|
|
98
|
+
report_results
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Test validate-lessons.sh
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
source "$SCRIPT_DIR/test-helpers.sh"
|
|
7
|
+
|
|
8
|
+
VALIDATOR="$SCRIPT_DIR/../validate-lessons.sh"
|
|
9
|
+
WORK=$(mktemp -d)
|
|
10
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
11
|
+
|
|
12
|
+
# Helper: create a lesson file in the temp lessons dir
|
|
13
|
+
create_lesson() {
|
|
14
|
+
local name="$1" content="$2"
|
|
15
|
+
local dir="$WORK/lessons"
|
|
16
|
+
mkdir -p "$dir"
|
|
17
|
+
echo "$content" > "$dir/$name"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# Helper: run validator against temp lessons dir
|
|
21
|
+
run_validator() {
|
|
22
|
+
local exit_code=0
|
|
23
|
+
LESSONS_DIR="$WORK/lessons" bash "$VALIDATOR" "$@" 2>&1 || exit_code=$?
|
|
24
|
+
echo "EXIT:$exit_code"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# === Test: Valid lesson passes ===
|
|
28
|
+
rm -rf "$WORK/lessons"
|
|
29
|
+
create_lesson "0001-test.md" '---
|
|
30
|
+
id: 1
|
|
31
|
+
title: "Test lesson"
|
|
32
|
+
severity: blocker
|
|
33
|
+
languages: [python]
|
|
34
|
+
pattern:
|
|
35
|
+
type: syntactic
|
|
36
|
+
regex: "foo"
|
|
37
|
+
---
|
|
38
|
+
Body text here.'
|
|
39
|
+
|
|
40
|
+
output=$(run_validator)
|
|
41
|
+
assert_contains "valid lesson: PASS" "validate-lessons: PASS" "$output"
|
|
42
|
+
assert_contains "valid lesson: exit 0" "EXIT:0" "$output"
|
|
43
|
+
|
|
44
|
+
# === Test: Missing --- start line fails ===
|
|
45
|
+
rm -rf "$WORK/lessons"
|
|
46
|
+
create_lesson "0002-bad-start.md" 'id: 1
|
|
47
|
+
title: "No frontmatter marker"
|
|
48
|
+
severity: blocker
|
|
49
|
+
languages: [python]
|
|
50
|
+
---'
|
|
51
|
+
|
|
52
|
+
output=$(run_validator)
|
|
53
|
+
assert_contains "missing ---: reports violation" "First line must be" "$output"
|
|
54
|
+
assert_contains "missing ---: FAIL" "FAIL" "$output"
|
|
55
|
+
assert_contains "missing ---: exit 1" "EXIT:1" "$output"
|
|
56
|
+
|
|
57
|
+
# === Test: Missing required field fails ===
|
|
58
|
+
rm -rf "$WORK/lessons"
|
|
59
|
+
create_lesson "0003-missing-field.md" '---
|
|
60
|
+
id: 3
|
|
61
|
+
severity: blocker
|
|
62
|
+
languages: [python]
|
|
63
|
+
pattern:
|
|
64
|
+
type: semantic
|
|
65
|
+
---'
|
|
66
|
+
|
|
67
|
+
output=$(run_validator)
|
|
68
|
+
assert_contains "missing title: reports violation" "Missing required field: title" "$output"
|
|
69
|
+
assert_contains "missing title: exit 1" "EXIT:1" "$output"
|
|
70
|
+
|
|
71
|
+
# === Test: Duplicate IDs fail ===
|
|
72
|
+
rm -rf "$WORK/lessons"
|
|
73
|
+
create_lesson "0004-dup-a.md" '---
|
|
74
|
+
id: 42
|
|
75
|
+
title: "First"
|
|
76
|
+
severity: blocker
|
|
77
|
+
languages: [python]
|
|
78
|
+
pattern:
|
|
79
|
+
type: semantic
|
|
80
|
+
---'
|
|
81
|
+
create_lesson "0005-dup-b.md" '---
|
|
82
|
+
id: 42
|
|
83
|
+
title: "Second"
|
|
84
|
+
severity: blocker
|
|
85
|
+
languages: [python]
|
|
86
|
+
pattern:
|
|
87
|
+
type: semantic
|
|
88
|
+
---'
|
|
89
|
+
|
|
90
|
+
output=$(run_validator)
|
|
91
|
+
assert_contains "duplicate IDs: reports violation" "Duplicate lesson ID: 42" "$output"
|
|
92
|
+
assert_contains "duplicate IDs: exit 1" "EXIT:1" "$output"
|
|
93
|
+
|
|
94
|
+
# === Test: Invalid severity fails ===
|
|
95
|
+
rm -rf "$WORK/lessons"
|
|
96
|
+
create_lesson "0006-bad-severity.md" '---
|
|
97
|
+
id: 6
|
|
98
|
+
title: "Bad severity"
|
|
99
|
+
severity: critical
|
|
100
|
+
languages: [python]
|
|
101
|
+
pattern:
|
|
102
|
+
type: semantic
|
|
103
|
+
---'
|
|
104
|
+
|
|
105
|
+
output=$(run_validator)
|
|
106
|
+
assert_contains "invalid severity: reports violation" "Invalid severity" "$output"
|
|
107
|
+
assert_contains "invalid severity: exit 1" "EXIT:1" "$output"
|
|
108
|
+
|
|
109
|
+
# === Test: Syntactic without regex fails ===
|
|
110
|
+
rm -rf "$WORK/lessons"
|
|
111
|
+
create_lesson "0007-no-regex.md" '---
|
|
112
|
+
id: 7
|
|
113
|
+
title: "Missing regex"
|
|
114
|
+
severity: blocker
|
|
115
|
+
languages: [python]
|
|
116
|
+
pattern:
|
|
117
|
+
type: syntactic
|
|
118
|
+
---'
|
|
119
|
+
|
|
120
|
+
output=$(run_validator)
|
|
121
|
+
assert_contains "syntactic no regex: reports violation" "Syntactic lesson missing regex field" "$output"
|
|
122
|
+
assert_contains "syntactic no regex: exit 1" "EXIT:1" "$output"
|
|
123
|
+
|
|
124
|
+
# === Test: --warn exits 0 even with violations ===
|
|
125
|
+
rm -rf "$WORK/lessons"
|
|
126
|
+
create_lesson "0008-warn-test.md" '---
|
|
127
|
+
id: 8
|
|
128
|
+
severity: blocker
|
|
129
|
+
languages: [python]
|
|
130
|
+
pattern:
|
|
131
|
+
type: semantic
|
|
132
|
+
---'
|
|
133
|
+
|
|
134
|
+
output=$(run_validator --warn)
|
|
135
|
+
assert_contains "--warn: still reports violation" "Missing required field: title" "$output"
|
|
136
|
+
assert_contains "--warn: exits 0" "EXIT:0" "$output"
|
|
137
|
+
|
|
138
|
+
# === Test: --help exits 0 ===
|
|
139
|
+
output=$(run_validator --help)
|
|
140
|
+
assert_contains "--help: shows usage" "Usage:" "$output"
|
|
141
|
+
assert_contains "--help: exits 0" "EXIT:0" "$output"
|
|
142
|
+
|
|
143
|
+
# === Test: Missing lessons directory fails ===
|
|
144
|
+
SAVE_DIR="$WORK/lessons"
|
|
145
|
+
rm -rf "$WORK/lessons"
|
|
146
|
+
output=$(LESSONS_DIR="$WORK/nonexistent" bash "$VALIDATOR" 2>&1 || echo "EXIT:$?")
|
|
147
|
+
assert_contains "missing dir: error message" "lessons directory not found" "$output"
|
|
148
|
+
assert_contains "missing dir: exit 1" "EXIT:1" "$output"
|
|
149
|
+
|
|
150
|
+
report_results
|