learn · git:20260616.41b2070 · 2026-06-16 · sha256 e927abd469af8fcb
learn git:20260616.41b2070A
Immutable. This exact content is served forever at /api/v1/blob/e927abd469af8fcb.
---
name: learn
description: Scan codebase to discover coding patterns and update draft/guardrails.md. Learns conventions (skip in future) and anti-patterns (always flag). Supports migration from workflow.md and pattern promotion.
---
# Learn — Pattern Discovery & Guardrails Update
Scan the codebase to discover recurring coding patterns and update `draft/guardrails.md` with learned conventions and anti-patterns. This improves future quality command accuracy by reducing false positives and catching known-bad patterns.
## MANDATORY GRAPH LOOKUP (read before pattern scanning)
First resolve the bundled helpers:
```bash
# Locate Draft's bundled helpers (cwd is the user's project; ${CLAUDE_PLUGIN_ROOT}
# is not exported into skill Bash). See core/shared/tool-resolver.md.
DRAFT_TOOLS="$(cat ~/.cache/draft/plugin-root 2>/dev/null)/scripts/tools"
[ -d "$DRAFT_TOOLS" ] || DRAFT_TOOLS="$(ls -d ~/.claude/plugins/cache/*/draft/*/scripts/tools 2>/dev/null | sort -V | tail -1)"
[ -d "$DRAFT_TOOLS" ] || DRAFT_TOOLS="$(ls -d ~/.claude/plugins/marketplaces/*draft*/scripts/tools 2>/dev/null | tail -1)"
[ -d "$DRAFT_TOOLS" ] || DRAFT_TOOLS="$PWD/scripts/tools"
```
When `draft/graph/schema.yaml` exists, this skill **must** follow the graph-first lookup contract in [core/shared/graph-query.md](../../core/shared/graph-query.md) §Mandatory Lookup Contract. Use the graph to:
1. Enumerate a module's symbols/files via `"$DRAFT_TOOLS/graph-callers.sh"`/`"$DRAFT_TOOLS/graph-impact.sh"` and `"$DRAFT_TOOLS/graph-arch.sh" --repo .` (preferred over `find`).
2. Prioritize hotspots via `"$DRAFT_TOOLS/hotspot-rank.sh" --repo .` — patterns in high-fanIn files are more impactful when learned.
3. For TS/Python/Go/C/C++, use `*-index.jsonl` to identify class/function definitions rather than re-discovering them via regex.
Filesystem `find` for source discovery (Step 2.1) is permitted **as a complement** to the graph for languages not covered by indexes (e.g. Ruby, Java without ctags). Record the rationale in the Graph Usage Report.
## Red Flags - STOP if you're:
See [shared red flags](../../core/shared/red-flags.md) — applies to all code-touching skills.
Skill-specific:
- Writing to guardrails.md without reading the codebase first
- Learning a pattern from fewer than 3 occurrences
- Auto-promoting patterns to Hard Guardrails (requires human approval)
- Overwriting human-curated Hard Guardrails with learned patterns
- Learning patterns that contradict `tech-stack.md ## Accepted Patterns`
- Removing existing learned entries (only update or add)
**Evidence over assumptions. Quantity over anecdote.**
---
## Arguments
- No arguments — full codebase pattern scan
- `promote` — review high-confidence learned patterns and offer promotion to Hard Guardrails or Accepted Patterns
- `migrate` — migrate `## Guardrails` from `workflow.md` to `guardrails.md` (for existing projects)
- `<path>` — scan specific directory or file pattern
---
## Step 0: Verify Draft Context
```bash
ls draft/ 2>/dev/null
```
If `draft/` does not exist: **STOP** — "No Draft context found. Run `/draft:init` first."
---
## Step 1: Load Existing Guardrails
### 1.1: Check for guardrails.md
```bash
ls draft/guardrails.md 2>/dev/null
```
If it exists, read it and internalize:
- Current Hard Guardrails (checked items)
- Current Learned Conventions (existing entries)
- Current Learned Anti-Patterns (existing entries)
**Then verify core guardrails integrity (backfill if missing):**
Check if `draft/guardrails.md` contains the C++/Systems Hard Guardrails from `core/guardrails.md`. Detection: look for the marker heading `### C++/Systems — Object Lifecycle & Memory Safety`.
- **If missing AND project contains C++ code:** The file predates `core/guardrails.md`. Backfill by inserting the full C++/Systems Hard Guardrails sections from `core/templates/guardrails.md` (G1.x–G7.x, all pre-checked `[x]`) into the `## Hard Guardrails` section, after any existing general guardrails. Preserve all existing entries. Announce: "Backfilled C++/Systems Hard Guardrails (G1.x–G7.x) from core/guardrails.md into draft/guardrails.md."
- **If missing AND project has no C++ code:** Skip — these guardrails only apply to C++ projects.
- **If present:** No action — core guardrails already integrated.
### 1.2: Check for Legacy Guardrails (migration path)
If `draft/guardrails.md` does NOT exist:
1. Check `draft/workflow.md` for `## Guardrails` section
2. If found, announce: "Found guardrails in workflow.md. Creating guardrails.md and migrating."
3. Create `draft/guardrails.md` using template from `core/templates/guardrails.md`
4. Copy checked guardrail items from `workflow.md ## Guardrails` into the Hard Guardrails section
5. Add a comment in `workflow.md` where `## Guardrails` was:
```markdown
## Guardrails
> **Migrated** — Guardrails have moved to `draft/guardrails.md`. See that file for hard guardrails, learned conventions, and learned anti-patterns.
```
If `migrate` argument was given, stop here after migration. Otherwise, continue to pattern scanning.
### 1.3: Load Supporting Context
Read and follow `core/shared/draft-context-loading.md` for full Draft context. Key files:
- `draft/.ai-context.md` — Module boundaries, invariants, concurrency model
- `draft/tech-stack.md` — Frameworks, accepted patterns (do not learn patterns that duplicate these)
- `draft/product.md` — Product requirements
---
## Step 2: Codebase Pattern Scan
### 2.1: Discover Source Files
```bash
# Find all source files (exclude vendored, generated, build artifacts)
find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \
-o -name "*.py" -o -name "*.go" -o -name "*.rs" -o -name "*.java" \
-o -name "*.cpp" -o -name "*.cc" -o -name "*.h" -o -name "*.hpp" \
-o -name "*.rb" -o -name "*.php" -o -name "*.swift" -o -name "*.kt" \) \
-not -path "*/node_modules/*" -not -path "*/vendor/*" -not -path "*/.git/*" \
-not -path "*/dist/*" -not -path "*/build/*" -not -path "*/__pycache__/*" \
-not -path "*/draft/*" \
| head -500
```
If scope argument provided, filter to that path.
### 2.2: Analyze Pattern Dimensions
Scan the codebase across these dimensions, looking for **recurring patterns** (3+ occurrences):
#### Error Handling Conventions
- How errors are caught, logged, and propagated
- Custom error classes or error codes
- Try/catch patterns, error boundaries
- Retry and fallback strategies
#### Naming Conventions
- Variable, function, class naming styles beyond language defaults
- File naming patterns (kebab-case, PascalCase, etc.)
- Module/directory organization conventions
#### Architecture Patterns
- Import/dependency patterns (barrel exports, lazy loading)
- State management approaches
- API call patterns (centralized client, interceptors)
- Component composition patterns
#### Concurrency Patterns
- Async/await usage conventions
- Locking and synchronization approaches
- Queue and worker patterns
- Cancellation and timeout handling
#### Data Flow Patterns
- Validation placement (boundary vs deep)
- Serialization/deserialization conventions
- Caching strategies
- Data transformation pipelines
#### Testing Conventions
- Test file placement and naming
- Test structure (arrange/act/assert, given/when/then)
- Mock/stub conventions
- Fixture and factory patterns
#### Configuration Patterns
- Environment variable access patterns
- Feature flag patterns
- Config file conventions
### 2.3: Temporal Pattern Analysis
Detect patterns that are being phased out by the team:
1. **Identify declining patterns** — For each candidate pattern, use `git blame` to check the age of files containing it:
- **Old files** (last modified >1 year ago): high occurrence of the pattern
- **New files** (last modified <6 months ago): low or zero occurrence of the pattern
- If occurrence ratio old:new is >3:1, flag as a declining pattern
2. **Mark declining patterns** — When writing to guardrails.md, add `declining: true` to the entry metadata:
```markdown
- **Declining:** yes — found in 8 old files (avg age 18mo), 1 new file (avg age 2mo). Being replaced by [newer pattern].
```
3. **Do NOT propagate declining patterns** — Quality commands should not flag absence of a declining pattern as inconsistency
4. **Example:** Old error handling style `try/catch with manual logging` found in files last modified >1 year ago, newer files use structured error middleware — the old style is declining, not a convention to enforce
**Reference:** Google large-scale changes (Rosie) — systematic detection of patterns being migrated away from.
### 2.4: Cross-Service Pattern Comparison (Monorepo)
When in a monorepo (detected by `draft/service-index.md` existing OR multiple `draft/` directories OR presence of `packages/`, `services/`, `apps/` directories):
1. **Scan across services** — Run pattern analysis in each service/package independently
2. **Compare patterns for the same concern** — For each pattern dimension (error handling, naming, etc.):
- Does Service A use a different approach than Service B for the same concern?
- Example: Service A uses `Result<T, E>` for error handling, Service B uses exceptions
3. **Flag inconsistencies** — Report cross-service divergences:
```
Cross-service inconsistency: Error Handling
services/auth/ → uses custom Result type (5 files)
services/billing/ → uses thrown exceptions (8 files)
Suggestion: standardize on one approach
```
4. **Respect intentional differences** — Do NOT flag inconsistencies when:
- Services use different languages or frameworks
- The pattern difference is documented in `tech-stack.md` or `.ai-context.md`
- The services have fundamentally different runtime requirements
**Reference:** Google monorepo practices — consistent patterns across services reduce cognitive overhead and enable large-scale tooling.
### 2.5: Cross-Reference Existing Knowledge
For each candidate pattern:
1. **Check `tech-stack.md ## Accepted Patterns`** — if already documented there, skip (no duplication)
2. **Check existing `guardrails.md` entries** — if already learned, update evidence count and date
3. **Check `.ai-context.md`** — if described as architecture, skip (it's documented)
4. **Verify consistency** — sample 3+ instances and confirm they follow the same approach
---
### 2.6: Git History Signal Mining
Mine git commit history for pattern signals that code scanning misses. Run only if the project is a git repository.
```bash
git log --oneline --no-merges -500
```
Scan the output for recurring message patterns (3+ occurrences of the same type):
| Commit pattern | Signal |
|---------------|--------|
| `fix: don't X` / `fix: never X` | Team keeps violating X → anti-pattern candidate |
| `refactor: replace X with Y` | X is declining, Y is the replacement → mark X as `declining: true` |
| `chore: enforce X` / `chore: add X check` | X is being formalized → convention candidate |
| `revert: ` followed by same topic 3+ times | That topic is consistently problematic → anti-pattern candidate |
**Rules:**
- Do NOT add git-only signals as standalone entries. Use them only to adjust confidence of patterns already found in Step 2.2.
- If a pattern appears in both commit history AND code (3+ occurrences): increase confidence by one level.
- If a pattern appears only in commit history but not in current code: note as `historically_recurring: true` — do not add as active anti-pattern.
**Recency weighting** — for each candidate pattern from Step 2.2, check when the files containing it were last modified:
```bash
git log --follow --oneline -1 -- {file_containing_pattern}
```
- Modified within 90 days AND pattern persists → add `recently_active: true` to the entry
- Not modified in 12+ months → add `stale: true` — lower enforcement priority
---
### 2.7: Graph-Aware Severity Enrichment
If `draft/graph/schema.yaml` exists (engine live), derive objective severity for all anti-pattern candidates based on the fanIn of files where the pattern was found via `"$DRAFT_TOOLS/hotspot-rank.sh" --repo .`.
For each anti-pattern candidate from Step 2.2:
1. Check if any evidence files appear in the hotspot output from `"$DRAFT_TOOLS/hotspot-rank.sh" --repo .`
2. Take the highest fanIn value across all evidence files:
- fanIn ≥ 10 → `graph_severity: critical` (breakage propagates to many callers)
- fanIn 5–9 → `graph_severity: high`
- fanIn 1–4 → `graph_severity: medium`
- fanIn 0 or file not in hotspot output → `graph_severity: low`
3. If no graph data exists → `graph_severity: unresolved`
Collect all evidence files with fanIn ≥ 5 for the `high_fanin_files` field.
When `graph_severity` differs from the subjectively assigned `severity`, use `graph_severity` as the enforcement priority in quality commands — it is objective and reproducible.
---
## Step 3: Apply Confidence Threshold
Follow the threshold from `core/shared/pattern-learning.md`:
| Evidence | Confidence | Action |
|----------|------------|--------|
| Found 1-2x | — | Skip (insufficient data) |
| Found 3-5x, all consistent | `medium` | Learn as convention or anti-pattern |
| Found >5x, all consistent, cross-verified | `high` | Learn + flag as promotion candidate |
| Found >5x but inconsistent | — | Do NOT learn (investigate inconsistency) |
**Consistency-claim ground-truth gate (Ground-Truth Discipline G1, G4):** "Consistent" and "cross-verified" require you to have **Read** at least 3 of the instances in this session (5 if claiming `high`). A grep match count is not consistency proof — variant call sites can use the same identifier with different semantics. List the file:line of each Read instance in the learned-pattern record under §Evidence. A `medium` or `high` entry whose Evidence list shows only grep counts is a Red Flag and must be downgraded or discarded.
### Distinguishing Conventions from Anti-Patterns
- **Convention:** Pattern is consistently applied AND does not cause bugs, security issues, or violations of documented invariants
- **Anti-Pattern:** Pattern is consistently applied BUT causes or risks bugs, security issues, performance problems, or invariant violations
---
## Step 3.5: Pattern Conflict Detection
Before saving any new pattern, check for conflicts with existing entries:
1. **Check against existing conventions** — Does the new pattern contradict a learned convention?
2. **Check against existing anti-patterns** — Does the new pattern contradict a learned anti-pattern?
3. **Check against Hard Guardrails** — Does the new pattern violate a hard guardrail?
**If conflict found:**
- Do NOT silently save the new pattern
- Alert the user with both patterns side by side:
```
CONFLICT DETECTED:
Existing convention: "Use async/await for all async operations"
Evidence: 12 files, high confidence, learned 2025-01-15
New candidate: "Avoid async in database module — use callback style"
Evidence: 4 files in src/db/, medium confidence
These may both be valid (module-scoped exception) or one may be outdated.
Options:
[1] Keep both (new pattern is a scoped exception)
[2] Replace existing with new (pattern has evolved)
[3] Discard new (existing is correct)
```
- Wait for user input before proceeding
**Reference:** Google Code Health — conflicting patterns create confusion and should be resolved explicitly.
---
## Step 3.7: External Benchmark Comparison
After discovering patterns, optionally compare project conventions against community standards for the detected language:
| Language | Benchmarks |
|----------|-----------|
| **Go** | Effective Go, Go Code Review Comments |
| **Python** | PEP 8, PEP 20, Google Python Style Guide |
| **Java** | Effective Java, Google Java Style Guide |
| **TypeScript** | typescript-eslint recommended rules |
| **Rust** | Rust API Guidelines, Clippy lints |
| **C/C++** | Google C++ Style Guide, C++ Core Guidelines |
For each project convention that **deviates** from its language's community standard:
1. Note the deviation in the summary report (not as an anti-pattern — deviations may be intentional)
2. If the deviation is undocumented, suggest adding it to `tech-stack.md ## Accepted Patterns` with a rationale
3. Example: project uses `snake_case` for TypeScript functions (deviates from `camelCase` convention) — flag for documentation, not correction
**Reference:** Google Abseil Tips of the Week, language-specific style guides — deviations from community standards increase onboarding friction and should be documented even when intentional.
---
## Step 4: Update guardrails.md
Follow the write procedure in `core/shared/pattern-learning.md`:
1. Read current `draft/guardrails.md`
2. For each new pattern: check for duplicates, then append
3. For existing patterns: update evidence count, confidence, `last_verified`
4. Update `draft/metadata.json` with current git state (use `git-metadata.sh --project-metadata --generated-by "draft:learn"` or update `git.commit`, `git.commit_message`, and `synced_to_commit` manually) — `guardrails.md` frontmatter carries only stable fields per WS-8
**Cap enforcement:** Maintain a maximum of 50 learned entries per section. If at capacity, replace the oldest `medium` confidence entry that has not been re-verified in 90+ days (per `core/shared/pattern-learning.md`).
### Entry Format — Convention
```markdown
### [Pattern Name]
- **Category:** error-handling | naming | architecture | concurrency | state-management | data-flow | testing | configuration
- **Confidence:** high | medium
- **Evidence:** Found in N files — `path/file1.ext:line`, `path/file2.ext:line`, `path/file3.ext:line`
- **Discovered at:** YYYY-MM-DD (when Draft first observed this pattern)
- **Established at:** YYYY-MM-DD (when the pattern entered the codebase, via git blame)
- **Last verified:** YYYY-MM-DD
- **Last active:** YYYY-MM-DD (when source files containing this pattern were last modified)
- **Discovered by:** draft:learn on YYYY-MM-DD
- **Description:** [What the pattern is and why it's intentional]
```
### Entry Format — Anti-Pattern
```markdown
### [Anti-Pattern Name]
- **Category:** security | reliability | performance | correctness | concurrency
- **Severity:** critical | high | medium
- **graph_severity:** critical | high | medium | low | unresolved (fanIn-derived from Step 2.7; "unresolved" if no graph data)
- **high_fanin_files:** `path/file.go` (fanIn:12), `path/other.go` (fanIn:7) (omit line if none meet fanIn ≥ 5)
- **Evidence:** Found in N files — `path/file1.ext:line`, `path/file2.ext:line`
- **Discovered at:** YYYY-MM-DD (when Draft first observed this pattern)
- **Established at:** YYYY-MM-DD (when the pattern entered the codebase, via git blame)
- **Last verified:** YYYY-MM-DD
- **Last active:** YYYY-MM-DD (when source files containing this pattern were last modified)
- **recently_active:** true | false (true if any evidence file modified within 90 days)
- **stale:** true | false (true if all evidence files unmodified for 12+ months)
- **Discovered by:** draft:learn on YYYY-MM-DD
- **Description:** [What the pattern is and why it's problematic]
- **Suggested fix:** [Brief description of the correct approach]
```
---
## Step 5: Promotion Workflow (when `promote` argument given)
Review all learned patterns with `confidence: high` and present promotion candidates:
```
Pattern promotion candidates:
1. [Convention] "Centralized API client pattern" (high confidence, 12 files)
→ Promote to: tech-stack.md ## Accepted Patterns? [y/n]
2. [Convention] "Error boundary at route level" (high confidence, 8 files)
→ Promote to: Hard Guardrail (enforce always)? [y/n]
3. [Anti-Pattern] "Unguarded .env access" (high confidence, 6 files)
→ Promote to: Hard Guardrail (enforce always)? [y/n]
```
For each promoted pattern:
- **Convention → Accepted Pattern**: Append to `draft/tech-stack.md ## Accepted Patterns` and remove from guardrails.md Learned Conventions
- **Convention → Hard Guardrail**: Move from Learned Conventions to Hard Guardrails section (as checked `[x]` item)
- **Anti-Pattern → Hard Guardrail**: Move from Learned Anti-Patterns to Hard Guardrails section (as checked `[x]` item)
---
## Step 6: Generate Summary Report
Display results to the user:
```
/draft:learn complete
Scanned: N source files across M directories
Duration: ~Xs
Results:
New conventions learned: N [list names]
New anti-patterns learned: N [list names]
Existing patterns updated: N [list names]
Skipped (insufficient data): N
Skipped (already documented): N
Promotion candidates (high confidence):
N patterns ready for promotion — run /draft:learn promote to review
Updated: draft/guardrails.md
```
---
## How Quality Commands Use guardrails.md
After `/draft:learn` populates guardrails.md, all quality commands automatically:
| Section | Quality Command Behavior |
|---------|------------------------|
| **Hard Guardrails** (checked) | Flag violations as issues |
| **Learned Conventions** | Skip these patterns during analysis (not bugs) |
| **Learned Anti-Patterns** | Always flag these patterns as bugs |
| **Unchecked Hard Guardrails** | Ignore (not enforced) |
This creates a **continuous improvement loop**:
1. Quality command runs → discovers patterns → updates guardrails.md
2. Next quality command run → reads updated guardrails.md → fewer false positives, catches known-bad patterns
3. `/draft:learn promote` → graduates stable patterns to permanent status
---
## Anti-Patterns
| Don't | Instead |
|-------|---------|
| Learn from <3 occurrences | Require minimum 3 consistent instances |
| Auto-promote to Hard Guardrails | Always require human approval for promotion |
| Overwrite human-curated entries | Learned patterns complement, never replace |
| Learn framework defaults as conventions | Only learn project-specific patterns |
| Remove entries on re-scan | Update evidence/dates, never delete |
| Learn from test/mock code | Focus on production source code |
## Mandatory Self-Check (before completion summary)
Before printing the completion summary, internally verify and report:
1. **Graph files queried** — JSONL files loaded plus any live graph query-tool invocations.
2. **Layer 1 files deliberately skipped** — list any context sections skipped.
3. **Filesystem find/grep fallback justification** — for every `find`/`grep` run for source discovery, state which languages or extensions were not covered by the graph indexes (e.g. Ruby without ctags).
If `draft/graph/schema.yaml` does not exist, set `Graph files queried: NONE` and use justification `graph data unavailable`.
## Graph Usage Report (append to completion summary)
Emit the canonical footer from [core/shared/graph-usage-report.md](../../core/shared/graph-usage-report.md) §Canonical footer. The lint hook `scripts/tools/check-graph-usage-report.sh` validates the section on save.