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,968 @@
|
|
|
1
|
+
# Lesson Scope Metadata Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
4
|
+
|
|
5
|
+
**Goal:** Add project-level scope filtering to lesson-check.sh so domain-specific lessons (HA, Telegram, etc.) only fire on relevant projects, reducing false positives as lesson count grows past 100.
|
|
6
|
+
|
|
7
|
+
**Architecture:** lesson-check.sh gains a `scope:` field in YAML frontmatter (demand side) and reads `## Scope Tags` from the project's CLAUDE.md (supply side). A new `scope_matches()` function gates lessons before the existing language/grep check. A new `scope-infer.sh` script handles bulk tagging of existing lessons.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Bash, YAML frontmatter parsing, grep, jq (none — pure bash)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Batch 1: Scope Parsing & Matching (Core Engine)
|
|
14
|
+
|
|
15
|
+
### Task 1: Add scope parsing tests to test-lesson-check.sh
|
|
16
|
+
|
|
17
|
+
**Files:**
|
|
18
|
+
- Modify: `scripts/tests/test-lesson-check.sh`
|
|
19
|
+
|
|
20
|
+
**Step 1: Write failing tests for scope field parsing**
|
|
21
|
+
|
|
22
|
+
Add these tests after the existing Test 8 block (before the Summary section at line 153):
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# --- Test 9: Scope field parsed from lesson YAML ---
|
|
26
|
+
# Create a lesson with scope: [language:python] and verify it's respected
|
|
27
|
+
cat > "$WORK/scoped-lesson.md" <<'LESSON'
|
|
28
|
+
---
|
|
29
|
+
id: 999
|
|
30
|
+
title: "Test scoped lesson"
|
|
31
|
+
severity: should-fix
|
|
32
|
+
scope: [language:python]
|
|
33
|
+
languages: [python]
|
|
34
|
+
category: silent-failures
|
|
35
|
+
pattern:
|
|
36
|
+
type: syntactic
|
|
37
|
+
regex: "test_scope_marker"
|
|
38
|
+
description: "test marker"
|
|
39
|
+
fix: "test"
|
|
40
|
+
---
|
|
41
|
+
LESSON
|
|
42
|
+
|
|
43
|
+
# Create a CLAUDE.md with scope tags
|
|
44
|
+
cat > "$WORK/CLAUDE.md" <<'CMD'
|
|
45
|
+
# Test Project
|
|
46
|
+
|
|
47
|
+
## Scope Tags
|
|
48
|
+
language:python, framework:pytest
|
|
49
|
+
CMD
|
|
50
|
+
|
|
51
|
+
# Python file with the marker — should be detected (scope matches)
|
|
52
|
+
cat > "$WORK/scoped.py" <<'PY'
|
|
53
|
+
test_scope_marker = True
|
|
54
|
+
PY
|
|
55
|
+
|
|
56
|
+
output=$(cd "$WORK" && LESSONS_DIR="$WORK" bash "$LESSON_CHECK" "$WORK/scoped.py" 2>&1 || true)
|
|
57
|
+
if echo "$output" | grep -q '\[lesson-999\]'; then
|
|
58
|
+
pass "Scoped lesson detected when project scope matches"
|
|
59
|
+
else
|
|
60
|
+
fail "Scoped lesson should detect violation when project scope matches, got: $output"
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# --- Test 10: Scoped lesson skipped when project scope doesn't match ---
|
|
64
|
+
cat > "$WORK/CLAUDE-noscope.md" <<'CMD'
|
|
65
|
+
# Different Project
|
|
66
|
+
|
|
67
|
+
## Scope Tags
|
|
68
|
+
domain:ha-aria
|
|
69
|
+
CMD
|
|
70
|
+
|
|
71
|
+
output=$(cd "$WORK" && LESSONS_DIR="$WORK" PROJECT_CLAUDE_MD="$WORK/CLAUDE-noscope.md" bash "$LESSON_CHECK" "$WORK/scoped.py" 2>&1 || true)
|
|
72
|
+
if echo "$output" | grep -q '\[lesson-999\]'; then
|
|
73
|
+
fail "Scoped lesson should be SKIPPED when project scope doesn't match"
|
|
74
|
+
else
|
|
75
|
+
pass "Scoped lesson correctly skipped for non-matching project scope"
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# --- Test 11: Lesson without scope defaults to universal (backward compat) ---
|
|
79
|
+
# Use the real lesson 1 (no scope field) — should still work as before
|
|
80
|
+
output=$(bash "$LESSON_CHECK" "$WORK/bad.py" 2>&1 || true)
|
|
81
|
+
if echo "$output" | grep -q '\[lesson-1\]'; then
|
|
82
|
+
pass "Lesson without scope: field defaults to universal (backward compatible)"
|
|
83
|
+
else
|
|
84
|
+
fail "Missing scope: should default to universal, got: $output"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# --- Test 12: --show-scope displays detected project scope ---
|
|
88
|
+
output=$(cd "$WORK" && bash "$LESSON_CHECK" --show-scope 2>&1 || true)
|
|
89
|
+
if echo "$output" | grep -q 'language:python'; then
|
|
90
|
+
pass "--show-scope displays detected project scope"
|
|
91
|
+
else
|
|
92
|
+
fail "--show-scope should display detected scope from CLAUDE.md, got: $output"
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# --- Test 13: --all-scopes bypasses scope filtering ---
|
|
96
|
+
# Use a lesson scoped to domain:ha-aria on a python project — should be skipped normally
|
|
97
|
+
cat > "$WORK/ha-lesson.md" <<'LESSON'
|
|
98
|
+
---
|
|
99
|
+
id: 998
|
|
100
|
+
title: "HA-only test lesson"
|
|
101
|
+
severity: should-fix
|
|
102
|
+
scope: [domain:ha-aria]
|
|
103
|
+
languages: [python]
|
|
104
|
+
category: silent-failures
|
|
105
|
+
pattern:
|
|
106
|
+
type: syntactic
|
|
107
|
+
regex: "ha_scope_marker"
|
|
108
|
+
description: "test marker"
|
|
109
|
+
fix: "test"
|
|
110
|
+
---
|
|
111
|
+
LESSON
|
|
112
|
+
|
|
113
|
+
cat > "$WORK/ha_file.py" <<'PY'
|
|
114
|
+
ha_scope_marker = True
|
|
115
|
+
PY
|
|
116
|
+
|
|
117
|
+
# Without --all-scopes: lesson 998 should be skipped (project is python, not ha-aria)
|
|
118
|
+
output=$(cd "$WORK" && LESSONS_DIR="$WORK" bash "$LESSON_CHECK" "$WORK/ha_file.py" 2>&1 || true)
|
|
119
|
+
if echo "$output" | grep -q '\[lesson-998\]'; then
|
|
120
|
+
fail "domain:ha-aria lesson should be skipped on a python-only project"
|
|
121
|
+
else
|
|
122
|
+
pass "domain:ha-aria lesson correctly skipped on non-matching project"
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
# With --all-scopes: lesson 998 should fire
|
|
126
|
+
output=$(cd "$WORK" && LESSONS_DIR="$WORK" bash "$LESSON_CHECK" --all-scopes "$WORK/ha_file.py" 2>&1 || true)
|
|
127
|
+
if echo "$output" | grep -q '\[lesson-998\]'; then
|
|
128
|
+
pass "--all-scopes bypasses scope filtering"
|
|
129
|
+
else
|
|
130
|
+
fail "--all-scopes should bypass scope filtering, got: $output"
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
# --- Test 14: --scope override replaces CLAUDE.md detection ---
|
|
134
|
+
output=$(cd "$WORK" && LESSONS_DIR="$WORK" bash "$LESSON_CHECK" --scope "domain:ha-aria" "$WORK/ha_file.py" 2>&1 || true)
|
|
135
|
+
if echo "$output" | grep -q '\[lesson-998\]'; then
|
|
136
|
+
pass "--scope override enables matching for specified scope"
|
|
137
|
+
else
|
|
138
|
+
fail "--scope override should enable domain:ha-aria matching, got: $output"
|
|
139
|
+
fi
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Step 2: Run tests to verify they fail**
|
|
143
|
+
|
|
144
|
+
Run: `bash scripts/tests/test-lesson-check.sh`
|
|
145
|
+
Expected: Tests 9-14 FAIL (scope features not yet implemented)
|
|
146
|
+
|
|
147
|
+
**Step 3: Commit test file**
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
git add scripts/tests/test-lesson-check.sh
|
|
151
|
+
git commit -m "test: add scope filtering tests for lesson-check.sh (red)"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
### Task 2: Add scope parsing to parse_lesson()
|
|
157
|
+
|
|
158
|
+
**Files:**
|
|
159
|
+
- Modify: `scripts/lesson-check.sh:17-88` (parse_lesson function)
|
|
160
|
+
|
|
161
|
+
**Step 1: Add lesson_scope variable initialization and parsing**
|
|
162
|
+
|
|
163
|
+
In `parse_lesson()`, add `lesson_scope=""` after the existing `lesson_languages=""` (line 24), then add a parsing case in the top-level fields block (after the `languages:` elif on line 52):
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# Add after line 24:
|
|
167
|
+
lesson_scope=""
|
|
168
|
+
|
|
169
|
+
# Add after the languages elif block (after line 57):
|
|
170
|
+
elif [[ "$line" =~ ^scope:[[:space:]]+(.*) ]]; then
|
|
171
|
+
lesson_scope="${BASH_REMATCH[1]}"
|
|
172
|
+
lesson_scope="${lesson_scope//[\[\]]/}"
|
|
173
|
+
lesson_scope="${lesson_scope//,/ }"
|
|
174
|
+
lesson_scope="${lesson_scope## }"
|
|
175
|
+
lesson_scope="${lesson_scope%% }"
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The parsing follows the exact same pattern as `lesson_languages` — strip brackets, replace commas with spaces, trim.
|
|
179
|
+
|
|
180
|
+
**Default when missing:** After the `return 1` checks at lines 76-77, add:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# Default scope to universal when omitted (backward compatible)
|
|
184
|
+
[[ -z "$lesson_scope" ]] && lesson_scope="universal"
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Step 2: Run existing tests to verify no regression**
|
|
188
|
+
|
|
189
|
+
Run: `bash scripts/tests/test-lesson-check.sh`
|
|
190
|
+
Expected: Tests 1-8 still pass, tests 9-14 still fail (matching not implemented yet)
|
|
191
|
+
|
|
192
|
+
**Step 3: Commit**
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
git add scripts/lesson-check.sh
|
|
196
|
+
git commit -m "feat: parse scope: field from lesson YAML frontmatter"
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
### Task 3: Add detect_project_scope() and scope_matches()
|
|
202
|
+
|
|
203
|
+
**Files:**
|
|
204
|
+
- Modify: `scripts/lesson-check.sh` (add two new functions after file_matches_languages)
|
|
205
|
+
|
|
206
|
+
**Step 1: Add detect_project_scope()**
|
|
207
|
+
|
|
208
|
+
Insert after `file_matches_languages()` (after line 183):
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
# ---------------------------------------------------------------------------
|
|
212
|
+
# detect_project_scope [claude_md_path]
|
|
213
|
+
# Reads ## Scope Tags from CLAUDE.md. Falls back to detect_project_type().
|
|
214
|
+
# Sets global: project_scope (space-separated tags)
|
|
215
|
+
# ---------------------------------------------------------------------------
|
|
216
|
+
detect_project_scope() {
|
|
217
|
+
local claude_md="${1:-}"
|
|
218
|
+
project_scope=""
|
|
219
|
+
|
|
220
|
+
# Try explicit path first, then search current directory upward
|
|
221
|
+
if [[ -z "$claude_md" ]]; then
|
|
222
|
+
claude_md="CLAUDE.md"
|
|
223
|
+
# Walk up to find CLAUDE.md (max 5 levels)
|
|
224
|
+
local search_dir="$PWD"
|
|
225
|
+
for _ in 1 2 3 4 5; do
|
|
226
|
+
if [[ -f "$search_dir/CLAUDE.md" ]]; then
|
|
227
|
+
claude_md="$search_dir/CLAUDE.md"
|
|
228
|
+
break
|
|
229
|
+
fi
|
|
230
|
+
search_dir="$(dirname "$search_dir")"
|
|
231
|
+
done
|
|
232
|
+
fi
|
|
233
|
+
|
|
234
|
+
# Parse ## Scope Tags section from CLAUDE.md
|
|
235
|
+
if [[ -f "$claude_md" ]]; then
|
|
236
|
+
local in_scope_section=false
|
|
237
|
+
local line
|
|
238
|
+
while IFS= read -r line; do
|
|
239
|
+
if [[ "$line" =~ ^##[[:space:]]+Scope[[:space:]]+Tags ]]; then
|
|
240
|
+
in_scope_section=true
|
|
241
|
+
continue
|
|
242
|
+
fi
|
|
243
|
+
if [[ "$in_scope_section" == true ]]; then
|
|
244
|
+
# Stop at next heading
|
|
245
|
+
if [[ "$line" =~ ^## ]]; then
|
|
246
|
+
break
|
|
247
|
+
fi
|
|
248
|
+
# Skip empty lines
|
|
249
|
+
[[ -z "${line// /}" ]] && continue
|
|
250
|
+
# Parse comma-separated tags
|
|
251
|
+
local tag
|
|
252
|
+
for tag in ${line//,/ }; do
|
|
253
|
+
tag="${tag## }"
|
|
254
|
+
tag="${tag%% }"
|
|
255
|
+
[[ -n "$tag" ]] && project_scope+="$tag "
|
|
256
|
+
done
|
|
257
|
+
fi
|
|
258
|
+
done < "$claude_md"
|
|
259
|
+
project_scope="${project_scope%% }"
|
|
260
|
+
fi
|
|
261
|
+
|
|
262
|
+
# Fallback: detect project type → language tag
|
|
263
|
+
if [[ -z "$project_scope" ]]; then
|
|
264
|
+
source "$SCRIPT_DIR/lib/common.sh" 2>/dev/null || true
|
|
265
|
+
if type detect_project_type &>/dev/null; then
|
|
266
|
+
local ptype
|
|
267
|
+
ptype=$(detect_project_type "$PWD")
|
|
268
|
+
case "$ptype" in
|
|
269
|
+
python) project_scope="language:python" ;;
|
|
270
|
+
node) project_scope="language:javascript" ;;
|
|
271
|
+
bash) project_scope="language:bash" ;;
|
|
272
|
+
*) project_scope="" ;;
|
|
273
|
+
esac
|
|
274
|
+
fi
|
|
275
|
+
fi
|
|
276
|
+
|
|
277
|
+
# If still empty, everything matches (universal behavior)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
# ---------------------------------------------------------------------------
|
|
281
|
+
# scope_matches <lesson_scope> <project_scope>
|
|
282
|
+
# Returns 0 if lesson should run on this project, 1 if it should be skipped.
|
|
283
|
+
# A lesson matches if ANY of its scope tags intersects the project's scope set,
|
|
284
|
+
# or if the lesson scope includes "universal".
|
|
285
|
+
# ---------------------------------------------------------------------------
|
|
286
|
+
scope_matches() {
|
|
287
|
+
local l_scope="$1" # space-separated lesson scope tags
|
|
288
|
+
local p_scope="$2" # space-separated project scope tags
|
|
289
|
+
|
|
290
|
+
# universal matches everything
|
|
291
|
+
local tag
|
|
292
|
+
for tag in $l_scope; do
|
|
293
|
+
[[ "$tag" == "universal" ]] && return 0
|
|
294
|
+
done
|
|
295
|
+
|
|
296
|
+
# If project has no scope, everything matches (backward compat)
|
|
297
|
+
[[ -z "$p_scope" ]] && return 0
|
|
298
|
+
|
|
299
|
+
# Check intersection
|
|
300
|
+
local ltag ptag
|
|
301
|
+
for ltag in $l_scope; do
|
|
302
|
+
for ptag in $p_scope; do
|
|
303
|
+
[[ "$ltag" == "$ptag" ]] && return 0
|
|
304
|
+
done
|
|
305
|
+
done
|
|
306
|
+
|
|
307
|
+
return 1
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**Step 2: Run tests**
|
|
312
|
+
|
|
313
|
+
Run: `bash scripts/tests/test-lesson-check.sh`
|
|
314
|
+
Expected: Tests 1-8 pass, tests 9-14 still fail (gate not wired in main loop yet)
|
|
315
|
+
|
|
316
|
+
**Step 3: Commit**
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
git add scripts/lesson-check.sh
|
|
320
|
+
git commit -m "feat: add detect_project_scope() and scope_matches() functions"
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
### Task 4: Wire scope filtering into main loop and CLI flags
|
|
326
|
+
|
|
327
|
+
**Files:**
|
|
328
|
+
- Modify: `scripts/lesson-check.sh` (CLI parsing, main loop gate, --help)
|
|
329
|
+
|
|
330
|
+
**Step 1: Add CLI flag parsing**
|
|
331
|
+
|
|
332
|
+
Replace the existing `--help` check block (lines 118-121) with expanded flag parsing:
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
# ---------------------------------------------------------------------------
|
|
336
|
+
# CLI flag parsing
|
|
337
|
+
# ---------------------------------------------------------------------------
|
|
338
|
+
ALL_SCOPES=false
|
|
339
|
+
SHOW_SCOPE=false
|
|
340
|
+
SCOPE_OVERRIDE=""
|
|
341
|
+
|
|
342
|
+
# Parse flags before file arguments
|
|
343
|
+
args=()
|
|
344
|
+
while [[ $# -gt 0 ]]; do
|
|
345
|
+
case "$1" in
|
|
346
|
+
--help|-h) build_help; exit 0 ;;
|
|
347
|
+
--all-scopes) ALL_SCOPES=true; shift ;;
|
|
348
|
+
--show-scope) SHOW_SCOPE=true; shift ;;
|
|
349
|
+
--scope) SCOPE_OVERRIDE="$2"; shift 2 ;;
|
|
350
|
+
*) args+=("$1"); shift ;;
|
|
351
|
+
esac
|
|
352
|
+
done
|
|
353
|
+
set -- "${args[@]+"${args[@]}"}"
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**Step 2: Add scope detection before main loop**
|
|
357
|
+
|
|
358
|
+
After the `existing_files` check (after line 160), add:
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
# ---------------------------------------------------------------------------
|
|
362
|
+
# Detect project scope (unless --all-scopes)
|
|
363
|
+
# ---------------------------------------------------------------------------
|
|
364
|
+
project_scope=""
|
|
365
|
+
if [[ "$ALL_SCOPES" == false ]]; then
|
|
366
|
+
if [[ -n "$SCOPE_OVERRIDE" ]]; then
|
|
367
|
+
project_scope="${SCOPE_OVERRIDE//,/ }"
|
|
368
|
+
else
|
|
369
|
+
detect_project_scope "${PROJECT_CLAUDE_MD:-}"
|
|
370
|
+
fi
|
|
371
|
+
fi
|
|
372
|
+
|
|
373
|
+
if [[ "$SHOW_SCOPE" == true ]]; then
|
|
374
|
+
if [[ -n "$project_scope" ]]; then
|
|
375
|
+
echo "Detected project scope: $project_scope"
|
|
376
|
+
else
|
|
377
|
+
echo "No project scope detected (all lessons will apply)"
|
|
378
|
+
fi
|
|
379
|
+
exit 0
|
|
380
|
+
fi
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
**Step 3: Add scope gate in main loop**
|
|
384
|
+
|
|
385
|
+
In the main loop (line 191: `parse_lesson "$lfile" || continue`), add the scope check right after:
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
parse_lesson "$lfile" || continue
|
|
389
|
+
|
|
390
|
+
# Scope filtering: skip lessons that don't match this project
|
|
391
|
+
if [[ "$ALL_SCOPES" == false ]]; then
|
|
392
|
+
scope_matches "$lesson_scope" "$project_scope" || continue
|
|
393
|
+
fi
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
**Step 4: Update build_help() to show scope**
|
|
397
|
+
|
|
398
|
+
In `build_help()`, update the checks_text line (line 101) to include scope:
|
|
399
|
+
|
|
400
|
+
```bash
|
|
401
|
+
local scope_display="$lesson_scope"
|
|
402
|
+
checks_text+=" [lesson-${lesson_id}] ${lesson_title} (${lang_display}) [scope: ${scope_display}]"$'\n'
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
And add scope flags to the usage text:
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
cat <<USAGE
|
|
409
|
+
Usage: lesson-check.sh [OPTIONS] [file ...]
|
|
410
|
+
Check files for known anti-patterns from lessons learned.
|
|
411
|
+
Files can be passed as arguments or piped via stdin (one per line).
|
|
412
|
+
If neither, defaults to git diff --name-only in current directory.
|
|
413
|
+
|
|
414
|
+
Options:
|
|
415
|
+
--help, -h Show this help
|
|
416
|
+
--all-scopes Bypass scope filtering (check all lessons regardless of project)
|
|
417
|
+
--show-scope Display detected project scope and exit
|
|
418
|
+
--scope <tags> Override project scope (comma-separated, e.g. "language:python,domain:ha-aria")
|
|
419
|
+
|
|
420
|
+
Checks (syntactic only — loaded from ${LESSONS_DIR}):
|
|
421
|
+
${checks_text}
|
|
422
|
+
Output: file:line: [lesson-N] description
|
|
423
|
+
Exit: 0 if clean, 1 if violations found
|
|
424
|
+
USAGE
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
**Step 5: Run all tests**
|
|
428
|
+
|
|
429
|
+
Run: `bash scripts/tests/test-lesson-check.sh`
|
|
430
|
+
Expected: All tests 1-14 PASS
|
|
431
|
+
|
|
432
|
+
**Step 6: Commit**
|
|
433
|
+
|
|
434
|
+
```bash
|
|
435
|
+
git add scripts/lesson-check.sh
|
|
436
|
+
git commit -m "feat: wire scope filtering into lesson-check main loop with CLI flags"
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## Batch 2: Scope Inference Script & Template Update
|
|
442
|
+
|
|
443
|
+
### Task 5: Write test-scope-infer.sh
|
|
444
|
+
|
|
445
|
+
**Files:**
|
|
446
|
+
- Create: `scripts/tests/test-scope-infer.sh`
|
|
447
|
+
|
|
448
|
+
**Step 1: Write test file**
|
|
449
|
+
|
|
450
|
+
```bash
|
|
451
|
+
#!/usr/bin/env bash
|
|
452
|
+
set -euo pipefail
|
|
453
|
+
|
|
454
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
455
|
+
source "$SCRIPT_DIR/test-helpers.sh"
|
|
456
|
+
|
|
457
|
+
INFER="$SCRIPT_DIR/../scope-infer.sh"
|
|
458
|
+
|
|
459
|
+
# --- Test: --help exits 0 ---
|
|
460
|
+
assert_exit "--help exits 0" 0 "$INFER" --help
|
|
461
|
+
|
|
462
|
+
# --- Test: --dry-run shows proposed scope without modifying files ---
|
|
463
|
+
WORK=$(mktemp -d)
|
|
464
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
465
|
+
|
|
466
|
+
# Create a lesson with no scope field, mentioning "HA" and "entity"
|
|
467
|
+
cat > "$WORK/0099-test-ha-lesson.md" <<'LESSON'
|
|
468
|
+
---
|
|
469
|
+
id: 99
|
|
470
|
+
title: "HA entity resolution fails on restart"
|
|
471
|
+
severity: should-fix
|
|
472
|
+
languages: [python]
|
|
473
|
+
category: data-model
|
|
474
|
+
pattern:
|
|
475
|
+
type: semantic
|
|
476
|
+
description: "HA entity lookup returns stale area"
|
|
477
|
+
fix: "Refresh entity registry on restart"
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
## Observation
|
|
481
|
+
Home Assistant entity area resolution uses a cached registry.
|
|
482
|
+
LESSON
|
|
483
|
+
|
|
484
|
+
dry_output=$("$INFER" --dir "$WORK" --dry-run 2>&1 || true)
|
|
485
|
+
assert_contains "--dry-run mentions ha-aria" "domain:ha-aria" "$dry_output"
|
|
486
|
+
|
|
487
|
+
# Verify file was NOT modified (dry run)
|
|
488
|
+
TESTS=$((TESTS + 1))
|
|
489
|
+
if grep -q '^scope:' "$WORK/0099-test-ha-lesson.md" 2>/dev/null; then
|
|
490
|
+
echo "FAIL: --dry-run should not modify lesson files"
|
|
491
|
+
FAILURES=$((FAILURES + 1))
|
|
492
|
+
else
|
|
493
|
+
echo "PASS: --dry-run does not modify lesson files"
|
|
494
|
+
fi
|
|
495
|
+
|
|
496
|
+
# --- Test: --apply writes scope field to lesson ---
|
|
497
|
+
"$INFER" --dir "$WORK" --apply > /dev/null 2>&1 || true
|
|
498
|
+
|
|
499
|
+
TESTS=$((TESTS + 1))
|
|
500
|
+
if grep -q '^scope:' "$WORK/0099-test-ha-lesson.md" 2>/dev/null; then
|
|
501
|
+
echo "PASS: --apply writes scope field to lesson"
|
|
502
|
+
else
|
|
503
|
+
echo "FAIL: --apply should write scope field to lesson"
|
|
504
|
+
FAILURES=$((FAILURES + 1))
|
|
505
|
+
fi
|
|
506
|
+
|
|
507
|
+
# Verify inferred scope is correct
|
|
508
|
+
scope_line=$(grep '^scope:' "$WORK/0099-test-ha-lesson.md" 2>/dev/null || true)
|
|
509
|
+
assert_contains "--apply infers domain:ha-aria" "domain:ha-aria" "$scope_line"
|
|
510
|
+
|
|
511
|
+
# --- Test: Lesson with existing scope is not modified ---
|
|
512
|
+
cat > "$WORK/0098-already-scoped.md" <<'LESSON'
|
|
513
|
+
---
|
|
514
|
+
id: 98
|
|
515
|
+
title: "Already scoped lesson"
|
|
516
|
+
severity: should-fix
|
|
517
|
+
scope: [language:python]
|
|
518
|
+
languages: [python]
|
|
519
|
+
category: silent-failures
|
|
520
|
+
pattern:
|
|
521
|
+
type: semantic
|
|
522
|
+
description: "test"
|
|
523
|
+
fix: "test"
|
|
524
|
+
---
|
|
525
|
+
LESSON
|
|
526
|
+
|
|
527
|
+
apply_output=$("$INFER" --dir "$WORK" --apply 2>&1 || true)
|
|
528
|
+
scope_line=$(grep '^scope:' "$WORK/0098-already-scoped.md" 2>/dev/null || true)
|
|
529
|
+
assert_contains "existing scope preserved" "language:python" "$scope_line"
|
|
530
|
+
|
|
531
|
+
# --- Test: Python-only lesson with no domain signals → language:python ---
|
|
532
|
+
cat > "$WORK/0097-python-only.md" <<'LESSON'
|
|
533
|
+
---
|
|
534
|
+
id: 97
|
|
535
|
+
title: "Generic Python anti-pattern"
|
|
536
|
+
severity: should-fix
|
|
537
|
+
languages: [python]
|
|
538
|
+
category: async-traps
|
|
539
|
+
pattern:
|
|
540
|
+
type: syntactic
|
|
541
|
+
regex: "some_pattern"
|
|
542
|
+
description: "test"
|
|
543
|
+
fix: "test"
|
|
544
|
+
---
|
|
545
|
+
|
|
546
|
+
## Observation
|
|
547
|
+
This is a generic Python lesson with no domain signals.
|
|
548
|
+
LESSON
|
|
549
|
+
|
|
550
|
+
"$INFER" --dir "$WORK" --apply > /dev/null 2>&1 || true
|
|
551
|
+
scope_line=$(grep '^scope:' "$WORK/0097-python-only.md" 2>/dev/null || true)
|
|
552
|
+
assert_contains "python-only → language:python" "language:python" "$scope_line"
|
|
553
|
+
|
|
554
|
+
# --- Test: No signals → universal ---
|
|
555
|
+
cat > "$WORK/0096-universal.md" <<'LESSON'
|
|
556
|
+
---
|
|
557
|
+
id: 96
|
|
558
|
+
title: "Generic coding practice"
|
|
559
|
+
severity: nice-to-have
|
|
560
|
+
languages: [all]
|
|
561
|
+
category: test-anti-patterns
|
|
562
|
+
pattern:
|
|
563
|
+
type: syntactic
|
|
564
|
+
regex: "some_other_pattern"
|
|
565
|
+
description: "test"
|
|
566
|
+
fix: "test"
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
## Observation
|
|
570
|
+
This applies to all projects everywhere.
|
|
571
|
+
LESSON
|
|
572
|
+
|
|
573
|
+
"$INFER" --dir "$WORK" --apply > /dev/null 2>&1 || true
|
|
574
|
+
scope_line=$(grep '^scope:' "$WORK/0096-universal.md" 2>/dev/null || true)
|
|
575
|
+
assert_contains "no signals → universal" "universal" "$scope_line"
|
|
576
|
+
|
|
577
|
+
# --- Test: Summary output shows counts ---
|
|
578
|
+
WORK2=$(mktemp -d)
|
|
579
|
+
trap 'rm -rf "$WORK" "$WORK2"' EXIT
|
|
580
|
+
|
|
581
|
+
cat > "$WORK2/0001-test.md" <<'LESSON'
|
|
582
|
+
---
|
|
583
|
+
id: 1
|
|
584
|
+
title: "Test lesson"
|
|
585
|
+
severity: should-fix
|
|
586
|
+
languages: [python]
|
|
587
|
+
category: silent-failures
|
|
588
|
+
pattern:
|
|
589
|
+
type: syntactic
|
|
590
|
+
regex: "test"
|
|
591
|
+
description: "test"
|
|
592
|
+
fix: "test"
|
|
593
|
+
---
|
|
594
|
+
Generic content.
|
|
595
|
+
LESSON
|
|
596
|
+
|
|
597
|
+
summary_output=$("$INFER" --dir "$WORK2" --dry-run 2>&1 || true)
|
|
598
|
+
assert_contains "summary shows count" "Inferred scope for" "$summary_output"
|
|
599
|
+
|
|
600
|
+
report_results
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
**Step 2: Run test to verify it fails**
|
|
604
|
+
|
|
605
|
+
Run: `bash scripts/tests/test-scope-infer.sh`
|
|
606
|
+
Expected: FAIL (script doesn't exist yet)
|
|
607
|
+
|
|
608
|
+
**Step 3: Commit**
|
|
609
|
+
|
|
610
|
+
```bash
|
|
611
|
+
git add scripts/tests/test-scope-infer.sh
|
|
612
|
+
git commit -m "test: add scope-infer.sh tests (red)"
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
---
|
|
616
|
+
|
|
617
|
+
### Task 6: Implement scope-infer.sh
|
|
618
|
+
|
|
619
|
+
**Files:**
|
|
620
|
+
- Create: `scripts/scope-infer.sh`
|
|
621
|
+
|
|
622
|
+
**Step 1: Write the script**
|
|
623
|
+
|
|
624
|
+
```bash
|
|
625
|
+
#!/usr/bin/env bash
|
|
626
|
+
# scope-infer.sh — Infer scope tags for lessons missing them
|
|
627
|
+
# Reads lesson content and applies heuristics to propose scope tags.
|
|
628
|
+
set -euo pipefail
|
|
629
|
+
|
|
630
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
631
|
+
|
|
632
|
+
# Defaults
|
|
633
|
+
LESSONS_DIR="$SCRIPT_DIR/../docs/lessons"
|
|
634
|
+
DRY_RUN=true
|
|
635
|
+
APPLY=false
|
|
636
|
+
|
|
637
|
+
usage() {
|
|
638
|
+
cat <<USAGE
|
|
639
|
+
Usage: scope-infer.sh [--dir <lessons-dir>] [--dry-run] [--apply]
|
|
640
|
+
|
|
641
|
+
Infer scope tags for lesson files that don't have a scope: field.
|
|
642
|
+
|
|
643
|
+
Options:
|
|
644
|
+
--dir <path> Lessons directory (default: docs/lessons/)
|
|
645
|
+
--dry-run Show proposed scope without modifying files (default)
|
|
646
|
+
--apply Write scope field into lesson files
|
|
647
|
+
--help, -h Show this help
|
|
648
|
+
USAGE
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
while [[ $# -gt 0 ]]; do
|
|
652
|
+
case "$1" in
|
|
653
|
+
--dir) LESSONS_DIR="$2"; shift 2 ;;
|
|
654
|
+
--dry-run) DRY_RUN=true; APPLY=false; shift ;;
|
|
655
|
+
--apply) APPLY=true; DRY_RUN=false; shift ;;
|
|
656
|
+
--help|-h) usage; exit 0 ;;
|
|
657
|
+
*) echo "Unknown flag: $1" >&2; usage >&2; exit 1 ;;
|
|
658
|
+
esac
|
|
659
|
+
done
|
|
660
|
+
|
|
661
|
+
# Counters
|
|
662
|
+
total=0
|
|
663
|
+
inferred=0
|
|
664
|
+
skipped=0
|
|
665
|
+
count_universal=0
|
|
666
|
+
count_language=0
|
|
667
|
+
count_domain=0
|
|
668
|
+
count_project=0
|
|
669
|
+
|
|
670
|
+
infer_scope() {
|
|
671
|
+
local file="$1"
|
|
672
|
+
local content
|
|
673
|
+
content=$(cat "$file")
|
|
674
|
+
|
|
675
|
+
# Domain signals (check title + body)
|
|
676
|
+
local title_and_body
|
|
677
|
+
title_and_body=$(echo "$content" | tr '[:upper:]' '[:lower:]')
|
|
678
|
+
|
|
679
|
+
# Domain: ha-aria
|
|
680
|
+
if echo "$title_and_body" | grep -qE '(home assistant|\\bha\\b|entity.*area|automation.*trigger|hass|ha-aria)'; then
|
|
681
|
+
echo "domain:ha-aria"
|
|
682
|
+
return
|
|
683
|
+
fi
|
|
684
|
+
|
|
685
|
+
# Domain: telegram
|
|
686
|
+
if echo "$title_and_body" | grep -qE '(telegram|bot.*poll|getupdates|chat_id|telegram-brief|telegram-capture)'; then
|
|
687
|
+
echo "domain:telegram"
|
|
688
|
+
return
|
|
689
|
+
fi
|
|
690
|
+
|
|
691
|
+
# Domain: notion
|
|
692
|
+
if echo "$title_and_body" | grep -qE '(\\bnotion\\b|notion.*sync|notion.*database|notion-tools|notion_api)'; then
|
|
693
|
+
echo "domain:notion"
|
|
694
|
+
return
|
|
695
|
+
fi
|
|
696
|
+
|
|
697
|
+
# Domain: ollama
|
|
698
|
+
if echo "$title_and_body" | grep -qE '(\\bollama\\b|ollama.*queue|local.*llm|ollama-queue)'; then
|
|
699
|
+
echo "domain:ollama"
|
|
700
|
+
return
|
|
701
|
+
fi
|
|
702
|
+
|
|
703
|
+
# Framework: systemd
|
|
704
|
+
if echo "$title_and_body" | grep -qE '(systemd|systemctl|\.service|\.timer|journalctl|envfile)'; then
|
|
705
|
+
echo "framework:systemd"
|
|
706
|
+
return
|
|
707
|
+
fi
|
|
708
|
+
|
|
709
|
+
# Framework: pytest
|
|
710
|
+
if echo "$title_and_body" | grep -qE '(\\bpytest\\b|conftest|fixture|parametrize)'; then
|
|
711
|
+
echo "framework:pytest"
|
|
712
|
+
return
|
|
713
|
+
fi
|
|
714
|
+
|
|
715
|
+
# Framework: preact/jsx
|
|
716
|
+
if echo "$title_and_body" | grep -qE '(\\bpreact\\b|\\bjsx\\b|esbuild.*jsx|jsx.*factory)'; then
|
|
717
|
+
echo "framework:preact"
|
|
718
|
+
return
|
|
719
|
+
fi
|
|
720
|
+
|
|
721
|
+
# Project-specific: autonomous-coding-toolkit
|
|
722
|
+
if echo "$title_and_body" | grep -qE '(run-plan|quality.gate|lesson-check|mab-run|batch.*audit|ralph.*loop|headless.*mode)'; then
|
|
723
|
+
echo "project:autonomous-coding-toolkit"
|
|
724
|
+
return
|
|
725
|
+
fi
|
|
726
|
+
|
|
727
|
+
# Language: check the languages field
|
|
728
|
+
local languages
|
|
729
|
+
languages=$(sed -n '/^---$/,/^---$/{ /^languages:/p; }' "$file" 2>/dev/null | head -1)
|
|
730
|
+
languages=$(echo "$languages" | sed 's/languages:[[:space:]]*//' | tr -d '[]' | tr ',' ' ' | xargs)
|
|
731
|
+
|
|
732
|
+
if [[ "$languages" == "python" ]]; then
|
|
733
|
+
echo "language:python"
|
|
734
|
+
return
|
|
735
|
+
elif [[ "$languages" == "shell" ]]; then
|
|
736
|
+
echo "language:bash"
|
|
737
|
+
return
|
|
738
|
+
elif [[ "$languages" == "javascript" || "$languages" == "typescript" ]]; then
|
|
739
|
+
echo "language:javascript"
|
|
740
|
+
return
|
|
741
|
+
fi
|
|
742
|
+
|
|
743
|
+
# No signals → universal
|
|
744
|
+
echo "universal"
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
for lesson_file in "$LESSONS_DIR"/[0-9]*.md; do
|
|
748
|
+
[[ -f "$lesson_file" ]] || continue
|
|
749
|
+
total=$((total + 1))
|
|
750
|
+
|
|
751
|
+
# Check if scope already present
|
|
752
|
+
if sed -n '/^---$/,/^---$/p' "$lesson_file" | grep -q '^scope:'; then
|
|
753
|
+
skipped=$((skipped + 1))
|
|
754
|
+
continue
|
|
755
|
+
fi
|
|
756
|
+
|
|
757
|
+
scope=$(infer_scope "$lesson_file")
|
|
758
|
+
inferred=$((inferred + 1))
|
|
759
|
+
|
|
760
|
+
# Count by type
|
|
761
|
+
case "$scope" in
|
|
762
|
+
universal) count_universal=$((count_universal + 1)) ;;
|
|
763
|
+
language:*) count_language=$((count_language + 1)) ;;
|
|
764
|
+
domain:*) count_domain=$((count_domain + 1)) ;;
|
|
765
|
+
project:*) count_project=$((count_project + 1)) ;;
|
|
766
|
+
framework:*) count_language=$((count_language + 1)) ;; # group with language
|
|
767
|
+
esac
|
|
768
|
+
|
|
769
|
+
basename_file=$(basename "$lesson_file")
|
|
770
|
+
|
|
771
|
+
if [[ "$APPLY" == true ]]; then
|
|
772
|
+
# Insert scope: [$scope] after the languages: line in YAML frontmatter
|
|
773
|
+
sed -i "/^languages:/a scope: [$scope]" "$lesson_file"
|
|
774
|
+
echo " APPLIED: $basename_file → scope: [$scope]"
|
|
775
|
+
else
|
|
776
|
+
echo " PROPOSED: $basename_file → scope: [$scope]"
|
|
777
|
+
fi
|
|
778
|
+
done
|
|
779
|
+
|
|
780
|
+
echo ""
|
|
781
|
+
echo "Inferred scope for $inferred lessons: $count_universal universal, $count_domain domain-specific, $count_language language/framework, $count_project project-specific"
|
|
782
|
+
echo "Skipped $skipped lessons (already have scope)"
|
|
783
|
+
echo "Total: $total lessons scanned"
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
**Step 2: Make executable**
|
|
787
|
+
|
|
788
|
+
```bash
|
|
789
|
+
chmod +x scripts/scope-infer.sh
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
**Step 3: Run tests**
|
|
793
|
+
|
|
794
|
+
Run: `bash scripts/tests/test-scope-infer.sh`
|
|
795
|
+
Expected: ALL PASS
|
|
796
|
+
|
|
797
|
+
**Step 4: Commit**
|
|
798
|
+
|
|
799
|
+
```bash
|
|
800
|
+
git add scripts/scope-infer.sh scripts/tests/test-scope-infer.sh
|
|
801
|
+
git commit -m "feat: add scope-infer.sh for bulk scope tagging of lessons"
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
---
|
|
805
|
+
|
|
806
|
+
### Task 7: Update TEMPLATE.md with scope field
|
|
807
|
+
|
|
808
|
+
**Files:**
|
|
809
|
+
- Modify: `docs/lessons/TEMPLATE.md`
|
|
810
|
+
|
|
811
|
+
**Step 1: Add scope field to the template**
|
|
812
|
+
|
|
813
|
+
Insert the `scope:` field after `languages:` in the YAML block. The template should show it as optional with a comment:
|
|
814
|
+
|
|
815
|
+
```yaml
|
|
816
|
+
languages: [<python|javascript|typescript|shell|all>]
|
|
817
|
+
scope: [<universal|language:X|framework:X|domain:X|project:X>] # optional, defaults to universal
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
**Step 2: Add scope to the Field Guide**
|
|
821
|
+
|
|
822
|
+
Add a new section after Categories:
|
|
823
|
+
|
|
824
|
+
```markdown
|
|
825
|
+
### Scope (Project-Level Filtering)
|
|
826
|
+
Scope controls which projects a lesson applies to. Language filtering (`languages:`) picks files; scope filtering picks projects. Both are orthogonal.
|
|
827
|
+
|
|
828
|
+
| Tag Format | Example | Matches |
|
|
829
|
+
|------------|---------|---------|
|
|
830
|
+
| `universal` | `[universal]` | All projects (default) |
|
|
831
|
+
| `language:<lang>` | `[language:python]` | Projects with that language |
|
|
832
|
+
| `framework:<name>` | `[framework:pytest]` | Projects using that framework |
|
|
833
|
+
| `domain:<name>` | `[domain:ha-aria]` | Domain-specific projects |
|
|
834
|
+
| `project:<name>` | `[project:autonomous-coding-toolkit]` | Exact project match |
|
|
835
|
+
|
|
836
|
+
Default when omitted: `[universal]` — backward compatible.
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
**Step 3: Run existing tests to verify no regression**
|
|
840
|
+
|
|
841
|
+
Run: `bash scripts/tests/run-all-tests.sh`
|
|
842
|
+
Expected: All tests pass
|
|
843
|
+
|
|
844
|
+
**Step 4: Commit**
|
|
845
|
+
|
|
846
|
+
```bash
|
|
847
|
+
git add docs/lessons/TEMPLATE.md
|
|
848
|
+
git commit -m "docs: add scope field to lesson TEMPLATE.md"
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
---
|
|
852
|
+
|
|
853
|
+
## Batch 3: Apply Scope Tags to Existing Lessons
|
|
854
|
+
|
|
855
|
+
### Task 8: Run scope-infer.sh --dry-run and review
|
|
856
|
+
|
|
857
|
+
**Step 1: Run dry-run**
|
|
858
|
+
|
|
859
|
+
```bash
|
|
860
|
+
bash scripts/scope-infer.sh --dir docs/lessons --dry-run
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
Expected: See proposed scope for all 67 lessons. Review the output to verify heuristics are reasonable.
|
|
864
|
+
|
|
865
|
+
**Step 2: Fix any obvious misclassifications**
|
|
866
|
+
|
|
867
|
+
If a lesson is proposed with the wrong scope, either:
|
|
868
|
+
- Adjust the heuristics in `scope-infer.sh`, OR
|
|
869
|
+
- Plan to manually fix after --apply
|
|
870
|
+
|
|
871
|
+
**Step 3: No commit (review only)**
|
|
872
|
+
|
|
873
|
+
---
|
|
874
|
+
|
|
875
|
+
### Task 9: Apply scope tags to the first 10 lessons manually
|
|
876
|
+
|
|
877
|
+
Apply the scope assignments from the design doc for lessons 0001-0010. These were manually reviewed.
|
|
878
|
+
|
|
879
|
+
**Files:**
|
|
880
|
+
- Modify: `docs/lessons/0001-bare-exception-swallowing.md` through `docs/lessons/0010-local-outside-function-bash.md`
|
|
881
|
+
|
|
882
|
+
**Step 1: Add scope field to each lesson**
|
|
883
|
+
|
|
884
|
+
For each file, insert `scope: [<value>]` after the `languages:` line in the YAML frontmatter:
|
|
885
|
+
|
|
886
|
+
| File | Scope to add |
|
|
887
|
+
|------|-------------|
|
|
888
|
+
| `0001-bare-exception-swallowing.md` | `scope: [language:python]` |
|
|
889
|
+
| `0002-async-def-without-await.md` | `scope: [language:python]` |
|
|
890
|
+
| `0003-create-task-without-callback.md` | `scope: [language:python]` |
|
|
891
|
+
| `0004-hardcoded-test-counts.md` | `scope: [universal]` |
|
|
892
|
+
| `0005-sqlite-without-closing.md` | `scope: [language:python]` |
|
|
893
|
+
| `0006-venv-pip-path.md` | `scope: [language:python, framework:pytest]` |
|
|
894
|
+
| `0007-runner-state-self-rejection.md` | `scope: [project:autonomous-coding-toolkit]` |
|
|
895
|
+
| `0008-quality-gate-blind-spot.md` | `scope: [project:autonomous-coding-toolkit]` |
|
|
896
|
+
| `0009-parser-overcount-empty-batches.md` | `scope: [project:autonomous-coding-toolkit]` |
|
|
897
|
+
| `0010-local-outside-function-bash.md` | `scope: [language:bash]` |
|
|
898
|
+
|
|
899
|
+
**Step 2: Run scope-infer.sh --apply for remaining lessons**
|
|
900
|
+
|
|
901
|
+
```bash
|
|
902
|
+
bash scripts/scope-infer.sh --dir docs/lessons --apply
|
|
903
|
+
```
|
|
904
|
+
|
|
905
|
+
This will add scope to lessons 0011-0067 (the first 10 already have scope and will be skipped).
|
|
906
|
+
|
|
907
|
+
**Step 3: Run full test suite**
|
|
908
|
+
|
|
909
|
+
Run: `bash scripts/tests/run-all-tests.sh`
|
|
910
|
+
Expected: All tests pass (scope field is backward-compatible)
|
|
911
|
+
|
|
912
|
+
**Step 4: Commit**
|
|
913
|
+
|
|
914
|
+
```bash
|
|
915
|
+
git add docs/lessons/
|
|
916
|
+
git commit -m "feat: add scope tags to all 67 toolkit lessons"
|
|
917
|
+
```
|
|
918
|
+
|
|
919
|
+
---
|
|
920
|
+
|
|
921
|
+
### Task 10: Run full test suite and verify
|
|
922
|
+
|
|
923
|
+
**Step 1: Run all tests**
|
|
924
|
+
|
|
925
|
+
```bash
|
|
926
|
+
bash scripts/tests/run-all-tests.sh
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
Expected: All test files pass, including the new scope tests.
|
|
930
|
+
|
|
931
|
+
**Step 2: Test behavioral scenarios**
|
|
932
|
+
|
|
933
|
+
```bash
|
|
934
|
+
# From the toolkit root:
|
|
935
|
+
cd /path/to/autonomous-coding-toolkit
|
|
936
|
+
bash scripts/lesson-check.sh --show-scope
|
|
937
|
+
|
|
938
|
+
# Should show the toolkit's scope tags from CLAUDE.md
|
|
939
|
+
# (If no ## Scope Tags section exists yet, it will fall back to language detection)
|
|
940
|
+
|
|
941
|
+
# Test with --all-scopes
|
|
942
|
+
bash scripts/lesson-check.sh --all-scopes scripts/lesson-check.sh
|
|
943
|
+
```
|
|
944
|
+
|
|
945
|
+
**Step 3: Final commit if needed**
|
|
946
|
+
|
|
947
|
+
```bash
|
|
948
|
+
git add -A
|
|
949
|
+
git commit -m "chore: Phase 5A scope metadata implementation complete"
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
---
|
|
953
|
+
|
|
954
|
+
## Summary
|
|
955
|
+
|
|
956
|
+
| Batch | Tasks | What it delivers |
|
|
957
|
+
|-------|-------|-----------------|
|
|
958
|
+
| 1 | Tasks 1-4 | Core scope engine: parsing, matching, CLI flags, filtering gate |
|
|
959
|
+
| 2 | Tasks 5-7 | Scope inference script + template update |
|
|
960
|
+
| 3 | Tasks 8-10 | Apply scope tags to all 67 lessons + full verification |
|
|
961
|
+
|
|
962
|
+
**Out of scope for this plan (deferred):**
|
|
963
|
+
- Lesson-scanner agent updates (workspace lessons — separate plan)
|
|
964
|
+
- `/capture-lesson` skill integration (scope inference at creation time)
|
|
965
|
+
- Propagation tracking (`validated_in` field)
|
|
966
|
+
- Workspace lessons bulk tagging (76 files in ~/Documents/docs/lessons/)
|
|
967
|
+
|
|
968
|
+
These are listed in the design doc and can be planned as follow-on batches.
|