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,1378 @@
|
|
|
1
|
+
# Full-Coverage Hardening Pass Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
4
|
+
|
|
5
|
+
**Goal:** Audit all code for quality issues, fix findings, and add comprehensive tests to close every coverage gap in the toolkit.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Two-phase approach — Phase 1 audits with shellcheck + schema validation + smoke tests, Phase 2 writes permanent test suites. Test files follow the existing bash framework (`assert_exit`/`assert_output_contains` helpers). The test runner at `scripts/tests/run-all-tests.sh` discovers files matching `test-*.sh` glob.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** bash, shellcheck 0.9.0, grep -P (PCRE), jq, awk
|
|
10
|
+
|
|
11
|
+
**Quality Gates:** `bash scripts/tests/run-all-tests.sh` — must maintain 109+ existing tests passing and add 60+ new tests.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Batch 1: Static Analysis — Shellcheck + Lesson Scanner
|
|
16
|
+
|
|
17
|
+
### Task 1: Run shellcheck against all scripts and catalog findings
|
|
18
|
+
|
|
19
|
+
**Files:**
|
|
20
|
+
- Read: `scripts/*.sh`, `scripts/lib/*.sh`, `hooks/stop-hook.sh`
|
|
21
|
+
- Create: `docs/plans/audit-findings.md`
|
|
22
|
+
|
|
23
|
+
**Step 1: Run shellcheck against all scripts**
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
shellcheck -s bash -f gcc scripts/run-plan.sh scripts/lesson-check.sh scripts/quality-gate.sh scripts/setup-ralph-loop.sh scripts/auto-compound.sh scripts/entropy-audit.sh scripts/batch-audit.sh scripts/batch-test.sh scripts/lib/*.sh hooks/stop-hook.sh 2>&1 | tee /tmp/shellcheck-findings.txt
|
|
27
|
+
echo "Exit code: $?"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Expected: Some warnings (SC2086 word splitting, SC2155 declare/assign, etc.)
|
|
31
|
+
|
|
32
|
+
**Step 2: Catalog all findings**
|
|
33
|
+
|
|
34
|
+
Create `docs/plans/audit-findings.md` with:
|
|
35
|
+
- Each finding: file:line, shellcheck code, severity, description
|
|
36
|
+
- Group by severity (error, warning, info)
|
|
37
|
+
- For each finding, add a disposition: FIX or SUPPRESS (with justification)
|
|
38
|
+
|
|
39
|
+
**Step 3: Commit findings**
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
git add docs/plans/audit-findings.md
|
|
43
|
+
git commit -m "docs: add shellcheck audit findings"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Task 2: Run lesson-scanner agent against toolkit's own codebase
|
|
47
|
+
|
|
48
|
+
**Files:**
|
|
49
|
+
- Read: `docs/lessons/*.md` (lesson definitions)
|
|
50
|
+
- Modify: `docs/plans/audit-findings.md` (append lesson scanner results)
|
|
51
|
+
|
|
52
|
+
**Step 1: Run lesson-check.sh against all shell scripts**
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
scripts/lesson-check.sh scripts/*.sh scripts/lib/*.sh hooks/stop-hook.sh
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Expected: Clean (these are bash, lessons target python/js). Record result.
|
|
59
|
+
|
|
60
|
+
**Step 2: Run lesson-check.sh against any Python/JS in examples**
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
find . -name "*.py" -o -name "*.js" -o -name "*.ts" | grep -v node_modules | grep -v .venv | xargs scripts/lesson-check.sh 2>/dev/null || echo "No matching files"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Step 3: Append results to audit-findings.md and commit**
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git add docs/plans/audit-findings.md
|
|
70
|
+
git commit -m "docs: add lesson scanner results to audit findings"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Batch 2: Schema Validation
|
|
76
|
+
|
|
77
|
+
### Task 3: Validate all lesson file YAML frontmatter
|
|
78
|
+
|
|
79
|
+
**Files:**
|
|
80
|
+
- Read: `docs/lessons/0001-*.md` through `docs/lessons/0006-*.md`
|
|
81
|
+
- Read: `docs/lessons/TEMPLATE.md` (schema reference)
|
|
82
|
+
- Modify: `docs/plans/audit-findings.md`
|
|
83
|
+
|
|
84
|
+
**Step 1: For each lesson file, verify required YAML fields exist**
|
|
85
|
+
|
|
86
|
+
Required fields per TEMPLATE.md:
|
|
87
|
+
- `id` — integer
|
|
88
|
+
- `title` — non-empty string
|
|
89
|
+
- `severity` — one of: blocker, should-fix, nice-to-have
|
|
90
|
+
- `languages` — array like `[python, javascript]` or `all`
|
|
91
|
+
- `category` — one of: async-traps, resource-lifecycle, silent-failures, integration-boundaries, test-anti-patterns, performance
|
|
92
|
+
- `pattern.type` — one of: syntactic, semantic
|
|
93
|
+
- `pattern.regex` — non-empty string (required if type=syntactic)
|
|
94
|
+
- `pattern.description` — non-empty string
|
|
95
|
+
- `fix` — non-empty string
|
|
96
|
+
- `example.bad` — non-empty
|
|
97
|
+
- `example.good` — non-empty
|
|
98
|
+
|
|
99
|
+
For each file, extract frontmatter and verify each field. Use awk:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
for f in docs/lessons/0*.md; do
|
|
103
|
+
echo "=== $f ==="
|
|
104
|
+
# Extract and display all top-level fields
|
|
105
|
+
awk '/^---$/{c++; if(c==2) exit} c==1 && !/^---$/{print}' "$f"
|
|
106
|
+
echo ""
|
|
107
|
+
done
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Step 2: Validate regex patterns are valid grep -P**
|
|
111
|
+
|
|
112
|
+
For each syntactic lesson, test the regex compiles:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
for f in docs/lessons/0*.md; do
|
|
116
|
+
regex=$(awk 'BEGIN{ip=0} /^---$/{c++; if(c==2) exit} c==1 && /^pattern:/{ip=1;next} ip && /^[^[:space:]]/{ip=0} ip && /^[[:space:]]+regex:/{sub(/^[[:space:]]+regex:[[:space:]]+/,""); gsub(/^["'"'"']|["'"'"']$/,""); print}' "$f")
|
|
117
|
+
if [[ -n "$regex" ]]; then
|
|
118
|
+
# Unescape double backslashes (YAML stores \\s, we need \s)
|
|
119
|
+
regex="${regex//\\\\/\\}"
|
|
120
|
+
if echo "" | grep -P "$regex" >/dev/null 2>&1 || [[ $? -le 1 ]]; then
|
|
121
|
+
echo "PASS: $(basename "$f") regex compiles: $regex"
|
|
122
|
+
else
|
|
123
|
+
echo "FAIL: $(basename "$f") invalid regex: $regex"
|
|
124
|
+
fi
|
|
125
|
+
fi
|
|
126
|
+
done
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Step 3: Append validation results to audit-findings.md and commit**
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
git add docs/plans/audit-findings.md
|
|
133
|
+
git commit -m "docs: add lesson schema validation to audit findings"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Task 4: Validate plugin manifests, hooks.json, skill frontmatters, and command files
|
|
137
|
+
|
|
138
|
+
**Files:**
|
|
139
|
+
- Read: `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`
|
|
140
|
+
- Read: `hooks/hooks.json`
|
|
141
|
+
- Read: `skills/*/SKILL.md` (all 15)
|
|
142
|
+
- Read: `commands/*.md` (all 6)
|
|
143
|
+
- Modify: `docs/plans/audit-findings.md`
|
|
144
|
+
|
|
145
|
+
**Step 1: Validate JSON files with jq**
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
jq . .claude-plugin/plugin.json >/dev/null && echo "PASS: plugin.json valid JSON"
|
|
149
|
+
jq . .claude-plugin/marketplace.json >/dev/null && echo "PASS: marketplace.json valid JSON"
|
|
150
|
+
jq . hooks/hooks.json >/dev/null && echo "PASS: hooks.json valid JSON"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Step 2: Check plugin.json has required fields**
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
jq -e '.name and .description and .version and .author' .claude-plugin/plugin.json >/dev/null && echo "PASS: plugin.json has required fields"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Step 3: Check all 15 skills have YAML frontmatter with name, description, version**
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
for f in skills/*/SKILL.md; do
|
|
163
|
+
name=$(awk '/^---$/{c++; if(c==2) exit} c==1 && /^name:/{sub(/^name:[[:space:]]+/,""); print}' "$f")
|
|
164
|
+
desc=$(awk '/^---$/{c++; if(c==2) exit} c==1 && /^description:/{sub(/^description:[[:space:]]+/,""); print}' "$f")
|
|
165
|
+
ver=$(awk '/^---$/{c++; if(c==2) exit} c==1 && /^version:/{sub(/^version:[[:space:]]+/,""); print}' "$f")
|
|
166
|
+
if [[ -n "$name" && -n "$desc" && -n "$ver" ]]; then
|
|
167
|
+
echo "PASS: $f (name=$name, version=$ver)"
|
|
168
|
+
else
|
|
169
|
+
echo "FAIL: $f missing: ${name:+}${name:-name }${desc:+}${desc:-desc }${ver:+}${ver:-version}"
|
|
170
|
+
fi
|
|
171
|
+
done
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Step 4: Check all 6 commands have frontmatter with name and description**
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
for f in commands/*.md; do
|
|
178
|
+
has_fm=$(awk '/^---$/{c++} c==2{print "yes"; exit}' "$f")
|
|
179
|
+
if [[ "$has_fm" == "yes" ]]; then
|
|
180
|
+
echo "PASS: $f has frontmatter"
|
|
181
|
+
else
|
|
182
|
+
echo "FAIL: $f missing frontmatter"
|
|
183
|
+
fi
|
|
184
|
+
done
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Step 5: Append to audit-findings.md and commit**
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
git add docs/plans/audit-findings.md
|
|
191
|
+
git commit -m "docs: add manifest and frontmatter validation to audit findings"
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Batch 3: Integration Smoke Tests
|
|
197
|
+
|
|
198
|
+
### Task 5: Smoke test lesson-check.sh with known patterns
|
|
199
|
+
|
|
200
|
+
**Files:**
|
|
201
|
+
- Read: `scripts/lesson-check.sh`
|
|
202
|
+
- Read: `docs/lessons/0001-bare-exception-swallowing.md` (regex: `^\s*except\s*:`)
|
|
203
|
+
- Read: `docs/lessons/0006-venv-pip-path.md` (regex: `\.venv/bin/pip\b`)
|
|
204
|
+
|
|
205
|
+
**Step 1: Create test fixture files with known anti-patterns**
|
|
206
|
+
|
|
207
|
+
Create temporary files:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
TMPDIR=$(mktemp -d)
|
|
211
|
+
|
|
212
|
+
# File that should trigger lesson-1 (bare except)
|
|
213
|
+
cat > "$TMPDIR/bad_except.py" <<'PYEOF'
|
|
214
|
+
try:
|
|
215
|
+
do_something()
|
|
216
|
+
except:
|
|
217
|
+
pass
|
|
218
|
+
PYEOF
|
|
219
|
+
|
|
220
|
+
# File that should trigger lesson-6 (.venv/bin/pip)
|
|
221
|
+
cat > "$TMPDIR/bad_pip.sh" <<'SHEOF'
|
|
222
|
+
.venv/bin/pip install requests
|
|
223
|
+
SHEOF
|
|
224
|
+
|
|
225
|
+
# Clean file — no violations
|
|
226
|
+
cat > "$TMPDIR/clean.py" <<'PYEOF'
|
|
227
|
+
try:
|
|
228
|
+
do_something()
|
|
229
|
+
except Exception as e:
|
|
230
|
+
logger.error("Failed: %s", e)
|
|
231
|
+
PYEOF
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Step 2: Run lesson-check against fixture files**
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
# Should find violations
|
|
238
|
+
scripts/lesson-check.sh "$TMPDIR/bad_except.py" "$TMPDIR/bad_pip.sh"
|
|
239
|
+
echo "Exit code: $?"
|
|
240
|
+
# Expected: exit 1, output showing [lesson-1] and [lesson-6]
|
|
241
|
+
|
|
242
|
+
# Should be clean
|
|
243
|
+
scripts/lesson-check.sh "$TMPDIR/clean.py"
|
|
244
|
+
echo "Exit code: $?"
|
|
245
|
+
# Expected: exit 0, "lesson-check: clean"
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Step 3: Clean up and record results**
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
rm -rf "$TMPDIR"
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Task 6: Smoke test quality-gate.sh with a mock project
|
|
255
|
+
|
|
256
|
+
**Files:**
|
|
257
|
+
- Read: `scripts/quality-gate.sh`
|
|
258
|
+
|
|
259
|
+
**Step 1: Create mock project directory**
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
MOCK_PROJECT=$(mktemp -d)
|
|
263
|
+
cd "$MOCK_PROJECT"
|
|
264
|
+
git init
|
|
265
|
+
echo "print('hello')" > main.py
|
|
266
|
+
git add main.py && git commit -m "init"
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**Step 2: Run quality-gate.sh against it**
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
scripts/quality-gate.sh --project-root "$MOCK_PROJECT"
|
|
273
|
+
echo "Exit code: $?"
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Expected: exit 0 (no changed files, no test suite detected, memory OK)
|
|
277
|
+
|
|
278
|
+
**Step 3: Verify --help works**
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
scripts/quality-gate.sh --help
|
|
282
|
+
echo "Exit code: $?"
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Expected: exit 0, help text shown
|
|
286
|
+
|
|
287
|
+
**Step 4: Clean up**
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
rm -rf "$MOCK_PROJECT"
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Task 7: Smoke test setup-ralph-loop.sh and stop-hook.sh
|
|
294
|
+
|
|
295
|
+
**Files:**
|
|
296
|
+
- Read: `scripts/setup-ralph-loop.sh`
|
|
297
|
+
- Read: `hooks/stop-hook.sh`
|
|
298
|
+
|
|
299
|
+
**Step 1: Test setup-ralph-loop.sh creates valid state file**
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
TMPDIR=$(mktemp -d)
|
|
303
|
+
cd "$TMPDIR"
|
|
304
|
+
bash /path/to/scripts/setup-ralph-loop.sh "Build a todo API" --max-iterations 10 --completion-promise "DONE"
|
|
305
|
+
# Verify state file
|
|
306
|
+
cat .claude/ralph-loop.local.md
|
|
307
|
+
# Should have: active: true, iteration: 1, max_iterations: 10, completion_promise: "DONE"
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
**Step 2: Test setup-ralph-loop.sh --help**
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
bash /path/to/scripts/setup-ralph-loop.sh --help
|
|
314
|
+
echo "Exit code: $?"
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Expected: exit 0, help text
|
|
318
|
+
|
|
319
|
+
**Step 3: Test setup-ralph-loop.sh with no args**
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
bash /path/to/scripts/setup-ralph-loop.sh 2>&1
|
|
323
|
+
echo "Exit code: $?"
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Expected: exit 1, error "No prompt provided"
|
|
327
|
+
|
|
328
|
+
**Step 4: Test stop-hook.sh with no state file**
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
cd "$TMPDIR"
|
|
332
|
+
rm -f .claude/ralph-loop.local.md
|
|
333
|
+
echo '{}' | bash /path/to/hooks/stop-hook.sh
|
|
334
|
+
echo "Exit code: $?"
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Expected: exit 0 (no state file = allow exit)
|
|
338
|
+
|
|
339
|
+
**Step 5: Clean up and record**
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
rm -rf "$TMPDIR"
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**Step 6: Commit smoke test results to audit-findings.md**
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
git add docs/plans/audit-findings.md
|
|
349
|
+
git commit -m "docs: add integration smoke test results to audit findings"
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## Batch 4: Fix All Audit Findings
|
|
355
|
+
|
|
356
|
+
### Task 8: Fix shellcheck issues
|
|
357
|
+
|
|
358
|
+
**Files:**
|
|
359
|
+
- Modify: Any scripts with shellcheck findings from Task 1
|
|
360
|
+
|
|
361
|
+
**Step 1: Read audit-findings.md for all FIX-disposition items**
|
|
362
|
+
|
|
363
|
+
**Step 2: For each FIX item, apply the fix**
|
|
364
|
+
|
|
365
|
+
Common shellcheck fixes:
|
|
366
|
+
- SC2086 (word splitting): Quote variables — `"$var"` instead of `$var`
|
|
367
|
+
- SC2155 (declare/assign): Split `local var=$(...)` into `local var; var=$(...)`
|
|
368
|
+
- SC2034 (unused variable): Remove or prefix with `_`
|
|
369
|
+
- SC2181 (check $?): Use `if command; then` instead of `command; if [[ $? ... ]]`
|
|
370
|
+
|
|
371
|
+
For SUPPRESS items: Add `# shellcheck disable=SCXXXX` with comment explaining why.
|
|
372
|
+
|
|
373
|
+
**Step 3: Re-run shellcheck to verify**
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
shellcheck -s bash -f gcc scripts/run-plan.sh scripts/lesson-check.sh scripts/quality-gate.sh scripts/setup-ralph-loop.sh scripts/auto-compound.sh scripts/entropy-audit.sh scripts/batch-audit.sh scripts/batch-test.sh scripts/lib/*.sh hooks/stop-hook.sh
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Expected: 0 findings (all fixed or suppressed)
|
|
380
|
+
|
|
381
|
+
**Step 4: Run existing tests to verify no regressions**
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
bash scripts/tests/run-all-tests.sh
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Expected: 109/109 pass
|
|
388
|
+
|
|
389
|
+
**Step 5: Commit**
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
git add -A
|
|
393
|
+
git commit -m "fix: resolve shellcheck findings across all scripts"
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### Task 9: Fix schema and smoke test findings
|
|
397
|
+
|
|
398
|
+
**Files:**
|
|
399
|
+
- Modify: Any files with schema or smoke test issues from Tasks 3-7
|
|
400
|
+
|
|
401
|
+
**Step 1: Fix any lesson file schema issues**
|
|
402
|
+
|
|
403
|
+
Check audit-findings.md for lesson file issues. Fix YAML frontmatter as needed.
|
|
404
|
+
|
|
405
|
+
**Step 2: Fix any manifest/frontmatter issues**
|
|
406
|
+
|
|
407
|
+
Fix plugin.json, marketplace.json, hooks.json, skill frontmatters, command frontmatters as needed.
|
|
408
|
+
|
|
409
|
+
**Step 3: Fix any bugs discovered during smoke tests**
|
|
410
|
+
|
|
411
|
+
**Step 4: Re-run all existing tests**
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
bash scripts/tests/run-all-tests.sh
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Expected: 109/109 pass
|
|
418
|
+
|
|
419
|
+
**Step 5: Commit**
|
|
420
|
+
|
|
421
|
+
```bash
|
|
422
|
+
git add -A
|
|
423
|
+
git commit -m "fix: resolve schema and smoke test findings"
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## Batch 5: lesson-check.sh Tests (**CRITICAL — A/B Competitive**)
|
|
429
|
+
|
|
430
|
+
### Task 10: Create test-lesson-check.sh with parse_lesson unit tests
|
|
431
|
+
|
|
432
|
+
**Files:**
|
|
433
|
+
- Create: `scripts/tests/test-lesson-check.sh`
|
|
434
|
+
- Read: `scripts/lesson-check.sh` (source `parse_lesson` function)
|
|
435
|
+
- Read: `docs/lessons/0001-*.md` through `docs/lessons/0006-*.md`
|
|
436
|
+
|
|
437
|
+
**Step 1: Write the test file with helper functions and parse_lesson tests**
|
|
438
|
+
|
|
439
|
+
Create `scripts/tests/test-lesson-check.sh`:
|
|
440
|
+
|
|
441
|
+
```bash
|
|
442
|
+
#!/usr/bin/env bash
|
|
443
|
+
# Test lesson-check.sh — parse_lesson(), regex matching, language filter, exit codes
|
|
444
|
+
set -euo pipefail
|
|
445
|
+
|
|
446
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
447
|
+
LESSON_CHECK="$SCRIPT_DIR/../lesson-check.sh"
|
|
448
|
+
|
|
449
|
+
# Source the lesson-check.sh to get parse_lesson and file_matches_languages
|
|
450
|
+
# We need to source it without running the main logic.
|
|
451
|
+
# Extract functions only by sourcing in a subshell context.
|
|
452
|
+
LESSONS_DIR="$SCRIPT_DIR/../../docs/lessons"
|
|
453
|
+
|
|
454
|
+
FAILURES=0
|
|
455
|
+
TESTS=0
|
|
456
|
+
|
|
457
|
+
assert_equals() {
|
|
458
|
+
local desc="$1" expected="$2" actual="$3"
|
|
459
|
+
TESTS=$((TESTS + 1))
|
|
460
|
+
if [[ "$actual" != "$expected" ]]; then
|
|
461
|
+
echo "FAIL: $desc"
|
|
462
|
+
echo " expected: $expected"
|
|
463
|
+
echo " actual: $actual"
|
|
464
|
+
FAILURES=$((FAILURES + 1))
|
|
465
|
+
else
|
|
466
|
+
echo "PASS: $desc"
|
|
467
|
+
fi
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
assert_exit() {
|
|
471
|
+
local desc="$1" expected_exit="$2"
|
|
472
|
+
shift 2
|
|
473
|
+
local actual_exit=0
|
|
474
|
+
local output
|
|
475
|
+
output=$("$@" 2>&1) || actual_exit=$?
|
|
476
|
+
TESTS=$((TESTS + 1))
|
|
477
|
+
if [[ "$actual_exit" -ne "$expected_exit" ]]; then
|
|
478
|
+
echo "FAIL: $desc"
|
|
479
|
+
echo " expected exit: $expected_exit"
|
|
480
|
+
echo " actual exit: $actual_exit"
|
|
481
|
+
echo " output: ${output:0:300}"
|
|
482
|
+
FAILURES=$((FAILURES + 1))
|
|
483
|
+
else
|
|
484
|
+
echo "PASS: $desc"
|
|
485
|
+
fi
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
assert_output_contains() {
|
|
489
|
+
local desc="$1" needle="$2"
|
|
490
|
+
shift 2
|
|
491
|
+
local output
|
|
492
|
+
output=$("$@" 2>&1) || true
|
|
493
|
+
TESTS=$((TESTS + 1))
|
|
494
|
+
if [[ "$output" != *"$needle"* ]]; then
|
|
495
|
+
echo "FAIL: $desc"
|
|
496
|
+
echo " expected to contain: $needle"
|
|
497
|
+
echo " in: ${output:0:300}"
|
|
498
|
+
FAILURES=$((FAILURES + 1))
|
|
499
|
+
else
|
|
500
|
+
echo "PASS: $desc"
|
|
501
|
+
fi
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
assert_output_not_contains() {
|
|
505
|
+
local desc="$1" needle="$2"
|
|
506
|
+
shift 2
|
|
507
|
+
local output
|
|
508
|
+
output=$("$@" 2>&1) || true
|
|
509
|
+
TESTS=$((TESTS + 1))
|
|
510
|
+
if [[ "$output" == *"$needle"* ]]; then
|
|
511
|
+
echo "FAIL: $desc"
|
|
512
|
+
echo " expected NOT to contain: $needle"
|
|
513
|
+
echo " in: ${output:0:300}"
|
|
514
|
+
FAILURES=$((FAILURES + 1))
|
|
515
|
+
else
|
|
516
|
+
echo "PASS: $desc"
|
|
517
|
+
fi
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
# --- Create test fixtures ---
|
|
521
|
+
FIXTURES=$(mktemp -d)
|
|
522
|
+
trap 'rm -rf "$FIXTURES"' EXIT
|
|
523
|
+
|
|
524
|
+
# Python file with bare except (triggers lesson-1)
|
|
525
|
+
cat > "$FIXTURES/bare_except.py" <<'EOF'
|
|
526
|
+
try:
|
|
527
|
+
do_something()
|
|
528
|
+
except:
|
|
529
|
+
pass
|
|
530
|
+
EOF
|
|
531
|
+
|
|
532
|
+
# Python file with clean except (should NOT trigger)
|
|
533
|
+
cat > "$FIXTURES/clean_except.py" <<'EOF'
|
|
534
|
+
try:
|
|
535
|
+
do_something()
|
|
536
|
+
except Exception as e:
|
|
537
|
+
logger.error("Failed: %s", e)
|
|
538
|
+
EOF
|
|
539
|
+
|
|
540
|
+
# Shell file with .venv/bin/pip (triggers lesson-6)
|
|
541
|
+
cat > "$FIXTURES/bad_pip.sh" <<'EOF'
|
|
542
|
+
.venv/bin/pip install requests
|
|
543
|
+
EOF
|
|
544
|
+
|
|
545
|
+
# Python file with sqlite3.connect (triggers lesson-5)
|
|
546
|
+
cat > "$FIXTURES/bad_sqlite.py" <<'EOF'
|
|
547
|
+
import sqlite3
|
|
548
|
+
conn = sqlite3.connect("test.db")
|
|
549
|
+
EOF
|
|
550
|
+
|
|
551
|
+
# Python file with hardcoded test count (triggers lesson-4)
|
|
552
|
+
cat > "$FIXTURES/bad_test_count.py" <<'EOF'
|
|
553
|
+
def test_items():
|
|
554
|
+
items = get_all()
|
|
555
|
+
assert len(items) == 42
|
|
556
|
+
EOF
|
|
557
|
+
|
|
558
|
+
# Clean shell file — no violations
|
|
559
|
+
cat > "$FIXTURES/clean.sh" <<'EOF'
|
|
560
|
+
#!/bin/bash
|
|
561
|
+
echo "hello world"
|
|
562
|
+
EOF
|
|
563
|
+
|
|
564
|
+
# Empty file
|
|
565
|
+
touch "$FIXTURES/empty.py"
|
|
566
|
+
|
|
567
|
+
# --- Tests: Exit codes ---
|
|
568
|
+
assert_exit "clean file exits 0" 0 "$LESSON_CHECK" "$FIXTURES/clean.sh"
|
|
569
|
+
assert_exit "bare except exits 1" 1 "$LESSON_CHECK" "$FIXTURES/bare_except.py"
|
|
570
|
+
assert_exit "no files exits 0" 0 "$LESSON_CHECK"
|
|
571
|
+
assert_exit "--help exits 0" 0 "$LESSON_CHECK" --help
|
|
572
|
+
|
|
573
|
+
# --- Tests: Detection accuracy ---
|
|
574
|
+
assert_output_contains "detects bare except" "[lesson-1]" "$LESSON_CHECK" "$FIXTURES/bare_except.py"
|
|
575
|
+
assert_output_not_contains "clean except not detected" "[lesson-1]" "$LESSON_CHECK" "$FIXTURES/clean_except.py"
|
|
576
|
+
assert_output_contains "detects .venv/bin/pip" "[lesson-6]" "$LESSON_CHECK" "$FIXTURES/bad_pip.sh"
|
|
577
|
+
assert_output_contains "detects sqlite3.connect" "[lesson-5]" "$LESSON_CHECK" "$FIXTURES/bad_sqlite.py"
|
|
578
|
+
assert_output_contains "detects hardcoded test count" "[lesson-4]" "$LESSON_CHECK" "$FIXTURES/bad_test_count.py"
|
|
579
|
+
|
|
580
|
+
# --- Tests: Language filtering ---
|
|
581
|
+
# lesson-1 is python-only — should NOT trigger on .sh files even if they contain "except:"
|
|
582
|
+
cat > "$FIXTURES/except_in_shell.sh" <<'EOF'
|
|
583
|
+
# This has except: in a comment
|
|
584
|
+
except:
|
|
585
|
+
EOF
|
|
586
|
+
assert_output_not_contains "python lesson skips .sh files" "[lesson-1]" "$LESSON_CHECK" "$FIXTURES/except_in_shell.sh"
|
|
587
|
+
|
|
588
|
+
# lesson-6 is shell-only — should NOT trigger on .py files
|
|
589
|
+
cat > "$FIXTURES/pip_in_python.py" <<'EOF'
|
|
590
|
+
# .venv/bin/pip is mentioned in a comment
|
|
591
|
+
path = ".venv/bin/pip"
|
|
592
|
+
EOF
|
|
593
|
+
assert_output_not_contains "shell lesson skips .py files" "[lesson-6]" "$LESSON_CHECK" "$FIXTURES/pip_in_python.py"
|
|
594
|
+
|
|
595
|
+
# --- Tests: Multiple files ---
|
|
596
|
+
assert_output_contains "multiple files: finds violation in first" "[lesson-1]" "$LESSON_CHECK" "$FIXTURES/bare_except.py" "$FIXTURES/clean.sh"
|
|
597
|
+
assert_exit "multiple files with violation exits 1" 1 "$LESSON_CHECK" "$FIXTURES/bare_except.py" "$FIXTURES/clean.sh"
|
|
598
|
+
|
|
599
|
+
# --- Tests: Stdin pipe mode ---
|
|
600
|
+
assert_output_contains "stdin pipe detects violation" "[lesson-1]" bash -c "echo '$FIXTURES/bare_except.py' | $LESSON_CHECK"
|
|
601
|
+
|
|
602
|
+
# --- Tests: --help shows dynamic lessons ---
|
|
603
|
+
assert_output_contains "--help shows lesson-1" "[lesson-1]" "$LESSON_CHECK" --help
|
|
604
|
+
assert_output_contains "--help shows lesson-6" "[lesson-6]" "$LESSON_CHECK" --help
|
|
605
|
+
|
|
606
|
+
# --- Tests: Empty and nonexistent files ---
|
|
607
|
+
assert_exit "empty file exits 0" 0 "$LESSON_CHECK" "$FIXTURES/empty.py"
|
|
608
|
+
assert_exit "nonexistent file exits 0" 0 "$LESSON_CHECK" "$FIXTURES/does_not_exist.py"
|
|
609
|
+
|
|
610
|
+
# --- Tests: Violation count ---
|
|
611
|
+
assert_output_contains "reports violation count" "violation(s) found" "$LESSON_CHECK" "$FIXTURES/bare_except.py"
|
|
612
|
+
assert_output_contains "clean reports clean" "lesson-check: clean" "$LESSON_CHECK" "$FIXTURES/clean.sh"
|
|
613
|
+
|
|
614
|
+
# --- Summary ---
|
|
615
|
+
echo ""
|
|
616
|
+
echo "Results: $((TESTS - FAILURES))/$TESTS passed"
|
|
617
|
+
if [[ $FAILURES -gt 0 ]]; then
|
|
618
|
+
echo "FAILURES: $FAILURES"
|
|
619
|
+
exit 1
|
|
620
|
+
fi
|
|
621
|
+
echo "ALL PASSED"
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
**Step 2: Run the test**
|
|
625
|
+
|
|
626
|
+
```bash
|
|
627
|
+
bash scripts/tests/test-lesson-check.sh
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
Expected: All tests pass. If any fail, fix lesson-check.sh or the test expectations.
|
|
631
|
+
|
|
632
|
+
**Step 3: Verify existing tests still pass**
|
|
633
|
+
|
|
634
|
+
```bash
|
|
635
|
+
bash scripts/tests/run-all-tests.sh
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
Expected: 109/109 still pass (new test file won't be picked up by `test-run-plan-*.sh` glob)
|
|
639
|
+
|
|
640
|
+
**Step 4: Update run-all-tests.sh to also discover test-lesson-check.sh and other new test files**
|
|
641
|
+
|
|
642
|
+
Modify `scripts/tests/run-all-tests.sh` line 13 — change the glob from `test-run-plan-*.sh` to `test-*.sh`:
|
|
643
|
+
|
|
644
|
+
```bash
|
|
645
|
+
# Old:
|
|
646
|
+
mapfile -t TEST_FILES < <(find "$SCRIPT_DIR" -maxdepth 1 -name "test-run-plan-*.sh" -type f | sort)
|
|
647
|
+
# New:
|
|
648
|
+
mapfile -t TEST_FILES < <(find "$SCRIPT_DIR" -maxdepth 1 -name "test-*.sh" -type f | sort)
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
**Step 5: Run updated test runner**
|
|
652
|
+
|
|
653
|
+
```bash
|
|
654
|
+
bash scripts/tests/run-all-tests.sh
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
Expected: 109 + new tests all pass (8 test files now)
|
|
658
|
+
|
|
659
|
+
**Step 6: Commit**
|
|
660
|
+
|
|
661
|
+
```bash
|
|
662
|
+
git add scripts/tests/test-lesson-check.sh scripts/tests/run-all-tests.sh
|
|
663
|
+
git commit -m "test: add comprehensive tests for lesson-check.sh"
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
---
|
|
667
|
+
|
|
668
|
+
## Batch 6: stop-hook.sh + setup-ralph-loop.sh Tests (**CRITICAL — A/B Competitive**)
|
|
669
|
+
|
|
670
|
+
### Task 11: Create test-stop-hook.sh
|
|
671
|
+
|
|
672
|
+
**Files:**
|
|
673
|
+
- Create: `scripts/tests/test-stop-hook.sh`
|
|
674
|
+
- Read: `hooks/stop-hook.sh`
|
|
675
|
+
|
|
676
|
+
**Step 1: Write test-stop-hook.sh**
|
|
677
|
+
|
|
678
|
+
Create `scripts/tests/test-stop-hook.sh`:
|
|
679
|
+
|
|
680
|
+
```bash
|
|
681
|
+
#!/usr/bin/env bash
|
|
682
|
+
# Test hooks/stop-hook.sh — state file parsing, iteration, completion promise, JSON output
|
|
683
|
+
set -euo pipefail
|
|
684
|
+
|
|
685
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
686
|
+
STOP_HOOK="$SCRIPT_DIR/../../hooks/stop-hook.sh"
|
|
687
|
+
|
|
688
|
+
FAILURES=0
|
|
689
|
+
TESTS=0
|
|
690
|
+
|
|
691
|
+
assert_exit() {
|
|
692
|
+
local desc="$1" expected_exit="$2"
|
|
693
|
+
shift 2
|
|
694
|
+
local actual_exit=0
|
|
695
|
+
local output
|
|
696
|
+
output=$("$@" 2>&1) || actual_exit=$?
|
|
697
|
+
TESTS=$((TESTS + 1))
|
|
698
|
+
if [[ "$actual_exit" -ne "$expected_exit" ]]; then
|
|
699
|
+
echo "FAIL: $desc"
|
|
700
|
+
echo " expected exit: $expected_exit"
|
|
701
|
+
echo " actual exit: $actual_exit"
|
|
702
|
+
echo " output: ${output:0:500}"
|
|
703
|
+
FAILURES=$((FAILURES + 1))
|
|
704
|
+
else
|
|
705
|
+
echo "PASS: $desc"
|
|
706
|
+
fi
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
assert_output_contains() {
|
|
710
|
+
local desc="$1" needle="$2"
|
|
711
|
+
shift 2
|
|
712
|
+
local output
|
|
713
|
+
output=$("$@" 2>&1) || true
|
|
714
|
+
TESTS=$((TESTS + 1))
|
|
715
|
+
if [[ "$output" != *"$needle"* ]]; then
|
|
716
|
+
echo "FAIL: $desc"
|
|
717
|
+
echo " expected to contain: $needle"
|
|
718
|
+
echo " in: ${output:0:500}"
|
|
719
|
+
FAILURES=$((FAILURES + 1))
|
|
720
|
+
else
|
|
721
|
+
echo "PASS: $desc"
|
|
722
|
+
fi
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
assert_json_field() {
|
|
726
|
+
local desc="$1" field="$2" expected="$3" json="$4"
|
|
727
|
+
local actual
|
|
728
|
+
actual=$(echo "$json" | jq -r "$field" 2>/dev/null || echo "PARSE_ERROR")
|
|
729
|
+
TESTS=$((TESTS + 1))
|
|
730
|
+
if [[ "$actual" != "$expected" ]]; then
|
|
731
|
+
echo "FAIL: $desc"
|
|
732
|
+
echo " field: $field"
|
|
733
|
+
echo " expected: $expected"
|
|
734
|
+
echo " actual: $actual"
|
|
735
|
+
FAILURES=$((FAILURES + 1))
|
|
736
|
+
else
|
|
737
|
+
echo "PASS: $desc"
|
|
738
|
+
fi
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
# --- Setup ---
|
|
742
|
+
WORK=$(mktemp -d)
|
|
743
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
744
|
+
cd "$WORK"
|
|
745
|
+
|
|
746
|
+
# --- Test: No state file = allow exit ---
|
|
747
|
+
assert_exit "no state file exits 0" 0 bash -c "echo '{}' | bash '$STOP_HOOK'"
|
|
748
|
+
|
|
749
|
+
# --- Test: State file with max iterations reached ---
|
|
750
|
+
mkdir -p .claude
|
|
751
|
+
cat > .claude/ralph-loop.local.md <<'STATE'
|
|
752
|
+
---
|
|
753
|
+
active: true
|
|
754
|
+
iteration: 10
|
|
755
|
+
max_iterations: 10
|
|
756
|
+
completion_promise: null
|
|
757
|
+
started_at: "2026-01-01T00:00:00Z"
|
|
758
|
+
---
|
|
759
|
+
|
|
760
|
+
Build something
|
|
761
|
+
STATE
|
|
762
|
+
|
|
763
|
+
assert_exit "max iterations reached exits 0" 0 bash -c "echo '{}' | bash '$STOP_HOOK'"
|
|
764
|
+
# State file should be removed
|
|
765
|
+
TESTS=$((TESTS + 1))
|
|
766
|
+
if [[ -f .claude/ralph-loop.local.md ]]; then
|
|
767
|
+
echo "FAIL: state file should be removed at max iterations"
|
|
768
|
+
FAILURES=$((FAILURES + 1))
|
|
769
|
+
else
|
|
770
|
+
echo "PASS: state file removed at max iterations"
|
|
771
|
+
fi
|
|
772
|
+
|
|
773
|
+
# --- Test: Corrupted state file (non-numeric iteration) ---
|
|
774
|
+
mkdir -p .claude
|
|
775
|
+
cat > .claude/ralph-loop.local.md <<'STATE'
|
|
776
|
+
---
|
|
777
|
+
active: true
|
|
778
|
+
iteration: abc
|
|
779
|
+
max_iterations: 10
|
|
780
|
+
completion_promise: null
|
|
781
|
+
started_at: "2026-01-01T00:00:00Z"
|
|
782
|
+
---
|
|
783
|
+
|
|
784
|
+
Build something
|
|
785
|
+
STATE
|
|
786
|
+
|
|
787
|
+
assert_exit "corrupted iteration exits 0" 0 bash -c "echo '{}' | bash '$STOP_HOOK'"
|
|
788
|
+
assert_output_contains "corrupted iteration warns" "corrupted" bash -c "echo '{}' | bash '$STOP_HOOK'" || true
|
|
789
|
+
|
|
790
|
+
# --- Test: Active loop with transcript containing completion promise ---
|
|
791
|
+
mkdir -p .claude
|
|
792
|
+
cat > .claude/ralph-loop.local.md <<'STATE'
|
|
793
|
+
---
|
|
794
|
+
active: true
|
|
795
|
+
iteration: 3
|
|
796
|
+
max_iterations: 0
|
|
797
|
+
completion_promise: "ALL_TESTS_PASS"
|
|
798
|
+
started_at: "2026-01-01T00:00:00Z"
|
|
799
|
+
---
|
|
800
|
+
|
|
801
|
+
Build and test everything
|
|
802
|
+
STATE
|
|
803
|
+
|
|
804
|
+
# Create mock transcript with completion promise
|
|
805
|
+
TRANSCRIPT="$WORK/transcript.jsonl"
|
|
806
|
+
cat > "$TRANSCRIPT" <<TRANSCRIPT_EOF
|
|
807
|
+
{"role":"user","message":{"content":[{"type":"text","text":"start"}]}}
|
|
808
|
+
{"role":"assistant","message":{"content":[{"type":"text","text":"Done! <promise>ALL_TESTS_PASS</promise>"}]}}
|
|
809
|
+
TRANSCRIPT_EOF
|
|
810
|
+
|
|
811
|
+
HOOK_INPUT=$(jq -n --arg tp "$TRANSCRIPT" '{"transcript_path": $tp}')
|
|
812
|
+
OUTPUT=$(echo "$HOOK_INPUT" | bash "$STOP_HOOK" 2>&1) || true
|
|
813
|
+
TESTS=$((TESTS + 1))
|
|
814
|
+
if [[ "$OUTPUT" == *"Detected"* ]]; then
|
|
815
|
+
echo "PASS: completion promise detected"
|
|
816
|
+
else
|
|
817
|
+
echo "FAIL: completion promise not detected"
|
|
818
|
+
echo " output: ${OUTPUT:0:500}"
|
|
819
|
+
FAILURES=$((FAILURES + 1))
|
|
820
|
+
fi
|
|
821
|
+
|
|
822
|
+
# --- Test: Active loop WITHOUT completion promise in transcript → block and continue ---
|
|
823
|
+
mkdir -p .claude
|
|
824
|
+
cat > .claude/ralph-loop.local.md <<'STATE'
|
|
825
|
+
---
|
|
826
|
+
active: true
|
|
827
|
+
iteration: 3
|
|
828
|
+
max_iterations: 0
|
|
829
|
+
completion_promise: "ALL_TESTS_PASS"
|
|
830
|
+
started_at: "2026-01-01T00:00:00Z"
|
|
831
|
+
---
|
|
832
|
+
|
|
833
|
+
Build and test everything
|
|
834
|
+
STATE
|
|
835
|
+
|
|
836
|
+
TRANSCRIPT2="$WORK/transcript2.jsonl"
|
|
837
|
+
cat > "$TRANSCRIPT2" <<TRANSCRIPT_EOF
|
|
838
|
+
{"role":"user","message":{"content":[{"type":"text","text":"start"}]}}
|
|
839
|
+
{"role":"assistant","message":{"content":[{"type":"text","text":"Still working on it..."}]}}
|
|
840
|
+
TRANSCRIPT_EOF
|
|
841
|
+
|
|
842
|
+
HOOK_INPUT2=$(jq -n --arg tp "$TRANSCRIPT2" '{"transcript_path": $tp}')
|
|
843
|
+
OUTPUT2=$(echo "$HOOK_INPUT2" | bash "$STOP_HOOK" 2>&1) || true
|
|
844
|
+
|
|
845
|
+
# Should output JSON with "decision": "block"
|
|
846
|
+
assert_json_field "block decision when promise not found" ".decision" "block" "$OUTPUT2"
|
|
847
|
+
assert_json_field "reason contains prompt" ".reason" "Build and test everything" "$OUTPUT2"
|
|
848
|
+
|
|
849
|
+
# Iteration should be incremented
|
|
850
|
+
TESTS=$((TESTS + 1))
|
|
851
|
+
if [[ -f .claude/ralph-loop.local.md ]]; then
|
|
852
|
+
NEW_ITER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' .claude/ralph-loop.local.md | grep '^iteration:' | sed 's/iteration: *//')
|
|
853
|
+
if [[ "$NEW_ITER" == "4" ]]; then
|
|
854
|
+
echo "PASS: iteration incremented to 4"
|
|
855
|
+
else
|
|
856
|
+
echo "FAIL: iteration should be 4, got $NEW_ITER"
|
|
857
|
+
FAILURES=$((FAILURES + 1))
|
|
858
|
+
fi
|
|
859
|
+
else
|
|
860
|
+
echo "FAIL: state file should still exist"
|
|
861
|
+
FAILURES=$((FAILURES + 1))
|
|
862
|
+
fi
|
|
863
|
+
|
|
864
|
+
# --- Summary ---
|
|
865
|
+
echo ""
|
|
866
|
+
echo "Results: $((TESTS - FAILURES))/$TESTS passed"
|
|
867
|
+
if [[ $FAILURES -gt 0 ]]; then
|
|
868
|
+
echo "FAILURES: $FAILURES"
|
|
869
|
+
exit 1
|
|
870
|
+
fi
|
|
871
|
+
echo "ALL PASSED"
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
**Step 2: Run test**
|
|
875
|
+
|
|
876
|
+
```bash
|
|
877
|
+
bash scripts/tests/test-stop-hook.sh
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
Expected: All pass
|
|
881
|
+
|
|
882
|
+
**Step 3: Commit**
|
|
883
|
+
|
|
884
|
+
```bash
|
|
885
|
+
git add scripts/tests/test-stop-hook.sh
|
|
886
|
+
git commit -m "test: add comprehensive tests for stop-hook.sh"
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
### Task 12: Create test-setup-ralph-loop.sh
|
|
890
|
+
|
|
891
|
+
**Files:**
|
|
892
|
+
- Create: `scripts/tests/test-setup-ralph-loop.sh`
|
|
893
|
+
- Read: `scripts/setup-ralph-loop.sh`
|
|
894
|
+
|
|
895
|
+
**Step 1: Write test-setup-ralph-loop.sh**
|
|
896
|
+
|
|
897
|
+
Create `scripts/tests/test-setup-ralph-loop.sh`:
|
|
898
|
+
|
|
899
|
+
```bash
|
|
900
|
+
#!/usr/bin/env bash
|
|
901
|
+
# Test scripts/setup-ralph-loop.sh — state file creation, arg parsing, error handling
|
|
902
|
+
set -euo pipefail
|
|
903
|
+
|
|
904
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
905
|
+
SETUP_RALPH="$SCRIPT_DIR/../setup-ralph-loop.sh"
|
|
906
|
+
|
|
907
|
+
FAILURES=0
|
|
908
|
+
TESTS=0
|
|
909
|
+
|
|
910
|
+
assert_exit() {
|
|
911
|
+
local desc="$1" expected_exit="$2"
|
|
912
|
+
shift 2
|
|
913
|
+
local actual_exit=0
|
|
914
|
+
local output
|
|
915
|
+
output=$("$@" 2>&1) || actual_exit=$?
|
|
916
|
+
TESTS=$((TESTS + 1))
|
|
917
|
+
if [[ "$actual_exit" -ne "$expected_exit" ]]; then
|
|
918
|
+
echo "FAIL: $desc"
|
|
919
|
+
echo " expected exit: $expected_exit"
|
|
920
|
+
echo " actual exit: $actual_exit"
|
|
921
|
+
echo " output: ${output:0:300}"
|
|
922
|
+
FAILURES=$((FAILURES + 1))
|
|
923
|
+
else
|
|
924
|
+
echo "PASS: $desc"
|
|
925
|
+
fi
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
assert_output_contains() {
|
|
929
|
+
local desc="$1" needle="$2"
|
|
930
|
+
shift 2
|
|
931
|
+
local output
|
|
932
|
+
output=$("$@" 2>&1) || true
|
|
933
|
+
TESTS=$((TESTS + 1))
|
|
934
|
+
if [[ "$output" != *"$needle"* ]]; then
|
|
935
|
+
echo "FAIL: $desc"
|
|
936
|
+
echo " expected to contain: $needle"
|
|
937
|
+
echo " in: ${output:0:300}"
|
|
938
|
+
FAILURES=$((FAILURES + 1))
|
|
939
|
+
else
|
|
940
|
+
echo "PASS: $desc"
|
|
941
|
+
fi
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
assert_file_contains() {
|
|
945
|
+
local desc="$1" needle="$2" filepath="$3"
|
|
946
|
+
TESTS=$((TESTS + 1))
|
|
947
|
+
if [[ ! -f "$filepath" ]]; then
|
|
948
|
+
echo "FAIL: $desc (file not found: $filepath)"
|
|
949
|
+
FAILURES=$((FAILURES + 1))
|
|
950
|
+
elif grep -q "$needle" "$filepath"; then
|
|
951
|
+
echo "PASS: $desc"
|
|
952
|
+
else
|
|
953
|
+
echo "FAIL: $desc"
|
|
954
|
+
echo " expected file to contain: $needle"
|
|
955
|
+
FAILURES=$((FAILURES + 1))
|
|
956
|
+
fi
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
# --- Setup ---
|
|
960
|
+
WORK=$(mktemp -d)
|
|
961
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
962
|
+
|
|
963
|
+
# --- Test: --help exits 0 ---
|
|
964
|
+
assert_exit "--help exits 0" 0 bash "$SETUP_RALPH" --help
|
|
965
|
+
|
|
966
|
+
# --- Test: No prompt exits 1 ---
|
|
967
|
+
assert_exit "no prompt exits 1" 1 bash -c "cd '$WORK' && bash '$SETUP_RALPH'"
|
|
968
|
+
|
|
969
|
+
# --- Test: Invalid --max-iterations exits 1 ---
|
|
970
|
+
assert_exit "non-numeric max-iterations exits 1" 1 bash -c "cd '$WORK' && bash '$SETUP_RALPH' Build something --max-iterations abc"
|
|
971
|
+
|
|
972
|
+
# --- Test: Basic prompt creates state file ---
|
|
973
|
+
rm -rf "$WORK/.claude"
|
|
974
|
+
(cd "$WORK" && bash "$SETUP_RALPH" "Build a todo API") >/dev/null 2>&1
|
|
975
|
+
TESTS=$((TESTS + 1))
|
|
976
|
+
if [[ -f "$WORK/.claude/ralph-loop.local.md" ]]; then
|
|
977
|
+
echo "PASS: state file created"
|
|
978
|
+
else
|
|
979
|
+
echo "FAIL: state file not created"
|
|
980
|
+
FAILURES=$((FAILURES + 1))
|
|
981
|
+
fi
|
|
982
|
+
|
|
983
|
+
# --- Test: State file has correct frontmatter ---
|
|
984
|
+
assert_file_contains "state has active: true" "active: true" "$WORK/.claude/ralph-loop.local.md"
|
|
985
|
+
assert_file_contains "state has iteration: 1" "iteration: 1" "$WORK/.claude/ralph-loop.local.md"
|
|
986
|
+
assert_file_contains "state has max_iterations: 0" "max_iterations: 0" "$WORK/.claude/ralph-loop.local.md"
|
|
987
|
+
assert_file_contains "state has prompt text" "Build a todo API" "$WORK/.claude/ralph-loop.local.md"
|
|
988
|
+
|
|
989
|
+
# --- Test: With --max-iterations and --completion-promise ---
|
|
990
|
+
rm -rf "$WORK/.claude"
|
|
991
|
+
(cd "$WORK" && bash "$SETUP_RALPH" "Fix the auth bug" --max-iterations 20 --completion-promise "DONE") >/dev/null 2>&1
|
|
992
|
+
assert_file_contains "max-iterations in state" "max_iterations: 20" "$WORK/.claude/ralph-loop.local.md"
|
|
993
|
+
assert_file_contains "completion-promise in state" 'completion_promise: "DONE"' "$WORK/.claude/ralph-loop.local.md"
|
|
994
|
+
assert_file_contains "prompt in state" "Fix the auth bug" "$WORK/.claude/ralph-loop.local.md"
|
|
995
|
+
|
|
996
|
+
# --- Test: Multi-word prompt without quotes ---
|
|
997
|
+
rm -rf "$WORK/.claude"
|
|
998
|
+
(cd "$WORK" && bash "$SETUP_RALPH" Build a todo API with tests) >/dev/null 2>&1
|
|
999
|
+
assert_file_contains "multi-word prompt joined" "Build a todo API with tests" "$WORK/.claude/ralph-loop.local.md"
|
|
1000
|
+
|
|
1001
|
+
# --- Test: Output contains activation message ---
|
|
1002
|
+
rm -rf "$WORK/.claude"
|
|
1003
|
+
assert_output_contains "output shows activated" "Ralph loop activated" bash -c "cd '$WORK' && bash '$SETUP_RALPH' Test prompt"
|
|
1004
|
+
|
|
1005
|
+
# --- Summary ---
|
|
1006
|
+
echo ""
|
|
1007
|
+
echo "Results: $((TESTS - FAILURES))/$TESTS passed"
|
|
1008
|
+
if [[ $FAILURES -gt 0 ]]; then
|
|
1009
|
+
echo "FAILURES: $FAILURES"
|
|
1010
|
+
exit 1
|
|
1011
|
+
fi
|
|
1012
|
+
echo "ALL PASSED"
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
**Step 2: Run test**
|
|
1016
|
+
|
|
1017
|
+
```bash
|
|
1018
|
+
bash scripts/tests/test-setup-ralph-loop.sh
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
Expected: All pass
|
|
1022
|
+
|
|
1023
|
+
**Step 3: Commit**
|
|
1024
|
+
|
|
1025
|
+
```bash
|
|
1026
|
+
git add scripts/tests/test-setup-ralph-loop.sh
|
|
1027
|
+
git commit -m "test: add tests for setup-ralph-loop.sh"
|
|
1028
|
+
```
|
|
1029
|
+
|
|
1030
|
+
---
|
|
1031
|
+
|
|
1032
|
+
## Batch 7: quality-gate.sh + Utility Tests
|
|
1033
|
+
|
|
1034
|
+
### Task 13: Create test-quality-gate.sh for orchestration logic
|
|
1035
|
+
|
|
1036
|
+
**Files:**
|
|
1037
|
+
- Create: `scripts/tests/test-quality-gate.sh`
|
|
1038
|
+
- Read: `scripts/quality-gate.sh`
|
|
1039
|
+
|
|
1040
|
+
**Step 1: Write test-quality-gate.sh**
|
|
1041
|
+
|
|
1042
|
+
Create `scripts/tests/test-quality-gate.sh`:
|
|
1043
|
+
|
|
1044
|
+
```bash
|
|
1045
|
+
#!/usr/bin/env bash
|
|
1046
|
+
# Test scripts/quality-gate.sh — CLI args, test runner detection, exit codes
|
|
1047
|
+
set -euo pipefail
|
|
1048
|
+
|
|
1049
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
1050
|
+
QUALITY_GATE="$SCRIPT_DIR/../quality-gate.sh"
|
|
1051
|
+
|
|
1052
|
+
FAILURES=0
|
|
1053
|
+
TESTS=0
|
|
1054
|
+
|
|
1055
|
+
assert_exit() {
|
|
1056
|
+
local desc="$1" expected_exit="$2"
|
|
1057
|
+
shift 2
|
|
1058
|
+
local actual_exit=0
|
|
1059
|
+
local output
|
|
1060
|
+
output=$("$@" 2>&1) || actual_exit=$?
|
|
1061
|
+
TESTS=$((TESTS + 1))
|
|
1062
|
+
if [[ "$actual_exit" -ne "$expected_exit" ]]; then
|
|
1063
|
+
echo "FAIL: $desc"
|
|
1064
|
+
echo " expected exit: $expected_exit"
|
|
1065
|
+
echo " actual exit: $actual_exit"
|
|
1066
|
+
echo " output: ${output:0:300}"
|
|
1067
|
+
FAILURES=$((FAILURES + 1))
|
|
1068
|
+
else
|
|
1069
|
+
echo "PASS: $desc"
|
|
1070
|
+
fi
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
assert_output_contains() {
|
|
1074
|
+
local desc="$1" needle="$2"
|
|
1075
|
+
shift 2
|
|
1076
|
+
local output
|
|
1077
|
+
output=$("$@" 2>&1) || true
|
|
1078
|
+
TESTS=$((TESTS + 1))
|
|
1079
|
+
if [[ "$output" != *"$needle"* ]]; then
|
|
1080
|
+
echo "FAIL: $desc"
|
|
1081
|
+
echo " expected to contain: $needle"
|
|
1082
|
+
echo " in: ${output:0:300}"
|
|
1083
|
+
FAILURES=$((FAILURES + 1))
|
|
1084
|
+
else
|
|
1085
|
+
echo "PASS: $desc"
|
|
1086
|
+
fi
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
# --- Test: --help exits 0 ---
|
|
1090
|
+
assert_exit "--help exits 0" 0 "$QUALITY_GATE" --help
|
|
1091
|
+
|
|
1092
|
+
# --- Test: No --project-root exits 1 ---
|
|
1093
|
+
assert_exit "no project-root exits 1" 1 "$QUALITY_GATE"
|
|
1094
|
+
|
|
1095
|
+
# --- Test: Nonexistent directory exits 1 ---
|
|
1096
|
+
assert_exit "nonexistent dir exits 1" 1 "$QUALITY_GATE" --project-root /tmp/nonexistent-dir-$$
|
|
1097
|
+
|
|
1098
|
+
# --- Test: Unknown option exits 1 ---
|
|
1099
|
+
assert_exit "unknown option exits 1" 1 "$QUALITY_GATE" --unknown-flag
|
|
1100
|
+
|
|
1101
|
+
# --- Test: Clean git repo with no test suite passes ---
|
|
1102
|
+
MOCK=$(mktemp -d)
|
|
1103
|
+
trap 'rm -rf "$MOCK"' EXIT
|
|
1104
|
+
cd "$MOCK"
|
|
1105
|
+
git init -q
|
|
1106
|
+
echo "hello" > file.txt
|
|
1107
|
+
git add file.txt && git commit -q -m "init"
|
|
1108
|
+
cd - >/dev/null
|
|
1109
|
+
|
|
1110
|
+
assert_exit "clean repo passes" 0 "$QUALITY_GATE" --project-root "$MOCK"
|
|
1111
|
+
assert_output_contains "skips test suite when none detected" "No test suite detected" "$QUALITY_GATE" --project-root "$MOCK"
|
|
1112
|
+
assert_output_contains "shows ALL PASSED" "ALL PASSED" "$QUALITY_GATE" --project-root "$MOCK"
|
|
1113
|
+
assert_output_contains "shows Memory" "Memory" "$QUALITY_GATE" --project-root "$MOCK"
|
|
1114
|
+
|
|
1115
|
+
# --- Test: help text mentions required options ---
|
|
1116
|
+
assert_output_contains "help mentions --project-root" "--project-root" "$QUALITY_GATE" --help
|
|
1117
|
+
assert_output_contains "help mentions lesson check" "Lesson check" "$QUALITY_GATE" --help
|
|
1118
|
+
|
|
1119
|
+
# --- Summary ---
|
|
1120
|
+
echo ""
|
|
1121
|
+
echo "Results: $((TESTS - FAILURES))/$TESTS passed"
|
|
1122
|
+
if [[ $FAILURES -gt 0 ]]; then
|
|
1123
|
+
echo "FAILURES: $FAILURES"
|
|
1124
|
+
exit 1
|
|
1125
|
+
fi
|
|
1126
|
+
echo "ALL PASSED"
|
|
1127
|
+
```
|
|
1128
|
+
|
|
1129
|
+
**Step 2: Run test**
|
|
1130
|
+
|
|
1131
|
+
```bash
|
|
1132
|
+
bash scripts/tests/test-quality-gate.sh
|
|
1133
|
+
```
|
|
1134
|
+
|
|
1135
|
+
**Step 3: Commit**
|
|
1136
|
+
|
|
1137
|
+
```bash
|
|
1138
|
+
git add scripts/tests/test-quality-gate.sh
|
|
1139
|
+
git commit -m "test: add orchestration tests for quality-gate.sh"
|
|
1140
|
+
```
|
|
1141
|
+
|
|
1142
|
+
---
|
|
1143
|
+
|
|
1144
|
+
## Batch 8: Low-Risk Validation Tests + Integration Wiring
|
|
1145
|
+
|
|
1146
|
+
### Task 14: Create test-lesson-schema.sh (reusable lesson file validator)
|
|
1147
|
+
|
|
1148
|
+
**Files:**
|
|
1149
|
+
- Create: `scripts/tests/test-lesson-schema.sh`
|
|
1150
|
+
|
|
1151
|
+
**Step 1: Write test-lesson-schema.sh**
|
|
1152
|
+
|
|
1153
|
+
Create `scripts/tests/test-lesson-schema.sh`:
|
|
1154
|
+
|
|
1155
|
+
```bash
|
|
1156
|
+
#!/usr/bin/env bash
|
|
1157
|
+
# Test that all lesson files in docs/lessons/ have valid YAML frontmatter
|
|
1158
|
+
set -euo pipefail
|
|
1159
|
+
|
|
1160
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
1161
|
+
LESSONS_DIR="$SCRIPT_DIR/../../docs/lessons"
|
|
1162
|
+
|
|
1163
|
+
FAILURES=0
|
|
1164
|
+
TESTS=0
|
|
1165
|
+
|
|
1166
|
+
assert_true() {
|
|
1167
|
+
local desc="$1" condition="$2"
|
|
1168
|
+
TESTS=$((TESTS + 1))
|
|
1169
|
+
if eval "$condition"; then
|
|
1170
|
+
echo "PASS: $desc"
|
|
1171
|
+
else
|
|
1172
|
+
echo "FAIL: $desc"
|
|
1173
|
+
FAILURES=$((FAILURES + 1))
|
|
1174
|
+
fi
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
VALID_SEVERITIES="blocker should-fix nice-to-have"
|
|
1178
|
+
VALID_TYPES="syntactic semantic"
|
|
1179
|
+
VALID_CATEGORIES="async-traps resource-lifecycle silent-failures integration-boundaries test-anti-patterns performance"
|
|
1180
|
+
|
|
1181
|
+
for f in "$LESSONS_DIR"/[0-9]*.md; do
|
|
1182
|
+
[[ -f "$f" ]] || continue
|
|
1183
|
+
base=$(basename "$f")
|
|
1184
|
+
|
|
1185
|
+
# Extract frontmatter
|
|
1186
|
+
fm=$(awk '/^---$/{c++; if(c==2) exit} c==1 && !/^---$/{print}' "$f")
|
|
1187
|
+
|
|
1188
|
+
# Required top-level fields
|
|
1189
|
+
id=$(echo "$fm" | grep '^id:' | sed 's/^id:[[:space:]]*//')
|
|
1190
|
+
title=$(echo "$fm" | grep '^title:' | sed 's/^title:[[:space:]]*//' | sed 's/^["'"'"']//;s/["'"'"']$//')
|
|
1191
|
+
severity=$(echo "$fm" | grep '^severity:' | sed 's/^severity:[[:space:]]*//')
|
|
1192
|
+
languages=$(echo "$fm" | grep '^languages:' | sed 's/^languages:[[:space:]]*//')
|
|
1193
|
+
category=$(echo "$fm" | grep '^category:' | sed 's/^category:[[:space:]]*//')
|
|
1194
|
+
fix=$(echo "$fm" | grep '^fix:' | sed 's/^fix:[[:space:]]*//')
|
|
1195
|
+
|
|
1196
|
+
# Nested pattern fields
|
|
1197
|
+
ptype=$(echo "$fm" | awk '/^pattern:/{ip=1;next} ip && /^[^[:space:]]/{ip=0} ip && /^[[:space:]]+type:/{sub(/^[[:space:]]+type:[[:space:]]+/,""); print}')
|
|
1198
|
+
|
|
1199
|
+
assert_true "$base: has id" '[[ -n "$id" ]]'
|
|
1200
|
+
assert_true "$base: id is numeric" '[[ "$id" =~ ^[0-9]+$ ]]'
|
|
1201
|
+
assert_true "$base: has title" '[[ -n "$title" ]]'
|
|
1202
|
+
assert_true "$base: has severity" '[[ -n "$severity" ]]'
|
|
1203
|
+
assert_true "$base: severity is valid" '[[ "$VALID_SEVERITIES" == *"$severity"* ]]'
|
|
1204
|
+
assert_true "$base: has languages" '[[ -n "$languages" ]]'
|
|
1205
|
+
assert_true "$base: has category" '[[ -n "$category" ]]'
|
|
1206
|
+
assert_true "$base: category is valid" '[[ "$VALID_CATEGORIES" == *"$category"* ]]'
|
|
1207
|
+
assert_true "$base: has pattern.type" '[[ -n "$ptype" ]]'
|
|
1208
|
+
assert_true "$base: pattern.type is valid" '[[ "$VALID_TYPES" == *"$ptype"* ]]'
|
|
1209
|
+
assert_true "$base: has fix" '[[ -n "$fix" ]]'
|
|
1210
|
+
|
|
1211
|
+
# If syntactic, must have regex
|
|
1212
|
+
if [[ "$ptype" == "syntactic" ]]; then
|
|
1213
|
+
pregex=$(echo "$fm" | awk '/^pattern:/{ip=1;next} ip && /^[^[:space:]]/{ip=0} ip && /^[[:space:]]+regex:/{sub(/^[[:space:]]+regex:[[:space:]]+/,""); gsub(/^["'"'"']|["'"'"']$/,""); print}')
|
|
1214
|
+
assert_true "$base: syntactic has regex" '[[ -n "$pregex" ]]'
|
|
1215
|
+
|
|
1216
|
+
# Test regex compiles with grep -P
|
|
1217
|
+
pregex_unesc="${pregex//\\\\/\\}"
|
|
1218
|
+
compile_ok=true
|
|
1219
|
+
echo "" | grep -P "$pregex_unesc" >/dev/null 2>&1 || { [[ $? -le 1 ]] || compile_ok=false; }
|
|
1220
|
+
assert_true "$base: regex compiles" '$compile_ok'
|
|
1221
|
+
fi
|
|
1222
|
+
done
|
|
1223
|
+
|
|
1224
|
+
# --- Summary ---
|
|
1225
|
+
echo ""
|
|
1226
|
+
echo "Results: $((TESTS - FAILURES))/$TESTS passed"
|
|
1227
|
+
if [[ $FAILURES -gt 0 ]]; then
|
|
1228
|
+
echo "FAILURES: $FAILURES"
|
|
1229
|
+
exit 1
|
|
1230
|
+
fi
|
|
1231
|
+
echo "ALL PASSED"
|
|
1232
|
+
```
|
|
1233
|
+
|
|
1234
|
+
**Step 2: Run test**
|
|
1235
|
+
|
|
1236
|
+
```bash
|
|
1237
|
+
bash scripts/tests/test-lesson-schema.sh
|
|
1238
|
+
```
|
|
1239
|
+
|
|
1240
|
+
**Step 3: Commit**
|
|
1241
|
+
|
|
1242
|
+
```bash
|
|
1243
|
+
git add scripts/tests/test-lesson-schema.sh
|
|
1244
|
+
git commit -m "test: add lesson schema validation tests"
|
|
1245
|
+
```
|
|
1246
|
+
|
|
1247
|
+
### Task 15: Create test-plugin-manifests.sh (JSON + frontmatter validation)
|
|
1248
|
+
|
|
1249
|
+
**Files:**
|
|
1250
|
+
- Create: `scripts/tests/test-plugin-manifests.sh`
|
|
1251
|
+
|
|
1252
|
+
**Step 1: Write test-plugin-manifests.sh**
|
|
1253
|
+
|
|
1254
|
+
Create `scripts/tests/test-plugin-manifests.sh`:
|
|
1255
|
+
|
|
1256
|
+
```bash
|
|
1257
|
+
#!/usr/bin/env bash
|
|
1258
|
+
# Test plugin manifests, hooks.json, skill frontmatters, and command frontmatters
|
|
1259
|
+
set -euo pipefail
|
|
1260
|
+
|
|
1261
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
1262
|
+
REPO_ROOT="$SCRIPT_DIR/../.."
|
|
1263
|
+
|
|
1264
|
+
FAILURES=0
|
|
1265
|
+
TESTS=0
|
|
1266
|
+
|
|
1267
|
+
assert_true() {
|
|
1268
|
+
local desc="$1" condition="$2"
|
|
1269
|
+
TESTS=$((TESTS + 1))
|
|
1270
|
+
if eval "$condition"; then
|
|
1271
|
+
echo "PASS: $desc"
|
|
1272
|
+
else
|
|
1273
|
+
echo "FAIL: $desc"
|
|
1274
|
+
FAILURES=$((FAILURES + 1))
|
|
1275
|
+
fi
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
# --- JSON validity ---
|
|
1279
|
+
assert_true "plugin.json is valid JSON" 'jq . "$REPO_ROOT/.claude-plugin/plugin.json" >/dev/null 2>&1'
|
|
1280
|
+
assert_true "marketplace.json is valid JSON" 'jq . "$REPO_ROOT/.claude-plugin/marketplace.json" >/dev/null 2>&1'
|
|
1281
|
+
assert_true "hooks.json is valid JSON" 'jq . "$REPO_ROOT/hooks/hooks.json" >/dev/null 2>&1'
|
|
1282
|
+
|
|
1283
|
+
# --- plugin.json required fields ---
|
|
1284
|
+
assert_true "plugin.json has name" 'jq -e ".name" "$REPO_ROOT/.claude-plugin/plugin.json" >/dev/null 2>&1'
|
|
1285
|
+
assert_true "plugin.json has description" 'jq -e ".description" "$REPO_ROOT/.claude-plugin/plugin.json" >/dev/null 2>&1'
|
|
1286
|
+
assert_true "plugin.json has version" 'jq -e ".version" "$REPO_ROOT/.claude-plugin/plugin.json" >/dev/null 2>&1'
|
|
1287
|
+
assert_true "plugin.json has author" 'jq -e ".author" "$REPO_ROOT/.claude-plugin/plugin.json" >/dev/null 2>&1'
|
|
1288
|
+
|
|
1289
|
+
# --- marketplace.json required fields ---
|
|
1290
|
+
assert_true "marketplace.json has \$schema" 'jq -e ".\"\\$schema\"" "$REPO_ROOT/.claude-plugin/marketplace.json" >/dev/null 2>&1'
|
|
1291
|
+
assert_true "marketplace.json has plugins" 'jq -e ".plugins" "$REPO_ROOT/.claude-plugin/marketplace.json" >/dev/null 2>&1'
|
|
1292
|
+
|
|
1293
|
+
# --- hooks.json structure ---
|
|
1294
|
+
assert_true "hooks.json has hooks.Stop" 'jq -e ".hooks.Stop" "$REPO_ROOT/hooks/hooks.json" >/dev/null 2>&1'
|
|
1295
|
+
assert_true "hooks.json references stop-hook.sh" 'jq -r ".hooks.Stop[0].hooks[0].command" "$REPO_ROOT/hooks/hooks.json" | grep -q "stop-hook.sh"'
|
|
1296
|
+
|
|
1297
|
+
# --- Skill frontmatters ---
|
|
1298
|
+
for f in "$REPO_ROOT"/skills/*/SKILL.md; do
|
|
1299
|
+
base=$(basename "$(dirname "$f")")
|
|
1300
|
+
has_name=$(awk '/^---$/{c++; if(c==2) exit} c==1 && /^name:/{print "yes"}' "$f")
|
|
1301
|
+
has_desc=$(awk '/^---$/{c++; if(c==2) exit} c==1 && /^description:/{print "yes"}' "$f")
|
|
1302
|
+
has_ver=$(awk '/^---$/{c++; if(c==2) exit} c==1 && /^version:/{print "yes"}' "$f")
|
|
1303
|
+
assert_true "skill $base has name" '[[ "$has_name" == "yes" ]]'
|
|
1304
|
+
assert_true "skill $base has description" '[[ "$has_desc" == "yes" ]]'
|
|
1305
|
+
assert_true "skill $base has version" '[[ "$has_ver" == "yes" ]]'
|
|
1306
|
+
done
|
|
1307
|
+
|
|
1308
|
+
# --- Command frontmatters ---
|
|
1309
|
+
for f in "$REPO_ROOT"/commands/*.md; do
|
|
1310
|
+
base=$(basename "$f")
|
|
1311
|
+
has_fm=$(awk '/^---$/{c++} c==2{print "yes"; exit}' "$f")
|
|
1312
|
+
assert_true "command $base has frontmatter" '[[ "$has_fm" == "yes" ]]'
|
|
1313
|
+
done
|
|
1314
|
+
|
|
1315
|
+
# --- Summary ---
|
|
1316
|
+
echo ""
|
|
1317
|
+
echo "Results: $((TESTS - FAILURES))/$TESTS passed"
|
|
1318
|
+
if [[ $FAILURES -gt 0 ]]; then
|
|
1319
|
+
echo "FAILURES: $FAILURES"
|
|
1320
|
+
exit 1
|
|
1321
|
+
fi
|
|
1322
|
+
echo "ALL PASSED"
|
|
1323
|
+
```
|
|
1324
|
+
|
|
1325
|
+
**Step 2: Run test**
|
|
1326
|
+
|
|
1327
|
+
```bash
|
|
1328
|
+
bash scripts/tests/test-plugin-manifests.sh
|
|
1329
|
+
```
|
|
1330
|
+
|
|
1331
|
+
**Step 3: Commit**
|
|
1332
|
+
|
|
1333
|
+
```bash
|
|
1334
|
+
git add scripts/tests/test-plugin-manifests.sh
|
|
1335
|
+
git commit -m "test: add plugin manifest and frontmatter validation tests"
|
|
1336
|
+
```
|
|
1337
|
+
|
|
1338
|
+
### Task 16: Final integration verification
|
|
1339
|
+
|
|
1340
|
+
**Files:**
|
|
1341
|
+
- Read: all test files
|
|
1342
|
+
|
|
1343
|
+
**Step 1: Run full test suite**
|
|
1344
|
+
|
|
1345
|
+
```bash
|
|
1346
|
+
bash scripts/tests/run-all-tests.sh
|
|
1347
|
+
```
|
|
1348
|
+
|
|
1349
|
+
Expected: All tests pass across all test files (old + new)
|
|
1350
|
+
|
|
1351
|
+
**Step 2: Count total tests**
|
|
1352
|
+
|
|
1353
|
+
```bash
|
|
1354
|
+
bash scripts/tests/run-all-tests.sh 2>&1 | tail -5
|
|
1355
|
+
```
|
|
1356
|
+
|
|
1357
|
+
Expected: Total should be 170+ (109 existing + 60+ new)
|
|
1358
|
+
|
|
1359
|
+
**Step 3: Run shellcheck one final time**
|
|
1360
|
+
|
|
1361
|
+
```bash
|
|
1362
|
+
shellcheck -s bash scripts/tests/test-*.sh
|
|
1363
|
+
```
|
|
1364
|
+
|
|
1365
|
+
Expected: Clean (our test files should also pass shellcheck)
|
|
1366
|
+
|
|
1367
|
+
**Step 4: Commit any final fixes**
|
|
1368
|
+
|
|
1369
|
+
```bash
|
|
1370
|
+
git add -A
|
|
1371
|
+
git commit -m "test: final integration verification — all tests pass"
|
|
1372
|
+
```
|
|
1373
|
+
|
|
1374
|
+
**Step 5: Push to remote**
|
|
1375
|
+
|
|
1376
|
+
```bash
|
|
1377
|
+
git push origin main
|
|
1378
|
+
```
|