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,125 @@
|
|
|
1
|
+
# Contributing Lessons
|
|
2
|
+
|
|
3
|
+
The autonomous-coding-toolkit improves with every user's production failures. When you encounter a bug caused by an anti-pattern, you can submit it as a lesson that becomes an automated check for every user.
|
|
4
|
+
|
|
5
|
+
## How Lessons Become Checks
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
You encounter a bug
|
|
9
|
+
→ Run /submit-lesson to capture it
|
|
10
|
+
→ PR is opened against this repo
|
|
11
|
+
→ Maintainer reviews and merges
|
|
12
|
+
→ Lesson file lands in docs/lessons/
|
|
13
|
+
→ lesson-check.sh picks up syntactic patterns automatically
|
|
14
|
+
→ lesson-scanner agent picks up semantic patterns automatically
|
|
15
|
+
→ Every user's next scan catches that anti-pattern
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Two tiers of enforcement:**
|
|
19
|
+
|
|
20
|
+
| Tier | Type | Speed | How it works |
|
|
21
|
+
|------|------|-------|-------------|
|
|
22
|
+
| Fast | Syntactic (grep-detectable) | <2 seconds | `lesson-check.sh` reads the lesson's `regex` field and runs `grep -P` |
|
|
23
|
+
| Deep | Semantic (needs context) | Minutes | `lesson-scanner` agent reads the lesson's `description` and `example` fields |
|
|
24
|
+
|
|
25
|
+
Adding a lesson file is all it takes — no code changes to the scanner or check script.
|
|
26
|
+
|
|
27
|
+
## Submitting a Lesson
|
|
28
|
+
|
|
29
|
+
### Option 1: Use the `/submit-lesson` command (recommended)
|
|
30
|
+
|
|
31
|
+
Inside a Claude Code session with this toolkit installed:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
/submit-lesson "bare except clauses hide failures in production"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The command walks you through:
|
|
38
|
+
1. Describing the bug
|
|
39
|
+
2. Classifying severity and category
|
|
40
|
+
3. Generating a grep pattern (if syntactic)
|
|
41
|
+
4. Writing the lesson file
|
|
42
|
+
5. Opening a PR
|
|
43
|
+
|
|
44
|
+
### Option 2: Manual PR
|
|
45
|
+
|
|
46
|
+
1. Copy `docs/lessons/TEMPLATE.md` to `docs/lessons/NNNN-<slug>.md`
|
|
47
|
+
2. Fill in the YAML frontmatter and body sections
|
|
48
|
+
3. Open a PR with title: `lesson: <short description>`
|
|
49
|
+
|
|
50
|
+
## Lesson File Format
|
|
51
|
+
|
|
52
|
+
Every lesson file has YAML frontmatter that the tools parse:
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
---
|
|
56
|
+
id: 7 # Auto-assigned sequential ID
|
|
57
|
+
title: "Short descriptive title"
|
|
58
|
+
severity: blocker # blocker | should-fix | nice-to-have
|
|
59
|
+
languages: [python] # python | javascript | typescript | shell | all
|
|
60
|
+
category: silent-failures # See categories below
|
|
61
|
+
pattern:
|
|
62
|
+
type: syntactic # syntactic | semantic
|
|
63
|
+
regex: "^\\s*except\\s*:" # grep -P pattern (syntactic only)
|
|
64
|
+
description: "what to look for"
|
|
65
|
+
fix: "how to fix it"
|
|
66
|
+
example:
|
|
67
|
+
bad: |
|
|
68
|
+
<anti-pattern code>
|
|
69
|
+
good: |
|
|
70
|
+
<correct code>
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Observation
|
|
74
|
+
[What happened — the bug, the symptom]
|
|
75
|
+
|
|
76
|
+
## Insight
|
|
77
|
+
[Why it happened — the root cause]
|
|
78
|
+
|
|
79
|
+
## Lesson
|
|
80
|
+
[The rule to follow going forward]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Categories
|
|
84
|
+
|
|
85
|
+
| Category | What it covers |
|
|
86
|
+
|----------|---------------|
|
|
87
|
+
| `async-traps` | Forgotten awaits, concurrent modification, coroutine misuse |
|
|
88
|
+
| `resource-lifecycle` | Leaked connections, missing cleanup, subscription without unsubscribe |
|
|
89
|
+
| `silent-failures` | Bare exceptions, swallowed errors, lost stack traces |
|
|
90
|
+
| `integration-boundaries` | Cross-module bugs, path issues, API contract mismatches |
|
|
91
|
+
| `test-anti-patterns` | Brittle assertions, mocking the wrong thing, false confidence |
|
|
92
|
+
| `performance` | Missing filters, unnecessary work, resource waste |
|
|
93
|
+
|
|
94
|
+
## Severity Guide
|
|
95
|
+
|
|
96
|
+
| Severity | When to use | Examples |
|
|
97
|
+
|----------|------------|---------|
|
|
98
|
+
| `blocker` | Data loss, crashes, silent corruption | Bare except swallowing errors, async def returning coroutine instead of result |
|
|
99
|
+
| `should-fix` | Subtle bugs, degraded behavior, tech debt | Leaked connections, hardcoded test counts, missing callbacks |
|
|
100
|
+
| `nice-to-have` | Code smells, future risks, style | Naming issues, missing type hints, suboptimal patterns |
|
|
101
|
+
|
|
102
|
+
## Quality Bar
|
|
103
|
+
|
|
104
|
+
Before submitting, verify:
|
|
105
|
+
|
|
106
|
+
1. **Real bug** — The lesson comes from a bug you actually encountered, not a hypothetical
|
|
107
|
+
2. **Regex accuracy** (syntactic only) — The regex catches the bad example and does NOT match the good example
|
|
108
|
+
3. **Clear description** — Someone unfamiliar with your codebase can understand the anti-pattern
|
|
109
|
+
4. **Actionable fix** — The fix section tells you what to do, not just "be careful"
|
|
110
|
+
5. **Realistic example** — The bad/good examples are from real code (anonymized if needed)
|
|
111
|
+
|
|
112
|
+
## Review Process
|
|
113
|
+
|
|
114
|
+
1. **Automated checks** — PR CI verifies the YAML frontmatter parses correctly and required fields are present
|
|
115
|
+
2. **Regex testing** — Maintainer tests the regex against real codebases for false positive rate
|
|
116
|
+
3. **Category review** — Maintainer verifies severity and category are appropriate
|
|
117
|
+
4. **Merge** — Once approved, the lesson is immediately available to all users on next toolkit update
|
|
118
|
+
|
|
119
|
+
## What Makes a Great Lesson
|
|
120
|
+
|
|
121
|
+
The best lessons:
|
|
122
|
+
- Come from bugs that took >30 minutes to debug
|
|
123
|
+
- Have a syntactic pattern (grep-detectable = instant enforcement for everyone)
|
|
124
|
+
- Apply across multiple projects (not just your specific codebase)
|
|
125
|
+
- Include the "why" — understanding the mechanism prevents the entire class of bug, not just this instance
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 1
|
|
3
|
+
title: "Bare exception swallowing hides failures"
|
|
4
|
+
severity: blocker
|
|
5
|
+
languages: [python]
|
|
6
|
+
scope: [language:python]
|
|
7
|
+
category: silent-failures
|
|
8
|
+
pattern:
|
|
9
|
+
type: syntactic
|
|
10
|
+
regex: "^\\s*except\\s*:"
|
|
11
|
+
description: "bare except clause without logging"
|
|
12
|
+
fix: "Always log the exception before returning a fallback: except Exception as e: logger.error(..., exc_info=True)"
|
|
13
|
+
example:
|
|
14
|
+
bad: |
|
|
15
|
+
try:
|
|
16
|
+
result = api_call()
|
|
17
|
+
except:
|
|
18
|
+
return default_value
|
|
19
|
+
good: |
|
|
20
|
+
try:
|
|
21
|
+
result = api_call()
|
|
22
|
+
except Exception as e:
|
|
23
|
+
logger.error("API call failed", exc_info=True)
|
|
24
|
+
return default_value
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Observation
|
|
28
|
+
Bare `except:` clauses silently swallow all exceptions including KeyboardInterrupt, SystemExit, and MemoryError. When the fallback value is returned, there's no log trail to indicate a failure occurred, making debugging impossible.
|
|
29
|
+
|
|
30
|
+
## Insight
|
|
31
|
+
The root cause is a habit of writing "safe" exception handling that catches everything. The Python exception hierarchy means `except:` catches far more than intended. Combined with no logging, failures become invisible.
|
|
32
|
+
|
|
33
|
+
## Lesson
|
|
34
|
+
Never use bare `except:` — always catch a specific exception class and log before returning a fallback. The 3-line rule: within 3 lines of an except clause, there must be a logging call.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 2
|
|
3
|
+
title: "async def without await returns truthy coroutine"
|
|
4
|
+
severity: blocker
|
|
5
|
+
languages: [python]
|
|
6
|
+
scope: [language:python]
|
|
7
|
+
category: async-traps
|
|
8
|
+
pattern:
|
|
9
|
+
type: semantic
|
|
10
|
+
description: "async def function body contains no await, async for, or async with — returns coroutine object instead of result"
|
|
11
|
+
fix: "Either add await for async I/O operations, or remove the async keyword if the function does no async work"
|
|
12
|
+
example:
|
|
13
|
+
bad: |
|
|
14
|
+
async def get_data():
|
|
15
|
+
return database.query("SELECT *") # Returns coroutine, not result
|
|
16
|
+
good: |
|
|
17
|
+
async def get_data():
|
|
18
|
+
return await database.query("SELECT *")
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Observation
|
|
22
|
+
An `async def` function that never uses `await`, `async for`, or `async with` returns a coroutine object instead of its result. Since coroutine objects are truthy, code like `if await get_data():` silently succeeds with a truthy coroutine even when the actual data would be falsy.
|
|
23
|
+
|
|
24
|
+
## Insight
|
|
25
|
+
This is insidious because the function appears to work — it returns something truthy, no exceptions are raised, no warnings are logged. The bug only surfaces when the return value is used for its actual content rather than truthiness.
|
|
26
|
+
|
|
27
|
+
## Lesson
|
|
28
|
+
Every `async def` must contain at least one `await`, `async for`, or `async with`. If it doesn't need any, remove the `async` keyword. This check requires multi-line analysis (scanning the full function body), so it's a semantic check in the lesson-scanner.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 3
|
|
3
|
+
title: "asyncio.create_task without done_callback swallows exceptions"
|
|
4
|
+
severity: should-fix
|
|
5
|
+
languages: [python]
|
|
6
|
+
scope: [language:python]
|
|
7
|
+
category: silent-failures
|
|
8
|
+
pattern:
|
|
9
|
+
type: semantic
|
|
10
|
+
description: "create_task() call without add_done_callback within 5 lines — untracked task may swallow exceptions silently"
|
|
11
|
+
fix: "Add a done_callback that logs exceptions: task.add_done_callback(lambda t: t.exception() and logger.error(...))"
|
|
12
|
+
example:
|
|
13
|
+
bad: |
|
|
14
|
+
task = asyncio.create_task(process_event(data))
|
|
15
|
+
# No callback — if process_event raises, you'll never know
|
|
16
|
+
good: |
|
|
17
|
+
task = asyncio.create_task(process_event(data))
|
|
18
|
+
task.add_done_callback(lambda t: t.exception() and logger.error("Task failed", exc_info=t.exception()))
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Observation
|
|
22
|
+
`asyncio.create_task()` launches a coroutine as a background task. If the task raises an exception and nobody awaits it or checks its result, Python logs a "Task exception was never retrieved" warning at garbage collection time — which may be much later or not at all.
|
|
23
|
+
|
|
24
|
+
## Insight
|
|
25
|
+
Fire-and-forget tasks are a common pattern but they create invisible failure paths. The exception is silently stored in the task object and only surfaces (maybe) when the task is garbage collected.
|
|
26
|
+
|
|
27
|
+
## Lesson
|
|
28
|
+
Every `create_task()` call should be followed within 5 lines by `add_done_callback()` that handles exceptions. Alternatively, store the task and await it later.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 4
|
|
3
|
+
title: "Hardcoded count assertions break when datasets grow"
|
|
4
|
+
severity: should-fix
|
|
5
|
+
languages: [python, javascript, typescript]
|
|
6
|
+
scope: [universal]
|
|
7
|
+
category: test-anti-patterns
|
|
8
|
+
pattern:
|
|
9
|
+
type: syntactic
|
|
10
|
+
regex: "assert.*==\\s*\\d+|expect\\(.*\\)\\.toBe\\(\\d+\\)|assert_equal.*\\d+"
|
|
11
|
+
description: "test assertion comparing count to a hardcoded number"
|
|
12
|
+
fix: "Use >= for extensible collections, or assert against a computed expected value rather than a magic number"
|
|
13
|
+
example:
|
|
14
|
+
bad: |
|
|
15
|
+
assert len(collectors) == 15 # Breaks when a 16th collector is added
|
|
16
|
+
good: |
|
|
17
|
+
assert len(collectors) >= 15 # Passes as collection grows
|
|
18
|
+
# Or better: assert expected_collector in collectors
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Observation
|
|
22
|
+
Tests that assert exact counts (e.g., `assert len(items) == 15`) break every time a new item is added to an extensible collection. This creates friction where adding a feature requires updating unrelated test files.
|
|
23
|
+
|
|
24
|
+
## Insight
|
|
25
|
+
Exact count assertions conflate "the collection is not empty and has the expected items" with "the collection has exactly N items." The former is what you usually want to test; the latter creates brittle coupling.
|
|
26
|
+
|
|
27
|
+
## Lesson
|
|
28
|
+
For extensible collections, use `>=` assertions or check for specific members. Reserve exact count assertions for fixed-size structures where the count is genuinely part of the contract.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 5
|
|
3
|
+
title: "sqlite3 connections leak without closing() context manager"
|
|
4
|
+
severity: should-fix
|
|
5
|
+
languages: [python]
|
|
6
|
+
scope: [language:python]
|
|
7
|
+
category: silent-failures
|
|
8
|
+
pattern:
|
|
9
|
+
type: syntactic
|
|
10
|
+
regex: "sqlite3\\.connect\\("
|
|
11
|
+
description: "sqlite3.connect() — verify closing() context manager is used (with conn: manages transactions, not connections)"
|
|
12
|
+
fix: "Use contextlib.closing(): with closing(sqlite3.connect(db_path)) as conn:"
|
|
13
|
+
example:
|
|
14
|
+
bad: |
|
|
15
|
+
conn = sqlite3.connect("data.db")
|
|
16
|
+
with conn:
|
|
17
|
+
conn.execute("INSERT ...")
|
|
18
|
+
# Connection never explicitly closed — relies on GC
|
|
19
|
+
good: |
|
|
20
|
+
from contextlib import closing
|
|
21
|
+
with closing(sqlite3.connect("data.db")) as conn:
|
|
22
|
+
with conn:
|
|
23
|
+
conn.execute("INSERT ...")
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Observation
|
|
27
|
+
`with conn:` in sqlite3 manages transactions (auto-commit/rollback), NOT the connection lifecycle. The connection remains open until garbage collected. Under load or in long-running processes, this leaks file descriptors.
|
|
28
|
+
|
|
29
|
+
## Insight
|
|
30
|
+
Python's sqlite3 `with` statement is misleading — it looks like a resource manager but only manages transactions. The actual connection close requires either `conn.close()` or `contextlib.closing()`.
|
|
31
|
+
|
|
32
|
+
## Lesson
|
|
33
|
+
Always wrap `sqlite3.connect()` in `contextlib.closing()` for reliable cleanup. The pattern is: `with closing(connect(...)) as conn: with conn: ...` — outer for lifecycle, inner for transactions.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 6
|
|
3
|
+
title: ".venv/bin/pip installs to wrong site-packages"
|
|
4
|
+
severity: should-fix
|
|
5
|
+
languages: [python, shell]
|
|
6
|
+
scope: [language:python, framework:pytest]
|
|
7
|
+
category: integration-boundaries
|
|
8
|
+
pattern:
|
|
9
|
+
type: syntactic
|
|
10
|
+
regex: "\\.venv/bin/pip\\b"
|
|
11
|
+
description: ".venv/bin/pip instead of .venv/bin/python -m pip — pip shebang may point to wrong Python"
|
|
12
|
+
fix: "Use .venv/bin/python -m pip to ensure packages install into the correct virtual environment"
|
|
13
|
+
example:
|
|
14
|
+
bad: |
|
|
15
|
+
.venv/bin/pip install requests
|
|
16
|
+
good: |
|
|
17
|
+
.venv/bin/python -m pip install requests
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Observation
|
|
21
|
+
When multiple Python versions exist on the system (e.g., system Python + Homebrew Python), `.venv/bin/pip` may resolve to the wrong Python interpreter via its shebang line. Packages install into the wrong site-packages directory, making them invisible to the venv's Python.
|
|
22
|
+
|
|
23
|
+
## Insight
|
|
24
|
+
The pip executable's shebang (`#!/path/to/python`) is set at venv creation time. If PATH changes or another Python is installed later, the shebang becomes stale. Using `python -m pip` always uses the Python that's running it.
|
|
25
|
+
|
|
26
|
+
## Lesson
|
|
27
|
+
Never call `.venv/bin/pip` directly. Always use `.venv/bin/python -m pip` to guarantee the correct interpreter and site-packages directory.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 7
|
|
3
|
+
title: "Runner state file rejected by own git-clean check"
|
|
4
|
+
severity: should-fix
|
|
5
|
+
languages: [shell, all]
|
|
6
|
+
scope: [project:autonomous-coding-toolkit]
|
|
7
|
+
category: integration-boundaries
|
|
8
|
+
pattern:
|
|
9
|
+
type: semantic
|
|
10
|
+
description: "Tool-generated state files rejected by the tool's own git-clean check"
|
|
11
|
+
fix: "Add tool-generated state files to .gitignore before the first run"
|
|
12
|
+
example:
|
|
13
|
+
bad: |
|
|
14
|
+
#!/bin/bash
|
|
15
|
+
# Runner creates state file
|
|
16
|
+
echo '{"batch":1}' > .run-plan-state.json
|
|
17
|
+
# Later, quality gate rejects it
|
|
18
|
+
git status --porcelain | grep -q . && echo "ERROR: untracked files"
|
|
19
|
+
good: |
|
|
20
|
+
# .gitignore includes state file
|
|
21
|
+
echo ".run-plan-state.json" >> .gitignore
|
|
22
|
+
# Runner creates state file (now ignored)
|
|
23
|
+
echo '{"batch":1}' > .run-plan-state.json
|
|
24
|
+
# Quality gate passes
|
|
25
|
+
git status --porcelain | grep -q . || echo "OK: repo clean"
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Observation
|
|
29
|
+
A tool (e.g., plan runner) creates a state file (e.g., `.run-plan-state.json`) to persist execution progress. The tool's own quality gate includes a git-clean check that rejects untracked files. The tool creates the file, then its quality gate fails because the file is untracked.
|
|
30
|
+
|
|
31
|
+
## Insight
|
|
32
|
+
The root cause is a missing `.gitignore` entry. Tool-generated state files are not version-controlled artifacts — they're runtime metadata. If the tool creates them but the `.gitignore` doesn't exclude them, every tool run will create a file that the next quality gate rejects. This is a self-inflicted tooling loop.
|
|
33
|
+
|
|
34
|
+
## Lesson
|
|
35
|
+
Any tool that generates state files must add those files to `.gitignore` before the tool's first run. State files (`.state.json`, `.run-plan-state.json`, progress markers) are never version-controlled. The `.gitignore` is part of the tool's bootstrap, not the tool's runtime.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 8
|
|
3
|
+
title: "Quality gate blind spot for non-standard test suites"
|
|
4
|
+
severity: should-fix
|
|
5
|
+
languages: [shell, all]
|
|
6
|
+
scope: [project:autonomous-coding-toolkit]
|
|
7
|
+
category: silent-failures
|
|
8
|
+
pattern:
|
|
9
|
+
type: semantic
|
|
10
|
+
description: "Quality gate auto-detects only standard test frameworks, missing custom test suites"
|
|
11
|
+
fix: "Detect custom test runners by convention (executable run-all-tests.sh, test-*.sh glob)"
|
|
12
|
+
example:
|
|
13
|
+
bad: |
|
|
14
|
+
# Quality gate checks only standard frameworks
|
|
15
|
+
if [[ -f pytest.ini ]]; then pytest; fi
|
|
16
|
+
if [[ -f package.json ]]; then npm test; fi
|
|
17
|
+
if [[ -f Makefile ]]; then make test; fi
|
|
18
|
+
# Custom bash suite test-*.sh is never discovered
|
|
19
|
+
good: |
|
|
20
|
+
# Also check for executable test runners and test globs
|
|
21
|
+
if [[ -x run-all-tests.sh ]]; then ./run-all-tests.sh; fi
|
|
22
|
+
if ls test-*.sh &>/dev/null; then for t in test-*.sh; do ./"$t"; done; fi
|
|
23
|
+
# Plus standard framework checks...
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Observation
|
|
27
|
+
Quality gates that auto-detect test frameworks (pytest, npm test, make test) fail to discover custom test suites written as bash scripts (`test-integration.sh`, `test-smoke.sh`) or other non-standard runners. The gate reports "no tests detected" and passes, while hundreds of assertions exist and are never executed.
|
|
28
|
+
|
|
29
|
+
## Insight
|
|
30
|
+
The root cause is convention-based detection that assumes all projects use a standard framework. Custom runners are often ignored because they're not a recognized pattern. The gate author knows pytest/npm/make but not the project's own conventions, leading to a blind spot.
|
|
31
|
+
|
|
32
|
+
## Lesson
|
|
33
|
+
Quality gates must detect tests by multiple conventions: (1) standard frameworks (pytest, npm test, make), (2) executable scripts matching `test-*.sh`, and (3) a convention file like `run-all-tests.sh` that the project defines. If a gate only knows standard frameworks, it will miss half the projects using custom runners.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 9
|
|
3
|
+
title: "Plan parser over-count burns empty API calls"
|
|
4
|
+
severity: should-fix
|
|
5
|
+
languages: [shell, all]
|
|
6
|
+
scope: [project:autonomous-coding-toolkit]
|
|
7
|
+
category: silent-failures
|
|
8
|
+
pattern:
|
|
9
|
+
type: semantic
|
|
10
|
+
description: "Plan parser counts batch headers without checking if batch has content"
|
|
11
|
+
fix: "Check get_batch_text is non-empty before executing a batch"
|
|
12
|
+
example:
|
|
13
|
+
bad: |
|
|
14
|
+
# Parser counts headers
|
|
15
|
+
batch_count=$(grep -c "^## Batch" plan.md)
|
|
16
|
+
for batch in $(seq 1 $batch_count); do
|
|
17
|
+
# Each batch triggers an agent, even if empty
|
|
18
|
+
run_batch "$batch"
|
|
19
|
+
done
|
|
20
|
+
good: |
|
|
21
|
+
# Parser checks if batch has content
|
|
22
|
+
for batch in $(seq 1 $batch_count); do
|
|
23
|
+
text=$(get_batch_text "$batch")
|
|
24
|
+
[[ -z "$text" ]] && continue # Skip empty batches
|
|
25
|
+
run_batch "$batch"
|
|
26
|
+
done
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Observation
|
|
30
|
+
A plan parser counts batch headers (e.g., `## Batch 1`, `## Batch 2`) to determine how many batches to execute. If the plan has trailing or empty headers, the count includes them. Each phantom batch spawns an agent context that discovers "nothing to do" — a wasted API call, time, and context.
|
|
31
|
+
|
|
32
|
+
## Insight
|
|
33
|
+
The root cause is counting headers separately from extracting batch content. A header exists (line count is easy) but its content may be empty. Parser assumes every header has work, which is false for malformed or trailing headers.
|
|
34
|
+
|
|
35
|
+
## Lesson
|
|
36
|
+
Never count batch headers separately. Always extract the batch content first, then check if it's non-empty before executing. Skip empty batches silently. This prevents wasted agent spawns and keeps the pipeline efficient.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 10
|
|
3
|
+
title: "`local` outside function silently misbehaves in bash"
|
|
4
|
+
severity: blocker
|
|
5
|
+
languages: [shell]
|
|
6
|
+
scope: [language:bash]
|
|
7
|
+
category: silent-failures
|
|
8
|
+
pattern:
|
|
9
|
+
type: syntactic
|
|
10
|
+
regex: "^local "
|
|
11
|
+
description: "`local` keyword used at script top-level, outside any function"
|
|
12
|
+
fix: "Never use `local` outside a function; use plain variable assignment at script scope"
|
|
13
|
+
example:
|
|
14
|
+
bad: |
|
|
15
|
+
#!/bin/bash
|
|
16
|
+
local result="value"
|
|
17
|
+
echo "Result: $result"
|
|
18
|
+
# Works on some shells, fails/ignored on others
|
|
19
|
+
good: |
|
|
20
|
+
#!/bin/bash
|
|
21
|
+
result="value"
|
|
22
|
+
echo "Result: $result"
|
|
23
|
+
# Works consistently across all shells
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Observation
|
|
27
|
+
A bash script uses the `local` keyword at script top-level, outside any function. The script works on one machine but fails on another, or silently produces empty values on a third. The `local` keyword is not portable when used outside function scope.
|
|
28
|
+
|
|
29
|
+
## Insight
|
|
30
|
+
In bash, `local` is only defined for use within functions — it declares a variable in the local function scope. At script scope, `local` is undefined behavior. Some shells silently accept and ignore it (variable remains undefined), others error. Creating scripts that work on one machine but fail on another due to shell differences.
|
|
31
|
+
|
|
32
|
+
## Lesson
|
|
33
|
+
Never use `local` outside a function. At script scope, use plain variable assignment (`var=value`). Functions use `local var=value` for local scope. Script scope has no `local` keyword — all variables are global by default. Grep patterns checking for `^local ` are syntactic checks to catch this at write time.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 11
|
|
3
|
+
title: "Batch execution writes tests for unimplemented code"
|
|
4
|
+
severity: should-fix
|
|
5
|
+
languages: [all]
|
|
6
|
+
scope: [universal]
|
|
7
|
+
category: integration-boundaries
|
|
8
|
+
pattern:
|
|
9
|
+
type: semantic
|
|
10
|
+
description: "Tests in batch N reference code that won't exist until batch N+1"
|
|
11
|
+
fix: "Plan tasks so each batch is self-contained — tests only reference code from the same or earlier batches"
|
|
12
|
+
example:
|
|
13
|
+
bad: |
|
|
14
|
+
# Plan with forward-looking test
|
|
15
|
+
## Batch 3: Add tests for pipeline
|
|
16
|
+
- Write tests that call pipeline.transform() (not written yet)
|
|
17
|
+
|
|
18
|
+
## Batch 4: Implement pipeline
|
|
19
|
+
- Implement the transform() method
|
|
20
|
+
good: |
|
|
21
|
+
# Plan with self-contained batches
|
|
22
|
+
## Batch 3: Implement pipeline
|
|
23
|
+
- Implement the transform() method
|
|
24
|
+
|
|
25
|
+
## Batch 4: Add tests for pipeline
|
|
26
|
+
- Write tests that call pipeline.transform() (now exists)
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Observation
|
|
30
|
+
When a plan has batches 1-7 and batch 3's agent writes tests expecting batch 4's code, those tests fail until batch 4 runs. The agent does TDD correctly for its own batch (writes test, implements code) but accidentally creates forward dependencies when tests reference not-yet-implemented code from future batches.
|
|
31
|
+
|
|
32
|
+
## Insight
|
|
33
|
+
The root cause is batching by concern instead of by dependency. The planner thinks "batch 3 is tests, batch 4 is implementation" without considering that tests depend on the code existing. Tests can't pass (or even import) if the code they reference isn't in the codebase yet.
|
|
34
|
+
|
|
35
|
+
## Lesson
|
|
36
|
+
Plan tasks so each batch is self-contained. Tests reference only code from the same batch (TDD: write test, write code) or earlier batches (tested code). Never have batch N's tests reference batch N+1's code. If you need to test something, implement it in the same batch or an earlier batch.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 12
|
|
3
|
+
title: "API rejects markdown with unescaped special chars"
|
|
4
|
+
severity: nice-to-have
|
|
5
|
+
languages: [python, javascript, all]
|
|
6
|
+
scope: [universal]
|
|
7
|
+
category: integration-boundaries
|
|
8
|
+
pattern:
|
|
9
|
+
type: semantic
|
|
10
|
+
description: "Message API rejects text containing unescaped markdown special characters"
|
|
11
|
+
fix: "Either escape all special characters or use plain text mode as default with markdown as opt-in"
|
|
12
|
+
example:
|
|
13
|
+
bad: |
|
|
14
|
+
# Message with unescaped markdown chars
|
|
15
|
+
message = "Task: _compile_flags = ['-O2', '-Wall']"
|
|
16
|
+
response = api.send_message(message, parse_mode="Markdown")
|
|
17
|
+
# API rejects: parse error due to _ chars
|
|
18
|
+
good: |
|
|
19
|
+
# Either escape special chars
|
|
20
|
+
message = "Task: \\_compile\\_flags = ['-O2', '-Wall']"
|
|
21
|
+
response = api.send_message(message, parse_mode="Markdown")
|
|
22
|
+
# Or use plain text by default
|
|
23
|
+
response = api.send_message(message) # parse_mode=None
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Observation
|
|
27
|
+
An API with `parse_mode=Markdown` rejects messages containing unescaped special characters like `_`, `*`, `[`, `]`. The message silently fails or returns a parse error. Code that works with one message format fails with another that contains these characters.
|
|
28
|
+
|
|
29
|
+
## Insight
|
|
30
|
+
The root cause is assuming plain text is safe to send with markdown parsing enabled. Markdown interpreters are strict — any `_` or `*` is interpreted as formatting syntax. If the text literally needs to include these characters (like code snippets or file paths), they must be escaped or the parser must be disabled.
|
|
31
|
+
|
|
32
|
+
## Lesson
|
|
33
|
+
When sending messages to APIs with markdown parsing, either: (1) escape all special characters (`_`, `*`, `[`, `]`, `` ` ``) in the text, or (2) use plain text mode by default and require opt-in for markdown. Never assume plain text is safe with markdown parsing enabled.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 13
|
|
3
|
+
title: "`export` prefix in env files breaks naive parsing"
|
|
4
|
+
severity: should-fix
|
|
5
|
+
languages: [shell]
|
|
6
|
+
scope: [language:bash]
|
|
7
|
+
category: silent-failures
|
|
8
|
+
pattern:
|
|
9
|
+
type: syntactic
|
|
10
|
+
regex: "cut -d= -f2"
|
|
11
|
+
description: "Env file parser using cut without stripping export prefix"
|
|
12
|
+
fix: "Strip `export ` prefix before parsing: sed 's/^export //'"
|
|
13
|
+
example:
|
|
14
|
+
bad: |
|
|
15
|
+
# .env file with export prefix
|
|
16
|
+
# export API_KEY=secret123
|
|
17
|
+
# Read without stripping export
|
|
18
|
+
value=$(grep "API_KEY" .env | cut -d= -f2)
|
|
19
|
+
# Works for KEY=value but fails for export KEY=value
|
|
20
|
+
good: |
|
|
21
|
+
# Strip export prefix before parsing
|
|
22
|
+
value=$(grep "API_KEY" .env | sed 's/^export //' | cut -d= -f2)
|
|
23
|
+
# Works for both KEY=value and export KEY=value
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Observation
|
|
27
|
+
`.env` files commonly use `export VAR=value` syntax (for shell-source-ability). A parser uses `grep VAR= file | cut -d= -f2` to extract values. For lines like `KEY=value` it works fine. For lines with `export KEY=value`, the `cut` command returns the correct value, but if the parsing step checks for the line format first (e.g., expecting no `export` prefix), it silently skips those lines.
|
|
28
|
+
|
|
29
|
+
## Insight
|
|
30
|
+
The root cause is assuming `.env` format is always `KEY=value` without the `export` keyword. Many `.env` files use `export` for shell-sourcing (so they can be sourced with `source .env`). Parsers that don't account for this prefix will silently skip or misparse those lines.
|
|
31
|
+
|
|
32
|
+
## Lesson
|
|
33
|
+
`.env` file parsers should strip the `export` prefix before parsing. Use `sed 's/^export //'` to normalize lines, then parse. This handles both `KEY=value` and `export KEY=value` consistently. Never assume the format — always normalize first.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 14
|
|
3
|
+
title: "Decorator registries are import-time side effects"
|
|
4
|
+
severity: should-fix
|
|
5
|
+
languages: [python]
|
|
6
|
+
scope: [language:python]
|
|
7
|
+
category: silent-failures
|
|
8
|
+
pattern:
|
|
9
|
+
type: semantic
|
|
10
|
+
description: "Decorator-based registry remains empty because module with decorated functions is never imported"
|
|
11
|
+
fix: "Ensure all modules with registrations are imported in __init__.py or an explicit loader"
|
|
12
|
+
example:
|
|
13
|
+
bad: |
|
|
14
|
+
# handlers.py
|
|
15
|
+
@register("command")
|
|
16
|
+
def handle_command(): pass
|
|
17
|
+
|
|
18
|
+
# main.py
|
|
19
|
+
# registry.get_all() returns empty — handlers.py was never imported
|
|
20
|
+
from registry import registry
|
|
21
|
+
for handler in registry.get_all(): # Empty!
|
|
22
|
+
handler()
|
|
23
|
+
good: |
|
|
24
|
+
# handlers.py (same as above)
|
|
25
|
+
@register("command")
|
|
26
|
+
def handle_command(): pass
|
|
27
|
+
|
|
28
|
+
# main.py
|
|
29
|
+
# Explicitly import to trigger decorators
|
|
30
|
+
from . import handlers # This runs the decorators
|
|
31
|
+
from registry import registry
|
|
32
|
+
for handler in registry.get_all(): # Has handlers now
|
|
33
|
+
handler()
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Observation
|
|
37
|
+
A Python project uses decorator-based registration (`@register("name")` adds a function to a registry). The registry is empty at runtime even though decorated functions exist in the codebase. No error is raised — the registry just returns an empty list.
|
|
38
|
+
|
|
39
|
+
## Insight
|
|
40
|
+
Decorator-based registries execute at import time. If the module containing decorated functions is never imported (perhaps `main.py` only imports specific modules), the decorators never run and the registry stays empty. The mistake is assuming the module will be imported implicitly, when it must be imported explicitly.
|
|
41
|
+
|
|
42
|
+
## Lesson
|
|
43
|
+
Decorator-based registries require explicit imports of all modules that have decorated functions. Add imports to `__init__.py` or a loader module that's guaranteed to run. Document this requirement. Alternatively, use a registration function that's called explicitly, instead of relying on import-time side effects. Never assume a module is imported — always be explicit.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 15
|
|
3
|
+
title: "Frontend-backend schema drift invisible until e2e trace"
|
|
4
|
+
severity: should-fix
|
|
5
|
+
languages: [typescript, javascript, python, all]
|
|
6
|
+
scope: [universal]
|
|
7
|
+
category: integration-boundaries
|
|
8
|
+
pattern:
|
|
9
|
+
type: semantic
|
|
10
|
+
description: "Frontend and backend define the same data shape independently and drift over time"
|
|
11
|
+
fix: "Shared schema definition (types generated from API schema) or contract tests"
|
|
12
|
+
example:
|
|
13
|
+
bad: |
|
|
14
|
+
# Backend (Python)
|
|
15
|
+
def get_user():
|
|
16
|
+
return {"id": 1, "name": "Alice", "email": "alice@example.com"}
|
|
17
|
+
|
|
18
|
+
# Frontend (TypeScript, independent definition)
|
|
19
|
+
interface User { id: number; name: string; }
|
|
20
|
+
// Missing email field! Silent bug.
|
|
21
|
+
good: |
|
|
22
|
+
# Shared schema (OpenAPI/GraphQL)
|
|
23
|
+
components:
|
|
24
|
+
schemas:
|
|
25
|
+
User:
|
|
26
|
+
type: object
|
|
27
|
+
properties:
|
|
28
|
+
id: { type: integer }
|
|
29
|
+
name: { type: string }
|
|
30
|
+
email: { type: string }
|
|
31
|
+
|
|
32
|
+
# Generated TypeScript (from schema)
|
|
33
|
+
// User interface auto-generated, always in sync
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Observation
|
|
37
|
+
Frontend and backend define the same data shape (User, Product, etc.) independently. Over time they drift — backend adds an `email` field to User, frontend's User interface doesn't include it. The field is silently ignored on the frontend. No error until a feature tries to use the field and finds it missing.
|
|
38
|
+
|
|
39
|
+
## Insight
|
|
40
|
+
The root cause is separate schema definitions. Each layer maintains its own types, and they're never synchronized. Backend and frontend evolve independently. The drift is invisible because both sides are "correct" within their own codebase — TypeScript compiles, Python runs, API calls succeed. Only end-to-end traces reveal the mismatch.
|
|
41
|
+
|
|
42
|
+
## Lesson
|
|
43
|
+
Never define schemas independently in frontend and backend. Use a single source of truth: OpenAPI, GraphQL schema, Protobuf, or equivalent. Generate types from the shared schema. Alternatively, use contract tests that verify the API response matches what the frontend expects. The contract must be version-controlled and tested.
|