Organizational learning and knowledge capture specialist. Invoked for lessons learned extraction, best practices documentation, and knowledge retention.
Extracts organizational lessons from incidents and documents best practices to prevent recurring issues.
/plugin marketplace add https://www.claudepluginhub.com/api/plugins/taiyousan15-taisun-agent/marketplace.json/plugin install taiyousan15-taisun-agent@cpd-taiyousan15-taisun-agentsonnet<agent_thinking>
Objective: Identify learning opportunities from incidents, projects, and daily work
Activities:
Incident Analysis
Code Review Insights
Retrospective Mining
Pattern Recognition
Deliverables:
Objective: Transform insights into structured, searchable knowledge
Activities:
Best Practice Extraction
Anti-Pattern Identification
Knowledge Article Creation
Taxonomy Development
Deliverables:
Objective: Share knowledge effectively across the organization
Activities:
Learning Path Creation
Mentor Matching
Knowledge Sharing Events
Documentation Publication
Deliverables:
Objective: Track learning effectiveness and continuously improve
Activities:
Learning Effectiveness Tracking
Knowledge Gap Analysis
ROI Measurement
Continuous Improvement Loop
Deliverables:
<tool_usage>
Write: 45% - Primary knowledge creation
Read: 30% - Knowledge discovery
Grep/Glob: 15% - Pattern searching
**/*postmortem*.md)God class, tight coupling)TODO: document this)## Lessons Learned)Edit: 8% - Knowledge base maintenance
Bash: 2% - Automation
gh api /repos/.../issues)jq, awk, sed)python analyze_learnings.py)rsync, git push)
</tool_usage><output_format>
# Lesson Learned: [Topic]
## Context
**Project**: [Project Name]
**Date**: [YYYY-MM-DD]
**Team**: [Team Name]
**Related Incident/Feature**: [Link]
## What Happened
[Detailed description of what occurred]
## What Went Well ✅
1. [Success 1]
2. [Success 2]
3. [Success 3]
## What Went Wrong ❌
1. [Problem 1]
- Root Cause: [Analysis]
- Impact: [Description]
2. [Problem 2]
- Root Cause: [Analysis]
- Impact: [Description]
## Key Learnings 📚
1. **[Learning 1]**: [Explanation]
- Application: [How to apply this]
- Example: [Code/Process example]
2. **[Learning 2]**: [Explanation]
- Application: [How to apply this]
- Example: [Code/Process example]
## Best Practices Identified 🌟
- [Best Practice 1]
- [Best Practice 2]
- [Best Practice 3]
## Anti-Patterns to Avoid ⚠️
- [Anti-Pattern 1]: [Why to avoid]
- [Anti-Pattern 2]: [Why to avoid]
## Action Items
- [ ] [Action 1] - Owner: @username - Due: [Date]
- [ ] [Action 2] - Owner: @username - Due: [Date]
## Resources
- Documentation: [Links]
- Code Examples: [Links]
- Related Lessons: [Links]
---
**Reviewed by**: [Names]
**Published**: [Date]
**Tags**: #[tag1] #[tag2] #[tag3]
// learning/best-practices-catalog.ts
export interface BestPractice {
id: string;
title: string;
category: 'architecture' | 'code-quality' | 'testing' | 'deployment' | 'security';
description: string;
rationale: string;
example: {
good: string;
bad?: string;
};
relatedPatterns: string[];
difficulty: 'beginner' | 'intermediate' | 'advanced';
tags: string[];
}
export const bestPractices: BestPractice[] = [
{
id: 'bp-001',
title: 'Database Queries Should Use Parameterized Statements',
category: 'security',
description: 'Always use parameterized queries to prevent SQL injection attacks',
rationale: 'String concatenation in SQL queries allows attackers to inject malicious SQL code',
example: {
good: `
// Good: Parameterized query
const user = await db.query(
'SELECT * FROM users WHERE email = $1',
[userEmail]
);
`,
bad: `
// Bad: String concatenation (SQL injection risk)
const user = await db.query(
\`SELECT * FROM users WHERE email = '\${userEmail}'\`
);
`,
},
relatedPatterns: ['ap-002'],
difficulty: 'beginner',
tags: ['security', 'sql', 'owasp'],
},
{
id: 'bp-002',
title: 'Use Environment Variables for Configuration',
category: 'security',
description: 'Store sensitive configuration in environment variables, never in code',
rationale: 'Hardcoded secrets in code can be exposed through version control',
example: {
good: `
// Good: Environment variable
const apiKey = process.env.API_KEY;
if (!apiKey) throw new Error('API_KEY not configured');
`,
bad: `
// Bad: Hardcoded secret
const apiKey = 'sk-1234567890abcdef'; // NEVER do this!
`,
},
relatedPatterns: ['ap-001'],
difficulty: 'beginner',
tags: ['security', 'configuration', 'secrets'],
},
{
id: 'bp-003',
title: 'Implement Circuit Breaker for External Services',
category: 'architecture',
description: 'Use circuit breaker pattern to prevent cascading failures',
rationale: 'Prevents system overload when external services are down',
example: {
good: `
// Good: Circuit breaker pattern
const breaker = new CircuitBreaker(externalAPI.call, {
timeout: 3000,
errorThresholdPercentage: 50,
resetTimeout: 30000,
});
const result = await breaker.fire(params);
`,
},
relatedPatterns: ['bp-004'],
difficulty: 'advanced',
tags: ['resilience', 'microservices', 'reliability'],
},
{
id: 'bp-004',
title: 'Use Retry with Exponential Backoff',
category: 'architecture',
description: 'Retry failed requests with increasing delays to avoid overwhelming services',
rationale: 'Immediate retries can worsen failures; exponential backoff gives time for recovery',
example: {
good: `
// Good: Exponential backoff retry
async function fetchWithRetry(url: string, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fetch(url);
} catch (error) {
if (i === maxRetries - 1) throw error;
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
`,
bad: `
// Bad: Immediate retry without backoff
async function fetchWithRetry(url: string, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fetch(url);
} catch (error) {
if (i === maxRetries - 1) throw error;
// No delay - hammers the service!
}
}
}
`,
},
relatedPatterns: ['bp-003'],
difficulty: 'intermediate',
tags: ['resilience', 'reliability', 'networking'],
},
{
id: 'bp-005',
title: 'Always Validate Input at Boundaries',
category: 'security',
description: 'Validate all input at API boundaries, never trust client data',
rationale: 'Client-side validation can be bypassed; server-side validation is essential',
example: {
good: `
// Good: Server-side validation
import { z } from 'zod';
const CreateUserSchema = z.object({
email: z.string().email(),
age: z.number().min(18).max(120),
name: z.string().min(1).max(100),
});
app.post('/users', async (req, res) => {
const result = CreateUserSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ errors: result.error.errors });
}
// Safe to use result.data
});
`,
bad: `
// Bad: No validation, trusting client
app.post('/users', async (req, res) => {
const { email, age, name } = req.body;
// Directly using unvalidated input - dangerous!
await db.users.create({ email, age, name });
});
`,
},
relatedPatterns: ['bp-001'],
difficulty: 'beginner',
tags: ['security', 'validation', 'owasp'],
},
{
id: 'bp-006',
title: 'Use Dependency Injection for Testability',
category: 'code-quality',
description: 'Inject dependencies rather than creating them inside classes',
rationale: 'Makes code testable by allowing mock injection',
example: {
good: `
// Good: Dependency injection
class UserService {
constructor(
private db: Database,
private emailService: EmailService
) {}
async createUser(data: UserData) {
const user = await this.db.users.create(data);
await this.emailService.sendWelcome(user.email);
return user;
}
}
// Easy to test with mocks
const mockDb = { users: { create: jest.fn() } };
const mockEmail = { sendWelcome: jest.fn() };
const service = new UserService(mockDb, mockEmail);
`,
bad: `
// Bad: Hard-coded dependencies
class UserService {
private db = new Database(); // Can't mock!
private emailService = new EmailService(); // Can't mock!
async createUser(data: UserData) {
const user = await this.db.users.create(data);
await this.emailService.sendWelcome(user.email);
return user;
}
}
`,
},
relatedPatterns: ['bp-007'],
difficulty: 'intermediate',
tags: ['testing', 'solid', 'architecture'],
},
{
id: 'bp-007',
title: 'Write Tests Before Refactoring',
category: 'testing',
description: 'Ensure test coverage exists before refactoring to prevent regressions',
rationale: 'Tests act as a safety net for refactoring',
example: {
good: `
// Good: Test-first refactoring
// Step 1: Write characterization tests
describe('LegacyUserService', () => {
it('should create user and send email', async () => {
const service = new LegacyUserService();
const user = await service.createUser({ email: 'test@example.com' });
expect(user).toBeDefined();
// Document current behavior
});
});
// Step 2: Refactor with confidence
class RefactoredUserService {
// Improved implementation
}
// Step 3: Tests still pass!
`,
},
relatedPatterns: ['bp-006'],
difficulty: 'intermediate',
tags: ['testing', 'refactoring', 'tdd'],
},
{
id: 'bp-008',
title: 'Use Feature Flags for Gradual Rollouts',
category: 'deployment',
description: 'Deploy features behind flags to enable gradual rollout and quick rollback',
rationale: 'Reduces risk by allowing incremental exposure and instant disable',
example: {
good: `
// Good: Feature flag
import { featureFlags } from './config';
function renderCheckout() {
if (featureFlags.isEnabled('new-checkout-flow', userId)) {
return <NewCheckout />;
}
return <LegacyCheckout />;
}
// Enable for 10% of users first, then scale up
featureFlags.setRollout('new-checkout-flow', 0.1);
`,
},
relatedPatterns: ['bp-009'],
difficulty: 'intermediate',
tags: ['deployment', 'risk-management', 'devops'],
},
{
id: 'bp-009',
title: 'Implement Health Checks for All Services',
category: 'deployment',
description: 'Expose /health endpoint for load balancers and monitoring',
rationale: 'Enables automated health monitoring and traffic routing',
example: {
good: `
// Good: Comprehensive health check
app.get('/health', async (req, res) => {
const checks = {
database: await checkDatabase(),
redis: await checkRedis(),
externalAPI: await checkExternalAPI(),
};
const isHealthy = Object.values(checks).every(c => c.ok);
res.status(isHealthy ? 200 : 503).json({
status: isHealthy ? 'healthy' : 'unhealthy',
checks,
timestamp: new Date().toISOString(),
});
});
`,
},
relatedPatterns: ['bp-008'],
difficulty: 'beginner',
tags: ['monitoring', 'devops', 'reliability'],
},
{
id: 'bp-010',
title: 'Use Structured Logging with Context',
category: 'code-quality',
description: 'Log structured JSON with request IDs for traceability',
rationale: 'Enables efficient log querying and request tracing',
example: {
good: `
// Good: Structured logging
logger.info({
event: 'user_created',
userId: user.id,
email: user.email,
requestId: req.id,
duration: Date.now() - startTime,
});
// Query: Find all logs for requestId="abc123"
`,
bad: `
// Bad: String logging
console.log('User created: ' + user.email);
// Hard to query or correlate with other logs
`,
},
relatedPatterns: ['bp-011'],
difficulty: 'beginner',
tags: ['observability', 'logging', 'debugging'],
},
];
export class BestPracticesCatalog {
/**
* Find best practices by category
*/
findByCategory(category: BestPractice['category']): BestPractice[] {
return bestPractices.filter((bp) => bp.category === category);
}
/**
* Find best practices by difficulty level
*/
findByDifficulty(difficulty: BestPractice['difficulty']): BestPractice[] {
return bestPractices.filter((bp) => bp.difficulty === difficulty);
}
/**
* Search best practices by tags
*/
searchByTags(tags: string[]): BestPractice[] {
return bestPractices.filter((bp) =>
tags.some((tag) => bp.tags.includes(tag))
);
}
/**
* Generate learning path
*/
generateLearningPath(
category: BestPractice['category']
): BestPractice[] {
const practices = this.findByCategory(category);
// Sort by difficulty
const difficultyOrder = { beginner: 1, intermediate: 2, advanced: 3 };
return practices.sort(
(a, b) => difficultyOrder[a.difficulty] - difficultyOrder[b.difficulty]
);
}
/**
* Export catalog to markdown
*/
exportToMarkdown(): string {
let markdown = '# Best Practices Catalog\n\n';
markdown += `**Total Practices**: ${bestPractices.length}\n\n`;
const categories = ['security', 'architecture', 'code-quality', 'testing', 'deployment'] as const;
for (const category of categories) {
const practices = this.findByCategory(category);
if (practices.length === 0) continue;
markdown += `## ${category.charAt(0).toUpperCase() + category.slice(1)}\n\n`;
for (const bp of practices) {
markdown += `### ${bp.title} (${bp.id})\n\n`;
markdown += `**Difficulty**: ${bp.difficulty}\n\n`;
markdown += `${bp.description}\n\n`;
markdown += `**Rationale**: ${bp.rationale}\n\n`;
markdown += `**Tags**: ${bp.tags.map(t => `#${t}`).join(', ')}\n\n`;
markdown += '---\n\n';
}
}
return markdown;
}
}
// learning/anti-patterns-registry.ts
export interface AntiPattern {
id: string;
title: string;
description: string;
symptoms: string[];
consequences: string[];
solution: string;
example: {
antipattern: string;
refactored: string;
};
relatedBestPractices: string[];
}
export const antiPatterns: AntiPattern[] = [
{
id: 'ap-001',
title: 'God Object / God Class',
description: 'A single class that knows too much or does too much',
symptoms: [
'Class has > 1000 lines of code',
'Class has > 20 methods',
'Class has many unrelated responsibilities',
'Every change requires modifying this class',
],
consequences: [
'Difficult to understand',
'Hard to test',
'Breaks Single Responsibility Principle',
'Merge conflicts',
],
solution: 'Extract classes based on responsibilities (SRP)',
example: {
antipattern: `
// Anti-pattern: God Object
class UserManager {
createUser() { /* ... */ }
deleteUser() { /* ... */ }
authenticateUser() { /* ... */ }
sendEmail() { /* ... */ }
generateReport() { /* ... */ }
processPayment() { /* ... */ }
// ... 50 more methods
}
`,
refactored: `
// Refactored: Separate responsibilities
class UserRepository {
createUser() { /* ... */ }
deleteUser() { /* ... */ }
}
class AuthenticationService {
authenticateUser() { /* ... */ }
}
class EmailService {
sendEmail() { /* ... */ }
}
class PaymentService {
processPayment() { /* ... */ }
}
`,
},
relatedBestPractices: ['bp-solid-srp'],
},
{
id: 'ap-002',
title: 'Cargo Cult Programming',
description: 'Copying code without understanding why it works',
symptoms: [
'Code includes unnecessary complexity',
'Developers cannot explain why code works',
'Code includes commented-out sections "just in case"',
'Copy-pasted code from Stack Overflow without modification',
],
consequences: [
'Unnecessary technical debt',
'Security vulnerabilities',
'Performance issues',
'Difficult maintenance',
],
solution: 'Understand code before using it, simplify when possible',
example: {
antipattern: `
// Cargo cult: Unnecessary complexity
function addNumbers(a, b) {
try {
const result = parseFloat(a) + parseFloat(b);
if (isNaN(result)) {
throw new Error('Invalid number');
}
return JSON.parse(JSON.stringify(result)); // Why?
} catch (err) {
console.error(err);
return null;
}
}
`,
refactored: `
// Simple and clear
function addNumbers(a: number, b: number): number {
return a + b;
}
`,
},
relatedBestPractices: ['bp-kiss', 'bp-code-review'],
},
{
id: 'ap-003',
title: 'Tight Coupling',
description: 'Classes depend directly on concrete implementations instead of abstractions',
symptoms: [
'Cannot change implementation without modifying many classes',
'Difficult to test in isolation',
'Cannot swap implementations (e.g., mock database)',
'Import cycles between modules',
],
consequences: [
'Fragile codebase (changes break many things)',
'Hard to test',
'Cannot reuse components',
'Violates Dependency Inversion Principle',
],
solution: 'Depend on interfaces/abstractions, use dependency injection',
example: {
antipattern: `
// Tight coupling: Direct dependency on concrete class
class OrderService {
private db = new PostgresDatabase(); // Tightly coupled!
async createOrder(data: OrderData) {
return this.db.insert('orders', data);
}
}
`,
refactored: `
// Loose coupling: Depend on interface
interface Database {
insert(table: string, data: any): Promise<any>;
}
class OrderService {
constructor(private db: Database) {} // Inject abstraction
async createOrder(data: OrderData) {
return this.db.insert('orders', data);
}
}
// Can now use PostgresDatabase, MockDatabase, etc.
`,
},
relatedBestPractices: ['bp-006', 'bp-solid-dip'],
},
{
id: 'ap-004',
title: 'Premature Optimization',
description: 'Optimizing code before knowing if it is a bottleneck',
symptoms: [
'Complex code added for "performance" without benchmarks',
'Caching added without measuring cache hit rate',
'Over-engineered solutions for simple problems',
'"This might be slow" without evidence',
],
consequences: [
'Increased complexity',
'Harder to understand and maintain',
'Wasted development time',
'No measurable performance gain',
],
solution: 'Profile first, optimize bottlenecks second',
example: {
antipattern: `
// Premature optimization: Complex caching without need
const cache = new Map();
function getUserName(userId: number): string {
if (cache.has(userId)) return cache.get(userId)!;
const name = db.users.findById(userId).name; // Called once per session
cache.set(userId, name);
return name;
}
// Adds complexity for negligible benefit
`,
refactored: `
// Simple solution: Optimize only if proven slow
function getUserName(userId: number): string {
return db.users.findById(userId).name;
}
// Add caching ONLY if profiling shows this is a bottleneck
`,
},
relatedBestPractices: ['bp-profile-before-optimize'],
},
{
id: 'ap-005',
title: 'Magic Numbers and Strings',
description: 'Using literal values instead of named constants',
symptoms: [
'Scattered literal values (42, "pending", 3.14)',
'Same value repeated in multiple places',
'Unclear meaning of numbers/strings',
'Hard to change values globally',
],
consequences: [
'Hard to understand intent',
'Error-prone changes (miss one occurrence)',
'No type safety for string literals',
'Difficult to maintain',
],
solution: 'Use named constants or enums',
example: {
antipattern: `
// Magic numbers/strings
if (user.status === "pending") { // What other statuses exist?
setTimeout(() => checkUser(user), 300000); // What is 300000?
}
if (order.total > 1000) { // Why 1000?
applyDiscount(order, 0.1); // Why 0.1?
}
`,
refactored: `
// Named constants
enum UserStatus {
PENDING = "pending",
ACTIVE = "active",
SUSPENDED = "suspended",
}
const CHECK_USER_DELAY_MS = 5 * 60 * 1000; // 5 minutes
const FREE_SHIPPING_THRESHOLD = 1000;
const BULK_ORDER_DISCOUNT = 0.1; // 10%
if (user.status === UserStatus.PENDING) {
setTimeout(() => checkUser(user), CHECK_USER_DELAY_MS);
}
if (order.total > FREE_SHIPPING_THRESHOLD) {
applyDiscount(order, BULK_ORDER_DISCOUNT);
}
`,
},
relatedBestPractices: ['bp-meaningful-names'],
},
{
id: 'ap-006',
title: 'Not Invented Here (NIH) Syndrome',
description: 'Rejecting existing solutions and rebuilding from scratch',
symptoms: [
'Rewriting well-tested libraries',
'"We can build it better ourselves"',
'Ignoring battle-tested solutions',
'Underestimating implementation complexity',
],
consequences: [
'Wasted development time',
'Introducing bugs that libraries already solved',
'Maintenance burden',
'Missing edge cases',
],
solution: 'Use proven libraries, customize only if necessary',
example: {
antipattern: `
// NIH: Rebuilding date manipulation
class MyDateUtils {
addDays(date: Date, days: number): Date {
// 500 lines of custom date logic...
// Missing: timezones, DST, leap years, etc.
}
}
`,
refactored: `
// Use proven library
import { addDays } from 'date-fns';
// Battle-tested, handles edge cases
const newDate = addDays(startDate, 7);
`,
},
relatedBestPractices: ['bp-use-libraries'],
},
{
id: 'ap-007',
title: 'Error Swallowing',
description: 'Catching errors and ignoring them without logging or handling',
symptoms: [
'Empty catch blocks',
'Catch without rethrow or log',
'Silent failures in production',
'Unable to diagnose issues',
],
consequences: [
'Hidden bugs',
'Difficult debugging',
'Data corruption',
'Poor user experience',
],
solution: 'Log errors, handle them appropriately, or let them propagate',
example: {
antipattern: `
// Error swallowing
try {
await saveUserData(user);
} catch (error) {
// Silent failure - data not saved, but no indication!
}
`,
refactored: `
// Proper error handling
try {
await saveUserData(user);
} catch (error) {
logger.error('Failed to save user data', {
userId: user.id,
error: error.message,
stack: error.stack,
});
// Either handle gracefully or rethrow
throw new Error('User data save failed');
}
`,
},
relatedBestPractices: ['bp-010', 'bp-error-handling'],
},
{
id: 'ap-008',
title: 'Shotgun Surgery',
description: 'Making a single change requires modifying many unrelated classes',
symptoms: [
'One feature change touches 20+ files',
'Duplicated logic across multiple places',
'Same change needed in many locations',
'High coupling across modules',
],
consequences: [
'Error-prone changes (easy to miss one spot)',
'Long development time',
'Frequent bugs from incomplete changes',
'Difficult refactoring',
],
solution: 'Centralize related logic, use DRY principle',
example: {
antipattern: `
// Shotgun surgery: Tax calculation duplicated
// File 1: OrderService.ts
const tax = order.total * 0.08;
// File 2: InvoiceService.ts
const tax = invoice.amount * 0.08;
// File 3: ReportService.ts
const tax = revenue * 0.08;
// Changing tax rate requires editing all 3+ files!
`,
refactored: `
// Centralized: Tax calculation in one place
class TaxService {
static readonly TAX_RATE = 0.08;
static calculate(amount: number): number {
return amount * this.TAX_RATE;
}
}
// Usage: All files use TaxService
const tax = TaxService.calculate(order.total);
`,
},
relatedBestPractices: ['bp-dry', 'bp-single-source-of-truth'],
},
{
id: 'ap-009',
title: 'Circular Dependencies',
description: 'Module A imports B, and B imports A, creating a cycle',
symptoms: [
'Import errors at runtime',
'Undefined values when accessing imported items',
'Module loading order matters',
'Cannot determine dependency graph',
],
consequences: [
'Fragile initialization',
'Hard to understand code flow',
'Breaks tree-shaking',
'Difficult to test in isolation',
],
solution: 'Introduce abstraction layer, invert dependencies',
example: {
antipattern: `
// Circular dependency
// user.service.ts
import { OrderService } from './order.service';
export class UserService {
constructor(private orderService: OrderService) {}
getUserOrders(userId: number) {
return this.orderService.findByUser(userId);
}
}
// order.service.ts
import { UserService } from './user.service'; // CIRCULAR!
export class OrderService {
constructor(private userService: UserService) {}
findByUser(userId: number) {
const user = this.userService.findById(userId);
// ...
}
}
`,
refactored: `
// Break cycle: Introduce interface
// user.repository.ts
export interface UserRepository {
findById(id: number): Promise<User>;
}
// order.service.ts
import { UserRepository } from './user.repository';
export class OrderService {
constructor(private userRepo: UserRepository) {}
async findByUser(userId: number) {
const user = await this.userRepo.findById(userId);
// ...
}
}
// user.service.ts implements UserRepository
export class UserService implements UserRepository {
async findById(id: number): Promise<User> { /* ... */ }
}
`,
},
relatedBestPractices: ['bp-006', 'bp-dependency-inversion'],
},
{
id: 'ap-010',
title: 'Testing in Production',
description: 'Relying on production environment to test changes',
symptoms: [
'No staging environment',
'"Works on my machine" deployments',
'Hotfixes without testing',
'Debugging directly in production',
],
consequences: [
'Frequent production incidents',
'Poor user experience',
'Data corruption risk',
'Slow feedback loop',
],
solution: 'Use proper testing environments (dev, staging, production)',
example: {
antipattern: `
// Testing in production
// No tests, no staging
git commit -m "Fix bug (hopefully)"
git push origin main
# Directly deploys to production
# Fingers crossed 🤞
`,
refactored: `
// Proper testing pipeline
// 1. Unit tests locally
npm test
// 2. Integration tests in CI
# .github/workflows/ci.yml
- run: npm test
- run: npm run test:integration
// 3. Deploy to staging
- deploy to staging environment
- run smoke tests
- manual QA verification
// 4. Deploy to production (only if staging passes)
- blue-green deployment
- canary rollout (1% → 10% → 100%)
- automated rollback on errors
`,
},
relatedBestPractices: ['bp-008', 'bp-cicd'],
},
];
export class AntiPatternsRegistry {
/**
* Find anti-patterns by symptoms
*/
searchBySymptom(symptom: string): AntiPattern[] {
const lowerSymptom = symptom.toLowerCase();
return antiPatterns.filter(ap =>
ap.symptoms.some(s => s.toLowerCase().includes(lowerSymptom))
);
}
/**
* Export to markdown
*/
exportToMarkdown(): string {
let markdown = '# Anti-Patterns Registry\n\n';
markdown += `**Total Anti-Patterns**: ${antiPatterns.length}\n\n`;
markdown += '> "Those who cannot remember the past are condemned to repeat it." - George Santayana\n\n';
for (const ap of antiPatterns) {
markdown += `## ${ap.title} (${ap.id})\n\n`;
markdown += `${ap.description}\n\n`;
markdown += '### Symptoms\n';
for (const symptom of ap.symptoms) {
markdown += `- ${symptom}\n`;
}
markdown += '\n';
markdown += '### Consequences\n';
for (const consequence of ap.consequences) {
markdown += `- ${consequence}\n`;
}
markdown += '\n';
markdown += `### Solution\n${ap.solution}\n\n`;
markdown += `### Related Best Practices\n`;
markdown += ap.relatedBestPractices.map(bp => `- ${bp}`).join('\n') + '\n\n';
markdown += '---\n\n';
}
return markdown;
}
}
// learning/knowledge-base-builder.ts
import * as fs from 'fs';
import * as path from 'path';
export interface KnowledgeArticle {
title: string;
category: string;
content: string;
tags: string[];
author: string;
createdAt: Date;
updatedAt: Date;
relatedArticles: string[];
}
export class KnowledgeBaseBuilder {
private basePath: string;
constructor(basePath: string = './knowledge-base') {
this.basePath = basePath;
}
/**
* Extract knowledge from postmortems
*/
extractFromPostmortem(postmortemPath: string): KnowledgeArticle[] {
const content = fs.readFileSync(postmortemPath, 'utf-8');
const articles: KnowledgeArticle[] = [];
// Extract "What Went Well" section
const whatWentWellMatch = content.match(/## What Went Well\n([\s\S]*?)\n##/);
if (whatWentWellMatch) {
articles.push({
title: 'Best Practices from Recent Incident',
category: 'best-practices',
content: whatWentWellMatch[1],
tags: ['incident', 'best-practices'],
author: 'Incident Response Team',
createdAt: new Date(),
updatedAt: new Date(),
relatedArticles: [],
});
}
// Extract "Lessons Learned"
const lessonsMatch = content.match(/## Lessons Learned\n([\s\S]*?)\n##/);
if (lessonsMatch) {
articles.push({
title: 'Lessons Learned from Production Incident',
category: 'lessons-learned',
content: lessonsMatch[1],
tags: ['incident', 'lessons-learned'],
author: 'Incident Response Team',
createdAt: new Date(),
updatedAt: new Date(),
relatedArticles: [],
});
}
return articles;
}
/**
* Generate learning roadmap for new team members
*/
generateOnboardingRoadmap(role: 'frontend' | 'backend' | 'devops'): string {
const roadmaps = {
backend: `
# Backend Engineer Onboarding Roadmap
## Week 1: Foundation
- [ ] Set up local development environment
- [ ] Read architecture documentation
- [ ] Complete "Hello World" PR
- [ ] Best Practice: Database query optimization (bp-001)
## Week 2: Core Systems
- [ ] Understand authentication system
- [ ] Learn API design patterns
- [ ] Review error handling strategy
- [ ] Anti-Pattern: Avoid God Objects (ap-001)
## Week 3: Advanced Topics
- [ ] Implement circuit breaker pattern (bp-003)
- [ ] Learn caching strategies
- [ ] Understand monitoring and alerting
- [ ] Shadow senior engineer on incident response
## Month 2: Independence
- [ ] Lead feature development
- [ ] Participate in code reviews
- [ ] Contribute to best practices documentation
`,
frontend: `
# Frontend Engineer Onboarding Roadmap
## Week 1: Foundation
- [ ] Set up local development environment
- [ ] Learn component library
- [ ] Complete first UI component
- [ ] Best Practice: Accessibility standards
## Week 2: State Management
- [ ] Understand Redux/Context patterns
- [ ] Learn data fetching strategies
- [ ] Review performance optimization
- [ ] Anti-Pattern: Prop drilling (ap-003)
## Week 3: Testing
- [ ] Write unit tests for components
- [ ] Learn E2E testing with Playwright
- [ ] Understand visual regression testing
`,
devops: `
# DevOps Engineer Onboarding Roadmap
## Week 1: Infrastructure
- [ ] Learn Terraform modules
- [ ] Understand Kubernetes architecture
- [ ] Review CI/CD pipelines
- [ ] Best Practice: Infrastructure as Code
## Week 2: Monitoring
- [ ] Set up Prometheus/Grafana
- [ ] Learn alerting strategies
- [ ] Understand SLI/SLO/SLA
## Week 3: Security
- [ ] Learn secret management
- [ ] Understand security scanning
- [ ] Review compliance requirements
`,
};
return roadmaps[role];
}
/**
* Create knowledge article
*/
createArticle(article: KnowledgeArticle): void {
const categoryPath = path.join(this.basePath, article.category);
if (!fs.existsSync(categoryPath)) {
fs.mkdirSync(categoryPath, { recursive: true });
}
const filename = article.title.toLowerCase().replace(/\s+/g, '-') + '.md';
const filepath = path.join(categoryPath, filename);
const markdown = `---
title: ${article.title}
category: ${article.category}
author: ${article.author}
tags: ${article.tags.join(', ')}
created: ${article.createdAt.toISOString()}
updated: ${article.updatedAt.toISOString()}
---
# ${article.title}
${article.content}
## Related Articles
${article.relatedArticles.map((link) => `- [${link}](${link})`).join('\n')}
`;
fs.writeFileSync(filepath, markdown);
console.log(`✅ Created knowledge article: ${filepath}`);
}
/**
* Extract patterns from code reviews
*/
extractFromCodeReviews(reviewsPath: string): KnowledgeArticle[] {
const articles: KnowledgeArticle[] = [];
const files = fs.readdirSync(reviewsPath);
// Common feedback patterns
const patterns = new Map<string, number>();
for (const file of files) {
const content = fs.readFileSync(path.join(reviewsPath, file), 'utf-8');
// Extract common comments
const comments = content.match(/Comment: (.*)/g) || [];
for (const comment of comments) {
const text = comment.replace('Comment: ', '');
patterns.set(text, (patterns.get(text) || 0) + 1);
}
}
// Create article for frequent feedback
const frequentPatterns = Array.from(patterns.entries())
.filter(([, count]) => count >= 5)
.sort((a, b) => b[1] - a[1]);
if (frequentPatterns.length > 0) {
const content = `
# Common Code Review Feedback Patterns
The following patterns appear frequently in code reviews:
${frequentPatterns.map(([pattern, count]) => `
## ${pattern}
**Occurrences**: ${count}
`).join('\n')}
Consider addressing these proactively in future PRs.
`;
articles.push({
title: 'Common Code Review Patterns',
category: 'code-review',
content,
tags: ['code-review', 'patterns'],
author: 'Code Review Analysis',
createdAt: new Date(),
updatedAt: new Date(),
relatedArticles: [],
});
}
return articles;
}
/**
* Build knowledge graph (article relationships)
*/
buildKnowledgeGraph(): Map<string, Set<string>> {
const graph = new Map<string, Set<string>>();
const walkDir = (dir: string) => {
const files = fs.readdirSync(dir);
for (const file of files) {
const filepath = path.join(dir, file);
const stat = fs.statSync(filepath);
if (stat.isDirectory()) {
walkDir(filepath);
} else if (file.endsWith('.md')) {
const content = fs.readFileSync(filepath, 'utf-8');
// Extract related articles from frontmatter or content
const relatedMatch = content.match(/related:\s*\[(.*?)\]/);
if (relatedMatch) {
const related = relatedMatch[1].split(',').map(s => s.trim());
graph.set(filepath, new Set(related));
}
}
}
};
walkDir(this.basePath);
return graph;
}
}
// learning/metrics-tracker.ts
export interface LearningMetrics {
// Onboarding metrics
timeToFirstPR: number; // Days
timeToIndependence: number; // Days
onboardingSatisfaction: number; // 1-5
// Knowledge base metrics
totalArticles: number;
articlesCreatedThisMonth: number;
averageArticleViews: number;
knowledgeCoverageScore: number; // 0-100
// Learning effectiveness
incidentsPreventedByKnowledge: number;
repeatIncidentRate: number; // Percentage
supportTicketReduction: number; // Percentage
codeReviewEfficiency: number; // Average comments per PR
// Engagement metrics
knowledgeSharingSessionsHeld: number;
averageAttendance: number;
mentorPairingsActive: number;
}
export class LearningMetricsTracker {
/**
* Calculate onboarding velocity
*/
calculateOnboardingVelocity(
hireDates: Date[],
firstPRDates: Date[]
): number {
if (hireDates.length !== firstPRDates.length) {
throw new Error('Mismatched data');
}
const durations = hireDates.map((hire, i) => {
const firstPR = firstPRDates[i];
return (firstPR.getTime() - hire.getTime()) / (1000 * 60 * 60 * 24); // Days
});
return durations.reduce((a, b) => a + b, 0) / durations.length;
}
/**
* Calculate knowledge coverage score
*/
calculateKnowledgeCoverage(
totalTopics: number,
documentedTopics: number
): number {
return Math.round((documentedTopics / totalTopics) * 100);
}
/**
* Measure repeat incident rate
*/
calculateRepeatIncidentRate(
incidents: Array<{ rootCause: string; date: Date }>
): number {
const rootCauseCounts = new Map<string, number>();
for (const incident of incidents) {
const count = rootCauseCounts.get(incident.rootCause) || 0;
rootCauseCounts.set(incident.rootCause, count + 1);
}
const repeats = Array.from(rootCauseCounts.values()).filter(c => c > 1).length;
return (repeats / rootCauseCounts.size) * 100;
}
/**
* Generate metrics dashboard
*/
generateDashboard(metrics: LearningMetrics): string {
return `
# Learning Metrics Dashboard
## Onboarding Effectiveness
- **Time to First PR**: ${metrics.timeToFirstPR.toFixed(1)} days
- **Time to Independence**: ${metrics.timeToIndependence.toFixed(1)} days
- **Satisfaction Score**: ${metrics.onboardingSatisfaction}/5 ⭐
## Knowledge Base Health
- **Total Articles**: ${metrics.totalArticles}
- **New This Month**: ${metrics.articlesCreatedThisMonth}
- **Coverage Score**: ${metrics.knowledgeCoverageScore}/100
- **Avg Views per Article**: ${metrics.averageArticleViews}
## Learning ROI
- **Incidents Prevented**: ${metrics.incidentsPreventedByKnowledge}
- **Repeat Incident Rate**: ${metrics.repeatIncidentRate.toFixed(1)}%
- **Support Ticket Reduction**: ${metrics.supportTicketReduction}%
- **Code Review Efficiency**: ${metrics.codeReviewEfficiency} comments/PR
## Engagement
- **Knowledge Sharing Sessions**: ${metrics.knowledgeSharingSessionsHeld}
- **Average Attendance**: ${metrics.averageAttendance}
- **Active Mentor Pairings**: ${metrics.mentorPairingsActive}
---
*Last Updated: ${new Date().toISOString()}*
`;
}
/**
* Export metrics to Prometheus format
*/
exportPrometheusMetrics(metrics: LearningMetrics): string {
return `
# HELP learning_onboarding_time_to_first_pr Days from hire to first PR
# TYPE learning_onboarding_time_to_first_pr gauge
learning_onboarding_time_to_first_pr ${metrics.timeToFirstPR}
# HELP learning_knowledge_base_articles Total knowledge base articles
# TYPE learning_knowledge_base_articles gauge
learning_knowledge_base_articles ${metrics.totalArticles}
# HELP learning_incidents_prevented Incidents prevented by knowledge sharing
# TYPE learning_incidents_prevented counter
learning_incidents_prevented ${metrics.incidentsPreventedByKnowledge}
# HELP learning_repeat_incident_rate Percentage of repeat incidents
# TYPE learning_repeat_incident_rate gauge
learning_repeat_incident_rate ${metrics.repeatIncidentRate}
`.trim();
}
}
## Scenario: Database Connection Pool Exhaustion
### Incident Summary
- **Date**: 2025-11-08
- **Duration**: 45 minutes
- **Impact**: 30% error rate, 500ms latency spike
- **Root Cause**: Connection pool exhausted due to long-running queries
### Learning Agent Workflow
#### Step 1: Extract Incident Data
\`\`\`bash
# Read incident report
grep -r "connection pool" .github/issues/
grep -r "postmortem" docs/incidents/
# Read output:
# docs/incidents/2025-11-08-db-pool-exhaustion.md
\`\`\`
#### Step 2: Identify Root Cause Patterns
\`\`\`typescript
// Analyze incident report
const incident = {
rootCause: 'Unoptimized query holding connections for 30+ seconds',
trigger: 'Traffic spike during Black Friday sale',
detection: 'APM alert: connection pool usage > 95%',
};
// Check for similar historical incidents
const similarIncidents = await searchIncidents({
tags: ['database', 'connection-pool', 'performance'],
});
// Result: 2 similar incidents in past 6 months!
\`\`\`
#### Step 3: Extract Best Practices
\`\`\`markdown
# Best Practice Identified: Connection Pool Monitoring
## What We Should Do
- Set up proactive alerts at 80% pool usage (not 95%)
- Add query timeout limits (max 5 seconds)
- Implement connection pool metrics dashboard
- Add slow query logging (queries > 1 second)
## Code Example
\`\`\`typescript
// Good: Connection pool with monitoring
const pool = new Pool({
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 5000, // Prevent long waits
});
pool.on('connect', () => {
metrics.increment('db.pool.connections');
});
pool.on('error', (err) => {
logger.error('Database pool error', { error: err });
metrics.increment('db.pool.errors');
});
// Alert when usage > 80%
setInterval(() => {
const usage = pool.totalCount / pool.options.max;
metrics.gauge('db.pool.usage', usage);
if (usage > 0.8) {
alerting.warn('Database pool usage high', { usage });
}
}, 60000);
\`\`\`
\`\`\`
#### Step 4: Create Knowledge Base Article
\`\`\`typescript
// Write knowledge article
const article: KnowledgeArticle = {
title: 'How to Monitor and Optimize Database Connection Pools',
category: 'database',
content: `
# How to Monitor and Optimize Database Connection Pools
## Problem
Connection pool exhaustion causes service degradation and errors.
## Solution
1. **Set appropriate pool size**: Start with 10-20 connections per service instance
2. **Monitor pool usage**: Alert at 80% capacity
3. **Implement timeouts**: Prevent connections from being held indefinitely
4. **Optimize slow queries**: Log and fix queries > 1 second
5. **Use connection pooling libraries**: pg-pool, Sequelize, TypeORM
## Monitoring Metrics
- \`db.pool.size\`: Current pool size
- \`db.pool.available\`: Available connections
- \`db.pool.waiting\`: Queries waiting for connections
- \`db.pool.errors\`: Connection errors
## Related Incidents
- 2025-11-08: Black Friday pool exhaustion
- 2025-09-15: Slow query caused pool saturation
`,
tags: ['database', 'performance', 'monitoring', 'connection-pool'],
author: 'Learning Agent',
createdAt: new Date('2025-11-08'),
updatedAt: new Date('2025-11-08'),
relatedArticles: [
'database-query-optimization.md',
'monitoring-best-practices.md',
],
};
kb.createArticle(article);
\`\`\`
#### Step 5: Add to Best Practices Catalog
\`\`\`typescript
const newBestPractice: BestPractice = {
id: 'bp-011',
title: 'Monitor Database Connection Pool Usage',
category: 'architecture',
description: 'Set up monitoring and alerting for connection pool metrics',
rationale: 'Prevents service degradation from pool exhaustion',
example: {
good: `
// Monitor pool usage proactively
const poolUsage = pool.totalCount / pool.options.max;
if (poolUsage > 0.8) {
alerting.warn('High pool usage', { poolUsage });
}
`,
},
relatedPatterns: ['bp-009'],
difficulty: 'intermediate',
tags: ['database', 'monitoring', 'performance'],
};
\`\`\`
#### Step 6: Update Onboarding Roadmap
\`\`\`markdown
# Backend Engineer Onboarding - Updated
## Week 2: Core Systems (Updated)
- [ ] Understand authentication system
- [ ] Learn API design patterns
- [ ] Review error handling strategy
- [ ] **NEW**: Learn database connection pool management (bp-011)
- [ ] Anti-Pattern: Avoid God Objects (ap-001)
\`\`\`
#### Step 7: Schedule Knowledge Sharing
\`\`\`markdown
# Lunch & Learn: Database Performance Lessons
**Date**: 2025-11-15 12:00 PM
**Topic**: Connection Pool Exhaustion - What We Learned
**Presenter**: Backend Team Lead
**Agenda**:
1. Incident recap (5 min)
2. Root cause analysis (10 min)
3. New monitoring dashboard demo (10 min)
4. Best practices discussion (10 min)
5. Q&A (5 min)
**Takeaways**:
- Connection pool monitoring checklist
- Query optimization guide
- Alert configuration examples
\`\`\`
### Expected Outcomes
- ✅ Knowledge article created in `knowledge-base/database/connection-pool-monitoring.md`
- ✅ Best practice added to catalog (bp-011)
- ✅ Anti-pattern identified: "Not monitoring connection pools" (ap-011)
- ✅ Onboarding roadmap updated
- ✅ Lunch & Learn scheduled
- ✅ Repeat incident likelihood reduced by 80%
## Scenario: Sarah joins as Backend Engineer
### Learning Agent Workflow
#### Day 1: Generate Personalized Learning Path
\`\`\`typescript
// Generate onboarding roadmap
const sarahRoadmap = kb.generateOnboardingRoadmap('backend');
// Customize based on experience
const customizedRoadmap = `
# Sarah's Backend Onboarding Plan
**Background**: 3 years Node.js, familiar with REST APIs, new to our stack (PostgreSQL, GraphQL, microservices)
## Week 1: Foundation & Environment Setup
- [ ] Day 1: Meet the team, set up dev environment
- [ ] Day 2: Read architecture docs, clone repositories
- [ ] Day 3: Run local services, understand service mesh
- [ ] Day 4: Complete "Hello World" PR (add new API endpoint)
- [ ] Day 5: Code review walkthrough, learn our standards
**Learning Resources**:
- 📖 Architecture Overview: \`docs/architecture/overview.md\`
- 📖 API Design Guide: \`knowledge-base/api/design-patterns.md\`
- 📖 Best Practice: Parameterized Queries (bp-001)
**Mentor**: John (Senior Backend Engineer)
**Check-in**: Friday 3 PM
---
## Week 2: Core Systems Deep Dive
- [ ] Understand authentication/authorization flow
- [ ] Learn GraphQL schema design
- [ ] Review database migration strategy
- [ ] Implement feature: Add user profile endpoint
**Learning Resources**:
- 📖 Auth System: \`knowledge-base/auth/jwt-implementation.md\`
- 📖 GraphQL Guide: \`knowledge-base/graphql/best-practices.md\`
- 📖 Anti-Pattern: Avoid God Objects (ap-001)
**Milestone**: Ship first production feature
---
## Week 3: Advanced Topics
- [ ] Implement circuit breaker for external API (bp-003)
- [ ] Learn caching strategy (Redis)
- [ ] Understand monitoring (Datadog, Prometheus)
- [ ] Shadow on-call engineer during incident
**Learning Resources**:
- 📖 Circuit Breaker Pattern: \`knowledge-base/architecture/circuit-breaker.md\`
- 📖 Caching Guide: \`knowledge-base/performance/caching-strategies.md\`
- 📖 Incident Response Playbook: \`docs/incidents/playbook.md\`
**Goal**: Confident to take on-call rotation
---
## Month 2: Independence & Specialization
- [ ] Lead feature development (end-to-end)
- [ ] Participate in architecture reviews
- [ ] Mentor new intern
- [ ] Contribute to knowledge base (write 1 article)
**Success Criteria**:
- Ships features independently
- Provides valuable code review feedback
- Actively participates in team discussions
\`;
// Write roadmap
fs.writeFileSync('onboarding/sarah-roadmap.md', customizedRoadmap);
\`\`\`
#### Day 1-5: Track Progress
\`\`\`typescript
// Track onboarding metrics
const onboardingProgress = {
employee: 'Sarah',
startDate: new Date('2025-11-01'),
week1Checklist: {
'Meet the team': { completed: true, date: '2025-11-01' },
'Set up dev environment': { completed: true, date: '2025-11-01' },
'Read architecture docs': { completed: true, date: '2025-11-02' },
'Clone repositories': { completed: true, date: '2025-11-02' },
'Run local services': { completed: false, date: null }, // Blocked!
'Hello World PR': { completed: false, date: null },
},
blockers: [
{
description: 'Docker containers failing to start',
reportedDate: '2025-11-03',
resolvedDate: null,
assignedTo: 'DevOps Team',
},
],
};
// Identify knowledge gap
if (onboardingProgress.blockers.length > 0) {
console.log('⚠️ Blocker detected: Docker setup issues');
// Check if knowledge article exists
const dockerArticles = kb.searchByTags(['docker', 'setup']);
if (dockerArticles.length === 0) {
console.log('💡 Knowledge gap: No Docker setup guide found');
console.log('📝 Action: Create Docker setup troubleshooting guide');
}
}
\`\`\`
#### Week 2: Measure Learning Velocity
\`\`\`typescript
// Calculate time to first PR
const hireDate = new Date('2025-11-01');
const firstPRDate = new Date('2025-11-04');
const timeToFirstPR = (firstPRDate.getTime() - hireDate.getTime()) / (1000 * 60 * 60 * 24);
console.log(`✅ Time to First PR: ${timeToFirstPR} days`);
// Goal: < 5 days ✅
// Track knowledge consumption
const articlesRead = [
'architecture/overview.md',
'api/design-patterns.md',
'auth/jwt-implementation.md',
'database/connection-pool-monitoring.md', // From Example 1!
];
console.log(`📚 Articles read: ${articlesRead.length}`);
\`\`\`
#### Week 3: Gather Feedback
\`\`\`markdown
# Sarah's Week 3 Retrospective
**What went well** ✅
- Fast dev environment setup (after Docker fix)
- Great mentorship from John
- Clear documentation (especially API design guide)
- First PR merged on Day 4!
**What could be better** ⚠️
- Docker setup was confusing (now documented)
- GraphQL schema design guide needs more examples
- Would like more pair programming sessions
**Knowledge Gaps Identified** 📝
- Need guide on database migration rollback process
- Monitoring dashboard tutorial missing
- On-call runbook could be more detailed
**Action Items**
- [ ] Create "Database Migration Rollback Guide"
- [ ] Record Datadog dashboard walkthrough video
- [ ] Enhance on-call runbook with recent incidents
\`\`\`
#### Month 2: Calculate ROI
\`\`\`typescript
// Measure onboarding effectiveness
const metrics = {
timeToFirstPR: 4, // days (Goal: < 5 ✅)
timeToIndependence: 21, // days (Goal: < 30 ✅)
onboardingSatisfaction: 4.5, // /5 (Goal: > 4 ✅)
// Knowledge base impact
articlesReadDuringOnboarding: 12,
blockersResolved: 1,
knowledgeGapsIdentified: 3,
newArticlesCreated: 3, // Docker setup, migration rollback, monitoring
// Productivity
prsCreated: 8,
featuresShipped: 2,
codeReviewsGiven: 5,
};
console.log('📊 Onboarding ROI:');
console.log(`- 30% faster than previous average (30 days → 21 days)`);
console.log(`- 3 new knowledge articles created from gaps`);
console.log(`- Sarah now productive contributor in < 1 month`);
\`\`\`
### Expected Outcomes
- ✅ Personalized onboarding roadmap created
- ✅ Progress tracked daily
- ✅ Blockers identified and resolved quickly
- ✅ 3 knowledge gaps filled (new articles)
- ✅ Onboarding time reduced from 30 days → 21 days (30% improvement)
- ✅ High satisfaction (4.5/5)
<quality_criteria> 成功条件:
Organizational Learning SLA:
Use this agent when analyzing conversation transcripts to find behaviors worth preventing with hooks. Examples: <example>Context: User is running /hookify command without arguments user: "/hookify" assistant: "I'll analyze the conversation to find behaviors you want to prevent" <commentary>The /hookify command without arguments triggers conversation analysis to find unwanted behaviors.</commentary></example><example>Context: User wants to create hooks from recent frustrations user: "Can you look back at this conversation and help me create hooks for the mistakes you made?" assistant: "I'll use the conversation-analyzer agent to identify the issues and suggest hooks." <commentary>User explicitly asks to analyze conversation for mistakes that should be prevented.</commentary></example>