引言

仓库源码地址:https://github.com/affaan-m/everything-claude-code

本文来自 Anthropic 黑客马拉松获胜者的完整 Claude Code 配置集合,该仓库包含:生产级代理、技能、钩子、命令、规则和 MCP 配置,经过 10 多个月构建真实产品的密集日常使用而演化,本文主要记录 Skills 模块。

在这里插入图片描述

本文不讲废话,直接上示例,大家可以直接根据目录导航来根据需要查看实际的生产配置,相关概念目录如下:

  • 后端模式(backend-patterns)
  • ClickHouse IO(clickhouse-io)
  • 编码标准(coding-standards)
  • 持续学习(continuous-learning)
  • 持续学习v2(continuous-learning-v2)
  • 评估框架(eval-harness)
  • 前端模式(frontend-patterns)
  • Golang模式(golang-patterns)
  • Golang测试(golang-testing)
  • 迭代检索(iterative-retrieval)
  • Postgres模式(postgres-patterns)
  • 项目指南示例(project-guidelines-example)
  • 云基础设施安全(cloud-infrastructure-security)
  • 安全性审查技能(SKILL)
  • 策略性压缩(strategic-compact)
  • TDD工作流(tdd-workflow)
  • 验证循环(verification-loop)

后端模式(backend-patterns)


---
name: backend-patterns
description: Backend architecture patterns, API design, database optimization, and server-side best practices for Node.js, Express, and Next.js API routes.
---

# 后端开发模式

用于可扩展伺服器端应用程式的后端架构模式和最佳实务。

## API 设计模式

### RESTful API 结构

```typescript
// ✅ 基于资源的 URL
GET    /api/markets                 # 列出资源
GET    /api/markets/:id             # 取得单一资源
POST   /api/markets                 # 建立资源
PUT    /api/markets/:id             # 替换资源
PATCH  /api/markets/:id             # 更新资源
DELETE /api/markets/:id             # 删除资源

// ✅ 用于过滤、排序、分页的查询参数
GET /api/markets?status=active&sort=volume&limit=20&offset=0
```

### Repository 模式

```typescript
// 抽象资料存取逻辑
interface MarketRepository {
  findAll(filters?: MarketFilters): Promise

ClickHouse IO(clickhouse-io)


---
name: clickhouse-io
description: ClickHouse database patterns, query optimization, analytics, and data engineering best practices for high-performance analytical workloads.
---

# ClickHouse 分析模式

用于高效能分析和资料工程的 ClickHouse 特定模式。

## 概述

ClickHouse 是一个列式资料库管理系统(DBMS),用于线上分析处理(OLAP)。它针对大型资料集的快速分析查询进行了优化。

**关键特性:**
- 列式储存
- 资料压缩
- 平行查询执行
- 分散式查询
- 即时分析

## 表格设计模式

### MergeTree 引擎(最常见)

```sql
CREATE TABLE markets_analytics (
    date Date,
    market_id String,
    market_name String,
    volume UInt64,
    trades UInt32,
    unique_traders UInt32,
    avg_trade_size Float64,
    created_at DateTime
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(date)
ORDER BY (date, market_id)
SETTINGS index_granularity = 8192;
```

### ReplacingMergeTree(去重)

```sql
-- 用于可能有重复的资料(例如来自多个来源)
CREATE TABLE user_events (
    event_id String,
    user_id String,
    event_type String,
    timestamp DateTime,
    properties String
) ENGINE = ReplacingMergeTree()
PARTITION BY toYYYYMM(timestamp)
ORDER BY (user_id, event_id, timestamp)
PRIMARY KEY (user_id, event_id);
```

### AggregatingMergeTree(预聚合)

```sql
-- 用于维护聚合指标
CREATE TABLE market_stats_hourly (
    hour DateTime,
    market_id String,
    total_volume AggregateFunction(sum, UInt64),
    total_trades AggregateFunction(count, UInt32),
    unique_users AggregateFunction(uniq, String)
) ENGINE = AggregatingMergeTree()
PARTITION BY toYYYYMM(hour)
ORDER BY (hour, market_id);

-- 查询聚合资料
SELECT
    hour,
    market_id,
    sumMerge(total_volume) AS volume,
    countMerge(total_trades) AS trades,
    uniqMerge(unique_users) AS users
FROM market_stats_hourly
WHERE hour >= toStartOfHour(now() - INTERVAL 24 HOUR)
GROUP BY hour, market_id
ORDER BY hour DESC;
```

## 查询优化模式

### 高效过滤

```sql
-- ✅ 良好:先使用索引栏位
SELECT *
FROM markets_analytics
WHERE date >= '2025-01-01'
  AND market_id = 'market-123'
  AND volume > 1000
ORDER BY date DESC
LIMIT 100;

-- ❌ 不良:先过滤非索引栏位
SELECT *
FROM markets_analytics
WHERE volume > 1000
  AND market_name LIKE '%election%'
  AND date >= '2025-01-01';
```

### 聚合

```sql
-- ✅ 良好:使用 ClickHouse 特定聚合函式
SELECT
    toStartOfDay(created_at) AS day,
    market_id,
    sum(volume) AS total_volume,
    count() AS total_trades,
    uniq(trader_id) AS unique_traders,
    avg(trade_size) AS avg_size
FROM trades
WHERE created_at >= today() - INTERVAL 7 DAY
GROUP BY day, market_id
ORDER BY day DESC, total_volume DESC;

-- ✅ 使用 quantile 计算百分位数(比 percentile 更高效)
SELECT
    quantile(0.50)(trade_size) AS median,
    quantile(0.95)(trade_size) AS p95,
    quantile(0.99)(trade_size) AS p99
FROM trades
WHERE created_at >= now() - INTERVAL 1 HOUR;
```

### 视窗函式

```sql
-- 计算累计总和
SELECT
    date,
    market_id,
    volume,
    sum(volume) OVER (
        PARTITION BY market_id
        ORDER BY date
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ) AS cumulative_volume
FROM markets_analytics
WHERE date >= today() - INTERVAL 30 DAY
ORDER BY market_id, date;
```

## 资料插入模式

### 批量插入(推荐)

```typescript
import { ClickHouse } from 'clickhouse'

const clickhouse = new ClickHouse({
  url: process.env.CLICKHOUSE_URL,
  port: 8123,
  basicAuth: {
    username: process.env.CLICKHOUSE_USER,
    password: process.env.CLICKHOUSE_PASSWORD
  }
})

// ✅ 批量插入(高效)
async function bulkInsertTrades(trades: Trade[]) {
  const values = trades.map(trade => `(
    '${trade.id}',
    '${trade.market_id}',
    '${trade.user_id}',
    ${trade.amount},
    '${trade.timestamp.toISOString()}'
  )`).join(',')

  await clickhouse.query(`
    INSERT INTO trades (id, market_id, user_id, amount, timestamp)
    VALUES ${values}
  `).toPromise()
}

// ❌ 个别插入(慢)
async function insertTrade(trade: Trade) {
  // 不要在回圈中这样做!
  await clickhouse.query(`
    INSERT INTO trades VALUES ('${trade.id}', ...)
  `).toPromise()
}
```

### 串流插入

```typescript
// 用于持续资料摄取
import { createWriteStream } from 'fs'
import { pipeline } from 'stream/promises'

async function streamInserts() {
  const stream = clickhouse.insert('trades').stream()

  for await (const batch of dataSource) {
    stream.write(batch)
  }

  await stream.end()
}
```

## 物化视图

### 即时聚合

```sql
-- 建立每小时统计的物化视图
CREATE MATERIALIZED VIEW market_stats_hourly_mv
TO market_stats_hourly
AS SELECT
    toStartOfHour(timestamp) AS hour,
    market_id,
    sumState(amount) AS total_volume,
    countState() AS total_trades,
    uniqState(user_id) AS unique_users
FROM trades
GROUP BY hour, market_id;

-- 查询物化视图
SELECT
    hour,
    market_id,
    sumMerge(total_volume) AS volume,
    countMerge(total_trades) AS trades,
    uniqMerge(unique_users) AS users
FROM market_stats_hourly
WHERE hour >= now() - INTERVAL 24 HOUR
GROUP BY hour, market_id;
```

## 效能监控

### 查询效能

```sql
-- 检查慢查询
SELECT
    query_id,
    user,
    query,
    query_duration_ms,
    read_rows,
    read_bytes,
    memory_usage
FROM system.query_log
WHERE type = 'QueryFinish'
  AND query_duration_ms > 1000
  AND event_time >= now() - INTERVAL 1 HOUR
ORDER BY query_duration_ms DESC
LIMIT 10;
```

### 表格统计

```sql
-- 检查表格大小
SELECT
    database,
    table,
    formatReadableSize(sum(bytes)) AS size,
    sum(rows) AS rows,
    max(modification_time) AS latest_modification
FROM system.parts
WHERE active
GROUP BY database, table
ORDER BY sum(bytes) DESC;
```

## 常见分析查询

### 时间序列分析

```sql
-- 每日活跃使用者
SELECT
    toDate(timestamp) AS date,
    uniq(user_id) AS daily_active_users
FROM events
WHERE timestamp >= today() - INTERVAL 30 DAY
GROUP BY date
ORDER BY date;

-- 留存分析
SELECT
    signup_date,
    countIf(days_since_signup = 0) AS day_0,
    countIf(days_since_signup = 1) AS day_1,
    countIf(days_since_signup = 7) AS day_7,
    countIf(days_since_signup = 30) AS day_30
FROM (
    SELECT
        user_id,
        min(toDate(timestamp)) AS signup_date,
        toDate(timestamp) AS activity_date,
        dateDiff('day', signup_date, activity_date) AS days_since_signup
    FROM events
    GROUP BY user_id, activity_date
)
GROUP BY signup_date
ORDER BY signup_date DESC;
```

### 漏斗分析

```sql
-- 转换漏斗
SELECT
    countIf(step = 'viewed_market') AS viewed,
    countIf(step = 'clicked_trade') AS clicked,
    countIf(step = 'completed_trade') AS completed,
    round(clicked / viewed * 100, 2) AS view_to_click_rate,
    round(completed / clicked * 100, 2) AS click_to_completion_rate
FROM (
    SELECT
        user_id,
        session_id,
        event_type AS step
    FROM events
    WHERE event_date = today()
)
GROUP BY session_id;
```

### 世代分析

```sql
-- 按注册月份的使用者世代
SELECT
    toStartOfMonth(signup_date) AS cohort,
    toStartOfMonth(activity_date) AS month,
    dateDiff('month', cohort, month) AS months_since_signup,
    count(DISTINCT user_id) AS active_users
FROM (
    SELECT
        user_id,
        min(toDate(timestamp)) OVER (PARTITION BY user_id) AS signup_date,
        toDate(timestamp) AS activity_date
    FROM events
)
GROUP BY cohort, month, months_since_signup
ORDER BY cohort, months_since_signup;
```

## 资料管线模式

### ETL 模式

```typescript
// 提取、转换、载入
async function etlPipeline() {
  // 1. 从来源提取
  const rawData = await extractFromPostgres()

  // 2. 转换
  const transformed = rawData.map(row => ({
    date: new Date(row.created_at).toISOString().split('T')[0],
    market_id: row.market_slug,
    volume: parseFloat(row.total_volume),
    trades: parseInt(row.trade_count)
  }))

  // 3. 载入到 ClickHouse
  await bulkInsertToClickHouse(transformed)
}

// 定期执行
setInterval(etlPipeline, 60 * 60 * 1000)  // 每小时
```

### 变更资料捕获(CDC)

```typescript
// 监听 PostgreSQL 变更并同步到 ClickHouse
import { Client } from 'pg'

const pgClient = new Client({ connectionString: process.env.DATABASE_URL })

pgClient.query('LISTEN market_updates')

pgClient.on('notification', async (msg) => {
  const update = JSON.parse(msg.payload)

  await clickhouse.insert('market_updates', [
    {
      market_id: update.id,
      event_type: update.operation,  // INSERT, UPDATE, DELETE
      timestamp: new Date(),
      data: JSON.stringify(update.new_data)
    }
  ])
})
```

## 最佳实务

### 1. 分区策略
- 按时间分区(通常按月或日)
- 避免太多分区(效能影响)
- 分区键使用 DATE 类型

### 2. 排序键
- 最常过滤的栏位放在最前面
- 考虑基数(高基数优先)
- 排序影响压缩

### 3. 资料类型
- 使用最小的适当类型(UInt32 vs UInt64)
- 重复字串使用 LowCardinality
- 分类资料使用 Enum

### 4. 避免
- SELECT *(指定栏位)
- FINAL(改为在查询前合并资料)
- 太多 JOINs(为分析反正规化)
- 小量频繁插入(改用批量)

### 5. 监控
- 追踪查询效能
- 监控磁碟使用
- 检查合并操作
- 审查慢查询日志

**记住**:ClickHouse 擅长分析工作负载。为你的查询模式设计表格,批量插入,并利用物化视图进行即时聚合。

编码标准(coding-standards)


---
name: coding-standards
description: Universal coding standards, best practices, and patterns for TypeScript, JavaScript, React, and Node.js development.
---

# 程式码标准与最佳实务

适用于所有专案的通用程式码标准。

## 程式码品质原则

### 1. 可读性优先
- 程式码被阅读的次数远多于被撰写的次数
- 使用清晰的变数和函式名称
- 优先使用自文件化的程式码而非注解
- 保持一致的格式化

### 2. KISS(保持简单)
- 使用最简单的解决方案
- 避免过度工程
- 不做过早优化
- 易于理解 > 聪明的程式码

### 3. DRY(不重复自己)
- 将共用逻辑提取为函式
- 建立可重用的元件
- 在模组间共享工具函式
- 避免复制贴上程式设计

### 4. YAGNI(你不会需要它)
- 在需要之前不要建置功能
- 避免推测性的通用化
- 只在需要时增加复杂度
- 从简单开始,需要时再重构

## TypeScript/JavaScript 标准

### 变数命名

```typescript
// ✅ 良好:描述性名称
const marketSearchQuery = 'election'
const isUserAuthenticated = true
const totalRevenue = 1000

// ❌ 不良:不清楚的名称
const q = 'election'
const flag = true
const x = 1000
```

### 函式命名

```typescript
// ✅ 良好:动词-名词模式
async function fetchMarketData(marketId: string) { }
function calculateSimilarity(a: number[], b: number[]) { }
function isValidEmail(email: string): boolean { }

// ❌ 不良:不清楚或只有名词
async function market(id: string) { }
function similarity(a, b) { }
function email(e) { }
```

### 不可变性模式(关键)

```typescript
// ✅ 总是使用展开运算符
const updatedUser = {
  ...user,
  name: 'New Name'
}

const updatedArray = [...items, newItem]

// ❌ 永远不要直接修改
user.name = 'New Name'  // 不良
items.push(newItem)     // 不良
```

### 错误处理

```typescript
// ✅ 良好:完整的错误处理
async function fetchData(url: string) {
  try {
    const response = await fetch(url)

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`)
    }

    return await response.json()
  } catch (error) {
    console.error('Fetch failed:', error)
    throw new Error('Failed to fetch data')
  }
}

// ❌ 不良:无错误处理
async function fetchData(url) {
  const response = await fetch(url)
  return response.json()
}
```

### Async/Await 最佳实务

```typescript
// ✅ 良好:可能时并行执行
const [users, markets, stats] = await Promise.all([
  fetchUsers(),
  fetchMarkets(),
  fetchStats()
])

// ❌ 不良:不必要的顺序执行
const users = await fetchUsers()
const markets = await fetchMarkets()
const stats = await fetchStats()
```

### 型别安全

```typescript
// ✅ 良好:正确的型别
interface Market {
  id: string
  name: string
  status: 'active' | 'resolved' | 'closed'
  created_at: Date
}

function getMarket(id: string): Promise {
  // 实作
}

// ❌ 不良:使用 'any'
function getMarket(id: any): Promise {
  // 实作
}
```

## React 最佳实务

### 元件结构

```typescript
// ✅ 良好:具有型别的函式元件
interface ButtonProps {
  children: React.ReactNode
  onClick: () => void
  disabled?: boolean
  variant?: 'primary' | 'secondary'
}

export function Button({
  children,
  onClick,
  disabled = false,
  variant = 'primary'
}: ButtonProps) {
  return (
    

持续学习(continuous-learning)


---
name: continuous-learning
description: Automatically extract reusable patterns from Claude Code sessions and save them as learned skills for future use.
---

# 持续学习技能

自动评估 Claude Code 工作阶段结束时的内容,提取可重用模式并储存为学习技能。

## 运作方式

此技能作为 **Stop hook** 在每个工作阶段结束时执行:

1. **工作阶段评估**:检查工作阶段是否有足够讯息(预设:10+ 则)
2. **模式侦测**:从工作阶段识别可提取的模式
3. **技能提取**:将有用模式储存到 `~/.claude/skills/learned/`

## 设定

编辑 `config.json` 以自订:

```json
{
  "min_session_length": 10,
  "extraction_threshold": "medium",
  "auto_approve": false,
  "learned_skills_path": "~/.claude/skills/learned/",
  "patterns_to_detect": [
    "error_resolution",
    "user_corrections",
    "workarounds",
    "debugging_techniques",
    "project_specific"
  ],
  "ignore_patterns": [
    "simple_typos",
    "one_time_fixes",
    "external_api_issues"
  ]
}
```

## 模式类型

| 模式 | 描述 |
|------|------|
| `error_resolution` | 特定错误如何被解决 |
| `user_corrections` | 来自使用者修正的模式 |
| `workarounds` | 框架/函式库怪异问题的解决方案 |
| `debugging_techniques` | 有效的除错方法 |
| `project_specific` | 专案特定惯例 |

## Hook 设定

新增到你的 `~/.claude/settings.json`:

```json
{
  "hooks": {
    "Stop": [{
      "matcher": "*",
      "hooks": [{
        "type": "command",
        "command": "~/.claude/skills/continuous-learning/evaluate-session.sh"
      }]
    }]
  }
}
```

## 为什么用 Stop Hook?

- **轻量**:工作阶段结束时只执行一次
- **非阻塞**:不会为每则讯息增加延迟
- **完整上下文**:可存取完整工作阶段记录

## 相关

- [Longform Guide](https://x.com/affaanmustafa/status/2014040193557471352) - 持续学习章节
- `/learn` 指令 - 工作阶段中手动提取模式

---

## 比较笔记(研究:2025 年 1 月)

### vs Homunculus (github.com/humanplane/homunculus)

Homunculus v2 采用更复杂的方法:

| 功能 | 我们的方法 | Homunculus v2 |
|------|----------|---------------|
| 观察 | Stop hook(工作阶段结束) | PreToolUse/PostToolUse hooks(100% 可靠) |
| 分析 | 主要上下文 | 背景 agent(Haiku) |
| 粒度 | 完整技能 | 原子「本能」 |
| 信心 | 无 | 0.3-0.9 加权 |
| 演化 | 直接到技能 | 本能 → 聚类 → 技能/指令/agent |
| 分享 | 无 | 汇出/汇入本能 |

**来自 homunculus 的关键见解:**
> "v1 依赖技能进行观察。技能是机率性的——它们触发约 50-80% 的时间。v2 使用 hooks 进行观察(100% 可靠),并以本能作为学习行为的原子单位。"

### 潜在 v2 增强

1. **基于本能的学习** - 较小的原子行为,带信心评分
2. **背景观察者** - Haiku agent 并行分析
3. **信心衰减** - 如果被矛盾则本能失去信心
4. **领域标记** - code-style、testing、git、debugging 等
5. **演化路径** - 将相关本能聚类为技能/指令

参见:`/Users/affoon/Documents/tasks/12-continuous-learning-v2.md` 完整规格。

持续学习v2(continuous-learning-v2)


---
name: continuous-learning-v2
description: Instinct-based learning system that observes sessions via hooks, creates atomic instincts with confidence scoring, and evolves them into skills/commands/agents.
version: 2.0.0
---

# 持续学习 v2 - 基于本能的架构

进阶学习系统,透过原子「本能」(带信心评分的小型学习行为)将你的 Claude Code 工作阶段转化为可重用知识。

## v2 的新功能

| 功能 | v1 | v2 |
|------|----|----|
| 观察 | Stop hook(工作阶段结束) | PreToolUse/PostToolUse(100% 可靠) |
| 分析 | 主要上下文 | 背景 agent(Haiku) |
| 粒度 | 完整技能 | 原子「本能」 |
| 信心 | 无 | 0.3-0.9 加权 |
| 演化 | 直接到技能 | 本能 → 聚类 → 技能/指令/agent |
| 分享 | 无 | 汇出/汇入本能 |

## 本能模型

本能是一个小型学习行为:

```yaml
---
id: prefer-functional-style
trigger: "when writing new functions"
confidence: 0.7
domain: "code-style"
source: "session-observation"
---

# 偏好函式风格

## 动作
适当时使用函式模式而非类别。

## 证据
- 观察到 5 次函式模式偏好
- 使用者在 2025-01-15 将基于类别的方法修正为函式
```

**属性:**
- **原子性** — 一个触发器,一个动作
- **信心加权** — 0.3 = 试探性,0.9 = 近乎确定
- **领域标记** — code-style、testing、git、debugging、workflow 等
- **证据支持** — 追踪建立它的观察

## 运作方式

```
工作阶段活动
      │
      │ Hooks 捕获提示 + 工具使用(100% 可靠)
      ▼
┌─────────────────────────────────────────┐
│         observations.jsonl              │
│   (提示、工具呼叫、结果)               │
└─────────────────────────────────────────┘
      │
      │ Observer agent 读取(背景、Haiku)
      ▼
┌─────────────────────────────────────────┐
│          模式侦测                        │
│   • 使用者修正 → 本能                   │
│   • 错误解决 → 本能                     │
│   • 重复工作流程 → 本能                 │
└─────────────────────────────────────────┘
      │
      │ 建立/更新
      ▼
┌─────────────────────────────────────────┐
│         instincts/personal/             │
│   • prefer-functional.md (0.7)          │
│   • always-test-first.md (0.9)          │
│   • use-zod-validation.md (0.6)         │
└─────────────────────────────────────────┘
      │
      │ /evolve 聚类
      ▼
┌─────────────────────────────────────────┐
│              evolved/                   │
│   • commands/new-feature.md             │
│   • skills/testing-workflow.md          │
│   • agents/refactor-specialist.md       │
└─────────────────────────────────────────┘
```

## 快速开始

### 1. 启用观察 Hooks

新增到你的 `~/.claude/settings.json`:

```json
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "*",
      "hooks": [{
        "type": "command",
        "command": "~/.claude/skills/continuous-learning-v2/hooks/observe.sh pre"
      }]
    }],
    "PostToolUse": [{
      "matcher": "*",
      "hooks": [{
        "type": "command",
        "command": "~/.claude/skills/continuous-learning-v2/hooks/observe.sh post"
      }]
    }]
  }
}
```

### 2. 初始化目录结构

```bash
mkdir -p ~/.claude/homunculus/{instincts/{personal,inherited},evolved/{agents,skills,commands}}
touch ~/.claude/homunculus/observations.jsonl
```

### 3. 执行 Observer Agent(可选)

观察者可以在背景执行并分析观察:

```bash
# 启动背景观察者
~/.claude/skills/continuous-learning-v2/agents/start-observer.sh
```

## 指令

| 指令 | 描述 |
|------|------|
| `/instinct-status` | 显示所有学习本能及其信心 |
| `/evolve` | 将相关本能聚类为技能/指令 |
| `/instinct-export` | 汇出本能以分享 |
| `/instinct-import ` | 从他人汇入本能 |

## 设定

编辑 `config.json`:

```json
{
  "version": "2.0",
  "observation": {
    "enabled": true,
    "store_path": "~/.claude/homunculus/observations.jsonl",
    "max_file_size_mb": 10,
    "archive_after_days": 7
  },
  "instincts": {
    "personal_path": "~/.claude/homunculus/instincts/personal/",
    "inherited_path": "~/.claude/homunculus/instincts/inherited/",
    "min_confidence": 0.3,
    "auto_approve_threshold": 0.7,
    "confidence_decay_rate": 0.05
  },
  "observer": {
    "enabled": true,
    "model": "haiku",
    "run_interval_minutes": 5,
    "patterns_to_detect": [
      "user_corrections",
      "error_resolutions",
      "repeated_workflows",
      "tool_preferences"
    ]
  },
  "evolution": {
    "cluster_threshold": 3,
    "evolved_path": "~/.claude/homunculus/evolved/"
  }
}
```

## 档案结构

```
~/.claude/homunculus/
├── identity.json           # 你的个人资料、技术水平
├── observations.jsonl      # 当前工作阶段观察
├── observations.archive/   # 已处理观察
├── instincts/
│   ├── personal/           # 自动学习本能
│   └── inherited/          # 从他人汇入
└── evolved/
    ├── agents/             # 产生的专业 agents
    ├── skills/             # 产生的技能
    └── commands/           # 产生的指令
```

## 与 Skill Creator 整合

当你使用 [Skill Creator GitHub App](https://skill-creator.app) 时,它现在产生**两者**:
- 传统 SKILL.md 档案(用于向后相容)
- 本能集合(用于 v2 学习系统)

从仓库分析的本能有 `source: "repo-analysis"` 并包含来源仓库 URL。

## 信心评分

信心随时间演化:

| 分数 | 意义 | 行为 |
|------|------|------|
| 0.3 | 试探性 | 建议但不强制 |
| 0.5 | 中等 | 相关时应用 |
| 0.7 | 强烈 | 自动批准应用 |
| 0.9 | 近乎确定 | 核心行为 |

**信心增加**当:
- 重复观察到模式
- 使用者不修正建议行为
- 来自其他来源的类似本能同意

**信心减少**当:
- 使用者明确修正行为
- 长期未观察到模式
- 出现矛盾证据

## 为何 Hooks vs Skills 用于观察?

> "v1 依赖技能进行观察。技能是机率性的——它们根据 Claude 的判断触发约 50-80% 的时间。"

Hooks **100% 的时间**确定性地触发。这意味着:
- 每个工具呼叫都被观察
- 无模式被遗漏
- 学习是全面的

## 向后相容性

v2 完全相容 v1:
- 现有 `~/.claude/skills/learned/` 技能仍可运作
- Stop hook 仍执行(但现在也喂入 v2)
- 渐进迁移路径:两者并行执行

## 隐私

- 观察保持在你的机器**本机**
- 只有**本能**(模式)可被汇出
- 不会分享实际程式码或对话内容
- 你控制汇出内容

## 相关

- [Skill Creator](https://skill-creator.app) - 从仓库历史产生本能
- [Homunculus](https://github.com/humanplane/homunculus) - v2 架构灵感
- [Longform Guide](https://x.com/affaanmustafa/status/2014040193557471352) - 持续学习章节

---

*基于本能的学习:一次一个观察,教导 Claude 你的模式。*

评估框架(eval-harness)


---
name: eval-harness
description: Formal evaluation framework for Claude Code sessions implementing eval-driven development (EDD) principles
tools: Read, Write, Edit, Bash, Grep, Glob
---

# Eval Harness 技能

Claude Code 工作阶段的正式评估框架,实作 eval 驱动开发(EDD)原则。

## 理念

Eval 驱动开发将 evals 视为「AI 开发的单元测试」:
- 在实作前定义预期行为
- 开发期间持续执行 evals
- 每次变更追踪回归
- 使用 pass@k 指标进行可靠性测量

## Eval 类型

### 能力 Evals
测试 Claude 是否能做到以前做不到的事:
```markdown
[CAPABILITY EVAL: feature-name]
任务:Claude 应完成什么的描述
成功标准:
  - [ ] 标准 1
  - [ ] 标准 2
  - [ ] 标准 3
预期输出:预期结果描述
```

### 回归 Evals
确保变更不会破坏现有功能:
```markdown
[REGRESSION EVAL: feature-name]
基准:SHA 或检查点名称
测试:
  - existing-test-1: PASS/FAIL
  - existing-test-2: PASS/FAIL
  - existing-test-3: PASS/FAIL
结果:X/Y 通过(先前为 Y/Y)
```

## 评分器类型

### 1. 基于程式码的评分器
使用程式码的确定性检查:
```bash
# 检查档案是否包含预期模式
grep -q "export function handleAuth" src/auth.ts && echo "PASS" || echo "FAIL"

# 检查测试是否通过
npm test -- --testPathPattern="auth" && echo "PASS" || echo "FAIL"

# 检查建置是否成功
npm run build && echo "PASS" || echo "FAIL"
```

### 2. 基于模型的评分器
使用 Claude 评估开放式输出:
```markdown
[MODEL GRADER PROMPT]
评估以下程式码变更:
1. 它是否解决了陈述的问题?
2. 结构是否良好?
3. 边界案例是否被处理?
4. 错误处理是否适当?

分数:1-5(1=差,5=优秀)
理由:[解释]
```

### 3. 人工评分器
标记为手动审查:
```markdown
[HUMAN REVIEW REQUIRED]
变更:变更内容的描述
理由:为何需要人工审查
风险等级:LOW/MEDIUM/HIGH
```

## 指标

### pass@k
「k 次尝试中至少一次成功」
- pass@1:第一次尝试成功率
- pass@3:3 次尝试内成功
- 典型目标:pass@3 > 90%

### pass^k
「所有 k 次试验都成功」
- 更高的可靠性标准
- pass^3:连续 3 次成功
- 用于关键路径

## Eval 工作流程

### 1. 定义(编码前)
```markdown
## EVAL 定义:feature-xyz

### 能力 Evals
1. 可以建立新使用者帐户
2. 可以验证电子邮件格式
3. 可以安全地杂凑密码

### 回归 Evals
1. 现有登入仍可运作
2. 工作阶段管理未变更
3. 登出流程完整

### 成功指标
- 能力 evals 的 pass@3 > 90%
- 回归 evals 的 pass^3 = 100%
```

### 2. 实作
撰写程式码以通过定义的 evals。

### 3. 评估
```bash
# 执行能力 evals
[执行每个能力 eval,记录 PASS/FAIL]

# 执行回归 evals
npm test -- --testPathPattern="existing"

# 产生报告
```

### 4. 报告
```markdown
EVAL 报告:feature-xyz
========================

能力 Evals:
  create-user:     PASS (pass@1)
  validate-email:  PASS (pass@2)
  hash-password:   PASS (pass@1)
  整体:           3/3 通过

回归 Evals:
  login-flow:      PASS
  session-mgmt:    PASS
  logout-flow:     PASS
  整体:           3/3 通过

指标:
  pass@1: 67% (2/3)
  pass@3: 100% (3/3)

状态:准备审查
```

## 整合模式

### 实作前
```
/eval define feature-name
```
在 `.claude/evals/feature-name.md` 建立 eval 定义档案

### 实作期间
```
/eval check feature-name
```
执行当前 evals 并报告状态

### 实作后
```
/eval report feature-name
```
产生完整 eval 报告

## Eval 储存

在专案中储存 evals:
```
.claude/
  evals/
    feature-xyz.md      # Eval 定义
    feature-xyz.log     # Eval 执行历史
    baseline.json       # 回归基准
```

## 最佳实务

1. **编码前定义 evals** - 强制清楚思考成功标准
2. **频繁执行 evals** - 及早捕捉回归
3. **随时间追踪 pass@k** - 监控可靠性趋势
4. **可能时使用程式码评分器** - 确定性 > 机率性
5. **安全性需人工审查** - 永远不要完全自动化安全检查
6. **保持 evals 快速** - 慢 evals 不会被执行
7. **与程式码一起版本化 evals** - Evals 是一等工件

## 范例:新增认证

```markdown
## EVAL:add-authentication

### 阶段 1:定义(10 分钟)
能力 Evals:
- [ ] 使用者可以用电子邮件/密码注册
- [ ] 使用者可以用有效凭证登入
- [ ] 无效凭证被拒绝并显示适当错误
- [ ] 工作阶段在页面重新载入后持续
- [ ] 登出清除工作阶段

回归 Evals:
- [ ] 公开路由仍可存取
- [ ] API 回应未变更
- [ ] 资料库 schema 相容

### 阶段 2:实作(视情况而定)
[撰写程式码]

### 阶段 3:评估
执行:/eval check add-authentication

### 阶段 4:报告
EVAL 报告:add-authentication
==============================
能力:5/5 通过(pass@3:100%)
回归:3/3 通过(pass^3:100%)
状态:准备发布
```

前端模式(frontend-patterns)


---
name: frontend-patterns
description: Frontend development patterns for React, Next.js, state management, performance optimization, and UI best practices.
---

# 前端开发模式

用于 React、Next.js 和高效能使用者介面的现代前端模式。

## 元件模式

### 组合优于继承

```typescript
// ✅ 良好:元件组合
interface CardProps {
  children: React.ReactNode
  variant?: 'default' | 'outlined'
}

export function Card({ children, variant = 'default' }: CardProps) {
  return 

Golang模式(golang-patterns)


---
name: golang-patterns
description: Idiomatic Go patterns, best practices, and conventions for building robust, efficient, and maintainable Go applications.
---

# Go 开发模式

用于建构稳健、高效且可维护应用程式的惯用 Go 模式和最佳实务。

## 何时启用

- 撰写新的 Go 程式码
- 审查 Go 程式码
- 重构现有 Go 程式码
- 设计 Go 套件/模组

## 核心原则

### 1. 简单与清晰

Go 偏好简单而非聪明。程式码应该明显且易读。

```go
// 良好:清晰直接
func GetUser(id string) (*User, error) {
    user, err := db.FindUser(id)
    if err != nil {
        return nil, fmt.Errorf("get user %s: %w", id, err)
    }
    return user, nil
}

// 不良:过于聪明
func GetUser(id string) (*User, error) {
    return func() (*User, error) {
        if u, e := db.FindUser(id); e == nil {
            return u, nil
        } else {
            return nil, e
        }
    }()
}
```

### 2. 让零值有用

设计类型使其零值无需初始化即可立即使用。

```go
// 良好:零值有用
type Counter struct {
    mu    sync.Mutex
    count int // 零值为 0,可直接使用
}

func (c *Counter) Inc() {
    c.mu.Lock()
    c.count++
    c.mu.Unlock()
}

// 良好:bytes.Buffer 零值可用
var buf bytes.Buffer
buf.WriteString("hello")

// 不良:需要初始化
type BadCounter struct {
    counts map[string]int // nil map 会 panic
}
```

### 3. 接受介面,回传结构

函式应接受介面参数并回传具体类型。

```go
// 良好:接受介面,回传具体类型
func ProcessData(r io.Reader) (*Result, error) {
    data, err := io.ReadAll(r)
    if err != nil {
        return nil, err
    }
    return &Result{Data: data}, nil
}

// 不良:回传介面(不必要地隐藏实作细节)
func ProcessData(r io.Reader) (io.Reader, error) {
    // ...
}
```

## 错误处理模式

### 带上下文的错误包装

```go
// 良好:包装错误并加上上下文
func LoadConfig(path string) (*Config, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, fmt.Errorf("load config %s: %w", path, err)
    }

    var cfg Config
    if err := json.Unmarshal(data, &cfg); err != nil {
        return nil, fmt.Errorf("parse config %s: %w", path, err)
    }

    return &cfg, nil
}
```

### 自订错误类型

```go
// 定义领域特定错误
type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)
}

// 常见情况的哨兵错误
var (
    ErrNotFound     = errors.New("resource not found")
    ErrUnauthorized = errors.New("unauthorized")
    ErrInvalidInput = errors.New("invalid input")
)
```

### 使用 errors.Is 和 errors.As 检查错误

```go
func HandleError(err error) {
    // 检查特定错误
    if errors.Is(err, sql.ErrNoRows) {
        log.Println("No records found")
        return
    }

    // 检查错误类型
    var validationErr *ValidationError
    if errors.As(err, &validationErr) {
        log.Printf("Validation error on field %s: %s",
            validationErr.Field, validationErr.Message)
        return
    }

    // 未知错误
    log.Printf("Unexpected error: %v", err)
}
```

### 绝不忽略错误

```go
// 不良:用空白识别符忽略错误
result, _ := doSomething()

// 良好:处理或明确说明为何安全忽略
result, err := doSomething()
if err != nil {
    return err
}

// 可接受:当错误真的不重要时(罕见)
_ = writer.Close() // 尽力清理,错误在其他地方记录
```

## 并行模式

### Worker Pool

```go
func WorkerPool(jobs <-chan Job, results chan<- Result, numWorkers int) {
    var wg sync.WaitGroup

    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for job := range jobs {
                results <- process(job)
            }
        }()
    }

    wg.Wait()
    close(results)
}
```

### 取消和逾时的 Context

```go
func FetchWithTimeout(ctx context.Context, url string) ([]byte, error) {
    ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
    defer cancel()

    req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
    if err != nil {
        return nil, fmt.Errorf("create request: %w", err)
    }

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, fmt.Errorf("fetch %s: %w", url, err)
    }
    defer resp.Body.Close()

    return io.ReadAll(resp.Body)
}
```

### 优雅关闭

```go
func GracefulShutdown(server *http.Server) {
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

    <-quit
    log.Println("Shutting down server...")

    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    if err := server.Shutdown(ctx); err != nil {
        log.Fatalf("Server forced to shutdown: %v", err)
    }

    log.Println("Server exited")
}
```

### 协调 Goroutines 的 errgroup

```go
import "golang.org/x/sync/errgroup"

func FetchAll(ctx context.Context, urls []string) ([][]byte, error) {
    g, ctx := errgroup.WithContext(ctx)
    results := make([][]byte, len(urls))

    for i, url := range urls {
        i, url := i, url // 捕获回圈变数
        g.Go(func() error {
            data, err := FetchWithTimeout(ctx, url)
            if err != nil {
                return err
            }
            results[i] = data
            return nil
        })
    }

    if err := g.Wait(); err != nil {
        return nil, err
    }
    return results, nil
}
```

### 避免 Goroutine 泄漏

```go
// 不良:如果 context 被取消会泄漏 goroutine
func leakyFetch(ctx context.Context, url string) <-chan []byte {
    ch := make(chan []byte)
    go func() {
        data, _ := fetch(url)
        ch <- data // 如果无接收者会永远阻塞
    }()
    return ch
}

// 良好:正确处理取消
func safeFetch(ctx context.Context, url string) <-chan []byte {
    ch := make(chan []byte, 1) // 带缓冲的 channel
    go func() {
        data, err := fetch(url)
        if err != nil {
            return
        }
        select {
        case ch <- data:
        case <-ctx.Done():
        }
    }()
    return ch
}
```

## 介面设计

### 小而专注的介面

```go
// 良好:单一方法介面
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

type Closer interface {
    Close() error
}

// 依需要组合介面
type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}
```

### 在使用处定义介面

```go
// 在消费者套件中,而非提供者
package service

// UserStore 定义此服务需要的内容
type UserStore interface {
    GetUser(id string) (*User, error)
    SaveUser(user *User) error
}

type Service struct {
    store UserStore
}

// 具体实作可以在另一个套件
// 它不需要知道这个介面
```

### 使用型别断言的可选行为

```go
type Flusher interface {
    Flush() error
}

func WriteAndFlush(w io.Writer, data []byte) error {
    if _, err := w.Write(data); err != nil {
        return err
    }

    // 如果支援则 Flush
    if f, ok := w.(Flusher); ok {
        return f.Flush()
    }
    return nil
}
```

## 套件组织

### 标准专案结构

```text
myproject/
├── cmd/
│   └── myapp/
│       └── main.go           # 进入点
├── internal/
│   ├── handler/              # HTTP handlers
│   ├── service/              # 业务逻辑
│   ├── repository/           # 资料存取
│   └── config/               # 设定
├── pkg/
│   └── client/               # 公开 API 客户端
├── api/
│   └── v1/                   # API 定义(proto、OpenAPI)
├── testdata/                 # 测试 fixtures
├── go.mod
├── go.sum
└── Makefile
```

### 套件命名

```go
// 良好:简短、小写、无底线
package http
package json
package user

// 不良:冗长、混合大小写或冗余
package httpHandler
package json_parser
package userService // 冗余的 'Service' 后缀
```

### 避免套件层级状态

```go
// 不良:全域可变状态
var db *sql.DB

func init() {
    db, _ = sql.Open("postgres", os.Getenv("DATABASE_URL"))
}

// 良好:依赖注入
type Server struct {
    db *sql.DB
}

func NewServer(db *sql.DB) *Server {
    return &Server{db: db}
}
```

## 结构设计

### Functional Options 模式

```go
type Server struct {
    addr    string
    timeout time.Duration
    logger  *log.Logger
}

type Option func(*Server)

func WithTimeout(d time.Duration) Option {
    return func(s *Server) {
        s.timeout = d
    }
}

func WithLogger(l *log.Logger) Option {
    return func(s *Server) {
        s.logger = l
    }
}

func NewServer(addr string, opts ...Option) *Server {
    s := &Server{
        addr:    addr,
        timeout: 30 * time.Second, // 预设值
        logger:  log.Default(),    // 预设值
    }
    for _, opt := range opts {
        opt(s)
    }
    return s
}

// 使用方式
server := NewServer(":8080",
    WithTimeout(60*time.Second),
    WithLogger(customLogger),
)
```

### 嵌入用于组合

```go
type Logger struct {
    prefix string
}

func (l *Logger) Log(msg string) {
    fmt.Printf("[%s] %s\n", l.prefix, msg)
}

type Server struct {
    *Logger // 嵌入 - Server 获得 Log 方法
    addr    string
}

func NewServer(addr string) *Server {
    return &Server{
        Logger: &Logger{prefix: "SERVER"},
        addr:   addr,
    }
}

// 使用方式
s := NewServer(":8080")
s.Log("Starting...") // 呼叫嵌入的 Logger.Log
```

## 记忆体与效能

### 已知大小时预分配 Slice

```go
// 不良:多次扩展 slice
func processItems(items []Item) []Result {
    var results []Result
    for _, item := range items {
        results = append(results, process(item))
    }
    return results
}

// 良好:单次分配
func processItems(items []Item) []Result {
    results := make([]Result, 0, len(items))
    for _, item := range items {
        results = append(results, process(item))
    }
    return results
}
```

### 频繁分配使用 sync.Pool

```go
var bufferPool = sync.Pool{
    New: func() interface{} {
        return new(bytes.Buffer)
    },
}

func ProcessRequest(data []byte) []byte {
    buf := bufferPool.Get().(*bytes.Buffer)
    defer func() {
        buf.Reset()
        bufferPool.Put(buf)
    }()

    buf.Write(data)
    // 处理...
    return buf.Bytes()
}
```

### 避免回圈中的字串串接

```go
// 不良:产生多次字串分配
func join(parts []string) string {
    var result string
    for _, p := range parts {
        result += p + ","
    }
    return result
}

// 良好:使用 strings.Builder 单次分配
func join(parts []string) string {
    var sb strings.Builder
    for i, p := range parts {
        if i > 0 {
            sb.WriteString(",")
        }
        sb.WriteString(p)
    }
    return sb.String()
}

// 最佳:使用标准函式库
func join(parts []string) string {
    return strings.Join(parts, ",")
}
```

## Go 工具整合

### 基本指令

```bash
# 建置和执行
go build ./...
go run ./cmd/myapp

# 测试
go test ./...
go test -race ./...
go test -cover ./...

# 静态分析
go vet ./...
staticcheck ./...
golangci-lint run

# 模组管理
go mod tidy
go mod verify

# 格式化
gofmt -w .
goimports -w .
```

### 建议的 Linter 设定(.golangci.yml)

```yaml
linters:
  enable:
    - errcheck
    - gosimple
    - govet
    - ineffassign
    - staticcheck
    - unused
    - gofmt
    - goimports
    - misspell
    - unconvert
    - unparam

linters-settings:
  errcheck:
    check-type-assertions: true
  govet:
    check-shadowing: true

issues:
  exclude-use-default: false
```

## 快速参考:Go 惯用语

| 惯用语 | 描述 |
|-------|------|
| 接受介面,回传结构 | 函式接受介面参数,回传具体类型 |
| 错误是值 | 将错误视为一等值,而非例外 |
| 不要透过共享记忆体通讯 | 使用 channel 在 goroutine 间协调 |
| 让零值有用 | 类型应无需明确初始化即可工作 |
| 一点复制比一点依赖好 | 避免不必要的外部依赖 |
| 清晰优于聪明 | 优先考虑可读性而非聪明 |
| gofmt 不是任何人的最爱但是所有人的朋友 | 总是用 gofmt/goimports 格式化 |
| 提早返回 | 先处理错误,保持快乐路径不缩排 |

## 要避免的反模式

```go
// 不良:长函式中的裸返回
func process() (result int, err error) {
    // ... 50 行 ...
    return // 返回什么?
}

// 不良:使用 panic 作为控制流程
func GetUser(id string) *User {
    user, err := db.Find(id)
    if err != nil {
        panic(err) // 不要这样做
    }
    return user
}

// 不良:在结构中传递 context
type Request struct {
    ctx context.Context // Context 应该是第一个参数
    ID  string
}

// 良好:Context 作为第一个参数
func ProcessRequest(ctx context.Context, id string) error {
    // ...
}

// 不良:混合值和指标接收器
type Counter struct{ n int }
func (c Counter) Value() int { return c.n }    // 值接收器
func (c *Counter) Increment() { c.n++ }        // 指标接收器
// 选择一种风格并保持一致
```

**记住**:Go 程式码应该以最好的方式无聊 - 可预测、一致且易于理解。有疑虑时,保持简单。

Golang测试(golang-testing)


---
name: golang-testing
description: Go testing patterns including table-driven tests, subtests, benchmarks, fuzzing, and test coverage. Follows TDD methodology with idiomatic Go practices.
---

# Go 测试模式

用于撰写可靠、可维护测试的完整 Go 测试模式,遵循 TDD 方法论。

## 何时启用

- 撰写新的 Go 函式或方法
- 为现有程式码增加测试覆盖率
- 为效能关键程式码建立基准测试
- 实作输入验证的模糊测试
- 在 Go 专案中遵循 TDD 工作流程

## Go 的 TDD 工作流程

### RED-GREEN-REFACTOR 循环

```
RED     → 先写失败的测试
GREEN   → 撰写最少程式码使测试通过
REFACTOR → 在保持测试绿色的同时改善程式码
REPEAT  → 继续下一个需求
```

### Go 中的逐步 TDD

```go
// 步骤 1:定义介面/签章
// calculator.go
package calculator

func Add(a, b int) int {
    panic("not implemented") // 占位符
}

// 步骤 2:撰写失败测试(RED)
// calculator_test.go
package calculator

import "testing"

func TestAdd(t *testing.T) {
    got := Add(2, 3)
    want := 5
    if got != want {
        t.Errorf("Add(2, 3) = %d; want %d", got, want)
    }
}

// 步骤 3:执行测试 - 验证失败
// $ go test
// --- FAIL: TestAdd (0.00s)
// panic: not implemented

// 步骤 4:实作最少程式码(GREEN)
func Add(a, b int) int {
    return a + b
}

// 步骤 5:执行测试 - 验证通过
// $ go test
// PASS

// 步骤 6:如需要则重构,验证测试仍然通过
```

## 表格驱动测试

Go 测试的标准模式。以最少程式码达到完整覆盖。

```go
func TestAdd(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"positive numbers", 2, 3, 5},
        {"negative numbers", -1, -2, -3},
        {"zero values", 0, 0, 0},
        {"mixed signs", -1, 1, 0},
        {"large numbers", 1000000, 2000000, 3000000},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got := Add(tt.a, tt.b)
            if got != tt.expected {
                t.Errorf("Add(%d, %d) = %d; want %d",
                    tt.a, tt.b, got, tt.expected)
            }
        })
    }
}
```

### 带错误案例的表格驱动测试

```go
func TestParseConfig(t *testing.T) {
    tests := []struct {
        name    string
        input   string
        want    *Config
        wantErr bool
    }{
        {
            name:  "valid config",
            input: `{"host": "localhost", "port": 8080}`,
            want:  &Config{Host: "localhost", Port: 8080},
        },
        {
            name:    "invalid JSON",
            input:   `{invalid}`,
            wantErr: true,
        },
        {
            name:    "empty input",
            input:   "",
            wantErr: true,
        },
        {
            name:  "minimal config",
            input: `{}`,
            want:  &Config{}, // 零值 config
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := ParseConfig(tt.input)

            if tt.wantErr {
                if err == nil {
                    t.Error("expected error, got nil")
                }
                return
            }

            if err != nil {
                t.Fatalf("unexpected error: %v", err)
            }

            if !reflect.DeepEqual(got, tt.want) {
                t.Errorf("got %+v; want %+v", got, tt.want)
            }
        })
    }
}
```

## 子测试

### 组织相关测试

```go
func TestUser(t *testing.T) {
    // 所有子测试共享的设置
    db := setupTestDB(t)

    t.Run("Create", func(t *testing.T) {
        user := &User{Name: "Alice"}
        err := db.CreateUser(user)
        if err != nil {
            t.Fatalf("CreateUser failed: %v", err)
        }
        if user.ID == "" {
            t.Error("expected user ID to be set")
        }
    })

    t.Run("Get", func(t *testing.T) {
        user, err := db.GetUser("alice-id")
        if err != nil {
            t.Fatalf("GetUser failed: %v", err)
        }
        if user.Name != "Alice" {
            t.Errorf("got name %q; want %q", user.Name, "Alice")
        }
    })

    t.Run("Update", func(t *testing.T) {
        // ...
    })

    t.Run("Delete", func(t *testing.T) {
        // ...
    })
}
```

### 并行子测试

```go
func TestParallel(t *testing.T) {
    tests := []struct {
        name  string
        input string
    }{
        {"case1", "input1"},
        {"case2", "input2"},
        {"case3", "input3"},
    }

    for _, tt := range tests {
        tt := tt // 捕获范围变数
        t.Run(tt.name, func(t *testing.T) {
            t.Parallel() // 并行执行子测试
            result := Process(tt.input)
            // 断言...
            _ = result
        })
    }
}
```

## 测试辅助函式

### 辅助函式

```go
func setupTestDB(t *testing.T) *sql.DB {
    t.Helper() // 标记为辅助函式

    db, err := sql.Open("sqlite3", ":memory:")
    if err != nil {
        t.Fatalf("failed to open database: %v", err)
    }

    // 测试结束时清理
    t.Cleanup(func() {
        db.Close()
    })

    // 执行 migrations
    if _, err := db.Exec(schema); err != nil {
        t.Fatalf("failed to create schema: %v", err)
    }

    return db
}

func assertNoError(t *testing.T, err error) {
    t.Helper()
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
}

func assertEqual[T comparable](t *testing.T, got, want T) {
    t.Helper()
    if got != want {
        t.Errorf("got %v; want %v", got, want)
    }
}
```

### 临时档案和目录

```go
func TestFileProcessing(t *testing.T) {
    // 建立临时目录 - 自动清理
    tmpDir := t.TempDir()

    // 建立测试档案
    testFile := filepath.Join(tmpDir, "test.txt")
    err := os.WriteFile(testFile, []byte("test content"), 0644)
    if err != nil {
        t.Fatalf("failed to create test file: %v", err)
    }

    // 执行测试
    result, err := ProcessFile(testFile)
    if err != nil {
        t.Fatalf("ProcessFile failed: %v", err)
    }

    // 断言...
    _ = result
}
```

## Golden 档案

使用储存在 `testdata/` 中的预期输出档案进行测试。

```go
var update = flag.Bool("update", false, "update golden files")

func TestRender(t *testing.T) {
    tests := []struct {
        name  string
        input Template
    }{
        {"simple", Template{Name: "test"}},
        {"complex", Template{Name: "test", Items: []string{"a", "b"}}},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got := Render(tt.input)

            golden := filepath.Join("testdata", tt.name+".golden")

            if *update {
                // 更新 golden 档案:go test -update
                err := os.WriteFile(golden, got, 0644)
                if err != nil {
                    t.Fatalf("failed to update golden file: %v", err)
                }
            }

            want, err := os.ReadFile(golden)
            if err != nil {
                t.Fatalf("failed to read golden file: %v", err)
            }

            if !bytes.Equal(got, want) {
                t.Errorf("output mismatch:\ngot:\n%s\nwant:\n%s", got, want)
            }
        })
    }
}
```

## 使用介面 Mock

### 基于介面的 Mock

```go
// 定义依赖的介面
type UserRepository interface {
    GetUser(id string) (*User, error)
    SaveUser(user *User) error
}

// 生产实作
type PostgresUserRepository struct {
    db *sql.DB
}

func (r *PostgresUserRepository) GetUser(id string) (*User, error) {
    // 实际资料库查询
}

// 测试用 Mock 实作
type MockUserRepository struct {
    GetUserFunc  func(id string) (*User, error)
    SaveUserFunc func(user *User) error
}

func (m *MockUserRepository) GetUser(id string) (*User, error) {
    return m.GetUserFunc(id)
}

func (m *MockUserRepository) SaveUser(user *User) error {
    return m.SaveUserFunc(user)
}

// 使用 mock 的测试
func TestUserService(t *testing.T) {
    mock := &MockUserRepository{
        GetUserFunc: func(id string) (*User, error) {
            if id == "123" {
                return &User{ID: "123", Name: "Alice"}, nil
            }
            return nil, ErrNotFound
        },
    }

    service := NewUserService(mock)

    user, err := service.GetUserProfile("123")
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
    if user.Name != "Alice" {
        t.Errorf("got name %q; want %q", user.Name, "Alice")
    }
}
```

## 基准测试

### 基本基准测试

```go
func BenchmarkProcess(b *testing.B) {
    data := generateTestData(1000)
    b.ResetTimer() // 不计算设置时间

    for i := 0; i < b.N; i++ {
        Process(data)
    }
}

// 执行:go test -bench=BenchmarkProcess -benchmem
// 输出:BenchmarkProcess-8   10000   105234 ns/op   4096 B/op   10 allocs/op
```

### 不同大小的基准测试

```go
func BenchmarkSort(b *testing.B) {
    sizes := []int{100, 1000, 10000, 100000}

    for _, size := range sizes {
        b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) {
            data := generateRandomSlice(size)
            b.ResetTimer()

            for i := 0; i < b.N; i++ {
                // 复制以避免排序已排序的资料
                tmp := make([]int, len(data))
                copy(tmp, data)
                sort.Ints(tmp)
            }
        })
    }
}
```

### 记忆体分配基准测试

```go
func BenchmarkStringConcat(b *testing.B) {
    parts := []string{"hello", "world", "foo", "bar", "baz"}

    b.Run("plus", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            var s string
            for _, p := range parts {
                s += p
            }
            _ = s
        }
    })

    b.Run("builder", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            var sb strings.Builder
            for _, p := range parts {
                sb.WriteString(p)
            }
            _ = sb.String()
        }
    })

    b.Run("join", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            _ = strings.Join(parts, "")
        }
    })
}
```

## 模糊测试(Go 1.18+)

### 基本模糊测试

```go
func FuzzParseJSON(f *testing.F) {
    // 新增种子语料库
    f.Add(`{"name": "test"}`)
    f.Add(`{"count": 123}`)
    f.Add(`[]`)
    f.Add(`""`)

    f.Fuzz(func(t *testing.T, input string) {
        var result map[string]interface{}
        err := json.Unmarshal([]byte(input), &result)

        if err != nil {
            // 随机输入预期会有无效 JSON
            return
        }

        // 如果解析成功,重新编码应该可行
        _, err = json.Marshal(result)
        if err != nil {
            t.Errorf("Marshal failed after successful Unmarshal: %v", err)
        }
    })
}

// 执行:go test -fuzz=FuzzParseJSON -fuzztime=30s
```

### 多输入模糊测试

```go
func FuzzCompare(f *testing.F) {
    f.Add("hello", "world")
    f.Add("", "")
    f.Add("abc", "abc")

    f.Fuzz(func(t *testing.T, a, b string) {
        result := Compare(a, b)

        // 属性:Compare(a, a) 应该总是等于 0
        if a == b && result != 0 {
            t.Errorf("Compare(%q, %q) = %d; want 0", a, b, result)
        }

        // 属性:Compare(a, b) 和 Compare(b, a) 应该有相反符号
        reverse := Compare(b, a)
        if (result > 0 && reverse >= 0) || (result < 0 && reverse <= 0) {
            if result != 0 || reverse != 0 {
                t.Errorf("Compare(%q, %q) = %d, Compare(%q, %q) = %d; inconsistent",
                    a, b, result, b, a, reverse)
            }
        }
    })
}
```

## 测试覆盖率

### 执行覆盖率

```bash
# 基本覆盖率
go test -cover ./...

# 产生覆盖率 profile
go test -coverprofile=coverage.out ./...

# 在浏览器查看覆盖率
go tool cover -html=coverage.out

# 按函式查看覆盖率
go tool cover -func=coverage.out

# 含竞态侦测的覆盖率
go test -race -coverprofile=coverage.out ./...
```

### 覆盖率目标

| 程式码类型 | 目标 |
|-----------|------|
| 关键业务逻辑 | 100% |
| 公开 API | 90%+ |
| 一般程式码 | 80%+ |
| 产生的程式码 | 排除 |

## HTTP Handler 测试

```go
func TestHealthHandler(t *testing.T) {
    // 建立请求
    req := httptest.NewRequest(http.MethodGet, "/health", nil)
    w := httptest.NewRecorder()

    // 呼叫 handler
    HealthHandler(w, req)

    // 检查回应
    resp := w.Result()
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        t.Errorf("got status %d; want %d", resp.StatusCode, http.StatusOK)
    }

    body, _ := io.ReadAll(resp.Body)
    if string(body) != "OK" {
        t.Errorf("got body %q; want %q", body, "OK")
    }
}

func TestAPIHandler(t *testing.T) {
    tests := []struct {
        name       string
        method     string
        path       string
        body       string
        wantStatus int
        wantBody   string
    }{
        {
            name:       "get user",
            method:     http.MethodGet,
            path:       "/users/123",
            wantStatus: http.StatusOK,
            wantBody:   `{"id":"123","name":"Alice"}`,
        },
        {
            name:       "not found",
            method:     http.MethodGet,
            path:       "/users/999",
            wantStatus: http.StatusNotFound,
        },
        {
            name:       "create user",
            method:     http.MethodPost,
            path:       "/users",
            body:       `{"name":"Bob"}`,
            wantStatus: http.StatusCreated,
        },
    }

    handler := NewAPIHandler()

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            var body io.Reader
            if tt.body != "" {
                body = strings.NewReader(tt.body)
            }

            req := httptest.NewRequest(tt.method, tt.path, body)
            req.Header.Set("Content-Type", "application/json")
            w := httptest.NewRecorder()

            handler.ServeHTTP(w, req)

            if w.Code != tt.wantStatus {
                t.Errorf("got status %d; want %d", w.Code, tt.wantStatus)
            }

            if tt.wantBody != "" && w.Body.String() != tt.wantBody {
                t.Errorf("got body %q; want %q", w.Body.String(), tt.wantBody)
            }
        })
    }
}
```

## 测试指令

```bash
# 执行所有测试
go test ./...

# 执行详细输出的测试
go test -v ./...

# 执行特定测试
go test -run TestAdd ./...

# 执行匹配模式的测试
go test -run "TestUser/Create" ./...

# 执行带竞态侦测器的测试
go test -race ./...

# 执行带覆盖率的测试
go test -cover -coverprofile=coverage.out ./...

# 只执行短测试
go test -short ./...

# 执行带逾时的测试
go test -timeout 30s ./...

# 执行基准测试
go test -bench=. -benchmem ./...

# 执行模糊测试
go test -fuzz=FuzzParse -fuzztime=30s ./...

# 计算测试执行次数(用于侦测不稳定测试)
go test -count=10 ./...
```

## 最佳实务

**应该做的:**
- 先写测试(TDD)
- 使用表格驱动测试以获得完整覆盖
- 测试行为,而非实作
- 在辅助函式中使用 `t.Helper()`
- 对独立测试使用 `t.Parallel()`
- 用 `t.Cleanup()` 清理资源
- 使用描述情境的有意义测试名称

**不应该做的:**
- 不要直接测试私有函式(透过公开 API 测试)
- 不要在测试中使用 `time.Sleep()`(使用 channels 或条件)
- 不要忽略不稳定测试(修复或移除它们)
- 不要 mock 所有东西(可能时偏好整合测试)
- 不要跳过错误路径测试

## CI/CD 整合

```yaml
# GitHub Actions 范例
test:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-go@v5
      with:
        go-version: '1.22'

    - name: Run tests
      run: go test -race -coverprofile=coverage.out ./...

    - name: Check coverage
      run: |
        go tool cover -func=coverage.out | grep total | awk '{print $3}' | \
        awk -F'%' '{if ($1 < 80) exit 1}'
```

**记住**:测试是文件。它们展示你的程式码应该如何使用。清楚地撰写并保持更新。

迭代检索(iterative-retrieval)


---
name: iterative-retrieval
description: Pattern for progressively refining context retrieval to solve the subagent context problem
---

# 迭代检索模式

解决多 agent 工作流程中的「上下文问题」,其中子 agents 在开始工作之前不知道需要什么上下文。

## 问题

子 agents 以有限上下文产生。它们不知道:
- 哪些档案包含相关程式码
- 程式码库中存在什么模式
- 专案使用什么术语

标准方法失败:
- **传送所有内容**:超过上下文限制
- **不传送内容**:Agent 缺乏关键资讯
- **猜测需要什么**:经常错误

## 解决方案:迭代检索

一个渐进精炼上下文的 4 阶段循环:

```
┌─────────────────────────────────────────────┐
│                                             │
│   ┌──────────┐      ┌──────────┐            │
│   │ DISPATCH │─────▶│ EVALUATE │            │
│   └──────────┘      └──────────┘            │
│        ▲                  │                 │
│        │                  ▼                 │
│   ┌──────────┐      ┌──────────┐            │
│   │   LOOP   │◀─────│  REFINE  │            │
│   └──────────┘      └──────────┘            │
│                                             │
│        最多 3 个循环,然后继续               │
└─────────────────────────────────────────────┘
```

### 阶段 1:DISPATCH

初始广泛查询以收集候选档案:

```javascript
// 从高层意图开始
const initialQuery = {
  patterns: ['src/**/*.ts', 'lib/**/*.ts'],
  keywords: ['authentication', 'user', 'session'],
  excludes: ['*.test.ts', '*.spec.ts']
};

// 派遣到检索 agent
const candidates = await retrieveFiles(initialQuery);
```

### 阶段 2:EVALUATE

评估检索内容的相关性:

```javascript
function evaluateRelevance(files, task) {
  return files.map(file => ({
    path: file.path,
    relevance: scoreRelevance(file.content, task),
    reason: explainRelevance(file.content, task),
    missingContext: identifyGaps(file.content, task)
  }));
}
```

评分标准:
- **高(0.8-1.0)**:直接实作目标功能
- **中(0.5-0.7)**:包含相关模式或类型
- **低(0.2-0.4)**:间接相关
- **无(0-0.2)**:不相关,排除

### 阶段 3:REFINE

基于评估更新搜寻标准:

```javascript
function refineQuery(evaluation, previousQuery) {
  return {
    // 新增在高相关性档案中发现的新模式
    patterns: [...previousQuery.patterns, ...extractPatterns(evaluation)],

    // 新增在程式码库中找到的术语
    keywords: [...previousQuery.keywords, ...extractKeywords(evaluation)],

    // 排除确认不相关的路径
    excludes: [...previousQuery.excludes, ...evaluation
      .filter(e => e.relevance < 0.2)
      .map(e => e.path)
    ],

    // 针对特定缺口
    focusAreas: evaluation
      .flatMap(e => e.missingContext)
      .filter(unique)
  };
}
```

### 阶段 4:LOOP

以精炼标准重复(最多 3 个循环):

```javascript
async function iterativeRetrieve(task, maxCycles = 3) {
  let query = createInitialQuery(task);
  let bestContext = [];

  for (let cycle = 0; cycle < maxCycles; cycle++) {
    const candidates = await retrieveFiles(query);
    const evaluation = evaluateRelevance(candidates, task);

    // 检查是否有足够上下文
    const highRelevance = evaluation.filter(e => e.relevance >= 0.7);
    if (highRelevance.length >= 3 && !hasCriticalGaps(evaluation)) {
      return highRelevance;
    }

    // 精炼并继续
    query = refineQuery(evaluation, query);
    bestContext = mergeContext(bestContext, highRelevance);
  }

  return bestContext;
}
```

## 实际范例

### 范例 1:Bug 修复上下文

```
任务:「修复认证 token 过期 bug」

循环 1:
  DISPATCH:在 src/** 搜寻 "token"、"auth"、"expiry"
  EVALUATE:找到 auth.ts (0.9)、tokens.ts (0.8)、user.ts (0.3)
  REFINE:新增 "refresh"、"jwt" 关键字;排除 user.ts

循环 2:
  DISPATCH:搜寻精炼术语
  EVALUATE:找到 session-manager.ts (0.95)、jwt-utils.ts (0.85)
  REFINE:足够上下文(2 个高相关性档案)

结果:auth.ts、tokens.ts、session-manager.ts、jwt-utils.ts
```

### 范例 2:功能实作

```
任务:「为 API 端点增加速率限制」

循环 1:
  DISPATCH:在 routes/** 搜寻 "rate"、"limit"、"api"
  EVALUATE:无匹配 - 程式码库使用 "throttle" 术语
  REFINE:新增 "throttle"、"middleware" 关键字

循环 2:
  DISPATCH:搜寻精炼术语
  EVALUATE:找到 throttle.ts (0.9)、middleware/index.ts (0.7)
  REFINE:需要路由器模式

循环 3:
  DISPATCH:搜寻 "router"、"express" 模式
  EVALUATE:找到 router-setup.ts (0.8)
  REFINE:足够上下文

结果:throttle.ts、middleware/index.ts、router-setup.ts
```

## 与 Agents 整合

在 agent 提示中使用:

```markdown
为此任务检索上下文时:
1. 从广泛关键字搜寻开始
2. 评估每个档案的相关性(0-1 尺度)
3. 识别仍缺少的上下文
4. 精炼搜寻标准并重复(最多 3 个循环)
5. 回传相关性 >= 0.7 的档案
```

## 最佳实务

1. **从广泛开始,逐渐缩小** - 不要过度指定初始查询
2. **学习程式码库术语** - 第一个循环通常会揭示命名惯例
3. **追踪缺失内容** - 明确的缺口识别驱动精炼
4. **在「足够好」时停止** - 3 个高相关性档案胜过 10 个普通档案
5. **自信地排除** - 低相关性档案不会变得相关

## 相关

- [Longform Guide](https://x.com/affaanmustafa/status/2014040193557471352) - 子 agent 协调章节
- `continuous-learning` 技能 - 用于随时间改进的模式
- `~/.claude/agents/` 中的 Agent 定义

Postgres模式(postgres-patterns)


---
name: postgres-patterns
description: PostgreSQL database patterns for query optimization, schema design, indexing, and security. Based on Supabase best practices.
---

# PostgreSQL 模式

PostgreSQL 最佳实务快速参考。详细指南请使用 `database-reviewer` agent。

## 何时启用

- 撰写 SQL 查询或 migrations
- 设计资料库 schema
- 疑难排解慢查询
- 实作 Row Level Security
- 设定连线池

## 快速参考

### 索引速查表

| 查询模式 | 索引类型 | 范例 |
|---------|---------|------|
| `WHERE col = value` | B-tree(预设) | `CREATE INDEX idx ON t (col)` |
| `WHERE col > value` | B-tree | `CREATE INDEX idx ON t (col)` |
| `WHERE a = x AND b > y` | 复合 | `CREATE INDEX idx ON t (a, b)` |
| `WHERE jsonb @> '{}'` | GIN | `CREATE INDEX idx ON t USING gin (col)` |
| `WHERE tsv @@ query` | GIN | `CREATE INDEX idx ON t USING gin (col)` |
| 时间序列范围 | BRIN | `CREATE INDEX idx ON t USING brin (col)` |

### 资料类型快速参考

| 使用情况 | 正确类型 | 避免 |
|---------|---------|------|
| IDs | `bigint` | `int`、随机 UUID |
| 字串 | `text` | `varchar(255)` |
| 时间戳 | `timestamptz` | `timestamp` |
| 金额 | `numeric(10,2)` | `float` |
| 旗标 | `boolean` | `varchar`、`int` |

### 常见模式

**复合索引顺序:**
```sql
-- 等值栏位优先,然后是范围栏位
CREATE INDEX idx ON orders (status, created_at);
-- 适用于:WHERE status = 'pending' AND created_at > '2024-01-01'
```

**覆盖索引:**
```sql
CREATE INDEX idx ON users (email) INCLUDE (name, created_at);
-- 避免 SELECT email, name, created_at 时的表格查询
```

**部分索引:**
```sql
CREATE INDEX idx ON users (email) WHERE deleted_at IS NULL;
-- 更小的索引,只包含活跃使用者
```

**RLS 政策(优化):**
```sql
CREATE POLICY policy ON orders
  USING ((SELECT auth.uid()) = user_id);  -- 用 SELECT 包装!
```

**UPSERT:**
```sql
INSERT INTO settings (user_id, key, value)
VALUES (123, 'theme', 'dark')
ON CONFLICT (user_id, key)
DO UPDATE SET value = EXCLUDED.value;
```

**游标分页:**
```sql
SELECT * FROM products WHERE id > $last_id ORDER BY id LIMIT 20;
-- O(1) vs OFFSET 是 O(n)
```

**伫列处理:**
```sql
UPDATE jobs SET status = 'processing'
WHERE id = (
  SELECT id FROM jobs WHERE status = 'pending'
  ORDER BY created_at LIMIT 1
  FOR UPDATE SKIP LOCKED
) RETURNING *;
```

### 反模式侦测

```sql
-- 找出未建索引的外键
SELECT conrelid::regclass, a.attname
FROM pg_constraint c
JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = ANY(c.conkey)
WHERE c.contype = 'f'
  AND NOT EXISTS (
    SELECT 1 FROM pg_index i
    WHERE i.indrelid = c.conrelid AND a.attnum = ANY(i.indkey)
  );

-- 找出慢查询
SELECT query, mean_exec_time, calls
FROM pg_stat_statements
WHERE mean_exec_time > 100
ORDER BY mean_exec_time DESC;

-- 检查表格膨胀
SELECT relname, n_dead_tup, last_vacuum
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC;
```

### 设定范本

```sql
-- 连线限制(依 RAM 调整)
ALTER SYSTEM SET max_connections = 100;
ALTER SYSTEM SET work_mem = '8MB';

-- 逾时
ALTER SYSTEM SET idle_in_transaction_session_timeout = '30s';
ALTER SYSTEM SET statement_timeout = '30s';

-- 监控
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

-- 安全预设值
REVOKE ALL ON SCHEMA public FROM public;

SELECT pg_reload_conf();
```

## 相关

- Agent:`database-reviewer` - 完整资料库审查工作流程
- Skill:`clickhouse-io` - ClickHouse 分析模式
- Skill:`backend-patterns` - API 和后端模式

---

*基于 [Supabase Agent Skills](https://github.com/supabase/agent-skills)(MIT 授权)*

项目指南示例(project-guidelines-example)


# 专案指南技能(范例)

这是专案特定技能的范例。使用此作为你自己专案的范本。

基于真实生产应用程式:[Zenith](https://zenith.chat) - AI 驱动的客户探索平台。

---

## 何时使用

在处理专案特定设计时参考此技能。专案技能包含:
- 架构概览
- 档案结构
- 程式码模式
- 测试要求
- 部署工作流程

---

## 架构概览

**技术堆叠:**
- **前端**:Next.js 15(App Router)、TypeScript、React
- **后端**:FastAPI(Python)、Pydantic 模型
- **资料库**:Supabase(PostgreSQL)
- **AI**:Claude API 带工具呼叫和结构化输出
- **部署**:Google Cloud Run
- **测试**:Playwright(E2E)、pytest(后端)、React Testing Library

**服务:**
```
┌─────────────────────────────────────────────────────────────┐
│                         前端                                 │
│  Next.js 15 + TypeScript + TailwindCSS                     │
│  部署:Vercel / Cloud Run                                   │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                         后端                                 │
│  FastAPI + Python 3.11 + Pydantic                          │
│  部署:Cloud Run                                            │
└─────────────────────────────────────────────────────────────┘
                              │
              ┌───────────────┼───────────────┐
              ▼               ▼               ▼
        ┌──────────┐   ┌──────────┐   ┌──────────┐
        │ Supabase │   │  Claude  │   │  Redis   │
        │ Database │   │   API    │   │  Cache   │
        └──────────┘   └──────────┘   └──────────┘
```

---

## 档案结构

```
project/
├── frontend/
│   └── src/
│       ├── app/              # Next.js app router 页面
│       │   ├── api/          # API 路由
│       │   ├── (auth)/       # 需认证路由
│       │   └── workspace/    # 主应用程式工作区
│       ├── components/       # React 元件
│       │   ├── ui/           # 基础 UI 元件
│       │   ├── forms/        # 表单元件
│       │   └── layouts/      # 版面配置元件
│       ├── hooks/            # 自订 React hooks
│       ├── lib/              # 工具
│       ├── types/            # TypeScript 定义
│       └── config/           # 设定
│
├── backend/
│   ├── routers/              # FastAPI 路由处理器
│   ├── models.py             # Pydantic 模型
│   ├── main.py               # FastAPI app 进入点
│   ├── auth_system.py        # 认证
│   ├── database.py           # 资料库操作
│   ├── services/             # 业务逻辑
│   └── tests/                # pytest 测试
│
├── deploy/                   # 部署设定
├── docs/                     # 文件
└── scripts/                  # 工具脚本
```

---

## 程式码模式

### API 回应格式(FastAPI)

```python
from pydantic import BaseModel
from typing import Generic, TypeVar, Optional

T = TypeVar('T')

class ApiResponse(BaseModel, Generic[T]):
    success: bool
    data: Optional[T] = None
    error: Optional[str] = None

    @classmethod
    def ok(cls, data: T) -> "ApiResponse[T]":
        return cls(success=True, data=data)

    @classmethod
    def fail(cls, error: str) -> "ApiResponse[T]":
        return cls(success=False, error=error)
```

### 前端 API 呼叫(TypeScript)

```typescript
interface ApiResponse {
  success: boolean
  data?: T
  error?: string
}

async function fetchApi(
  endpoint: string,
  options?: RequestInit
): Promise

云基础设施安全(cloud-infrastructure-security)


| name | description |
|------|-------------|
| cloud-infrastructure-security | Use this skill when deploying to cloud platforms, configuring infrastructure, managing IAM policies, setting up logging/monitoring, or implementing CI/CD pipelines. Provides cloud security checklist aligned with best practices. |

# 云端与基础设施安全技能

此技能确保云端基础设施、CI/CD 管线和部署设定遵循安全最佳实务并符合业界标准。

## 何时启用

- 部署应用程式到云端平台(AWS、Vercel、Railway、Cloudflare)
- 设定 IAM 角色和权限
- 设置 CI/CD 管线
- 实作基础设施即程式码(Terraform、CloudFormation)
- 设定日志和监控
- 在云端环境管理密钥
- 设置 CDN 和边缘安全
- 实作灾难复原和备份策略

## 云端安全检查清单

### 1. IAM 与存取控制

#### 最小权限原则

```yaml
# ✅ 正确:最小权限
iam_role:
  permissions:
    - s3:GetObject  # 只有读取存取
    - s3:ListBucket
  resources:
    - arn:aws:s3:::my-bucket/*  # 只有特定 bucket

# ❌ 错误:过于广泛的权限
iam_role:
  permissions:
    - s3:*  # 所有 S3 动作
  resources:
    - "*"  # 所有资源
```

#### 多因素认证(MFA)

```bash
# 总是为 root/admin 帐户启用 MFA
aws iam enable-mfa-device \
  --user-name admin \
  --serial-number arn:aws:iam::123456789:mfa/admin \
  --authentication-code1 123456 \
  --authentication-code2 789012
```

#### 验证步骤

- [ ] 生产环境不使用 root 帐户
- [ ] 所有特权帐户启用 MFA
- [ ] 服务帐户使用角色,非长期凭证
- [ ] IAM 政策遵循最小权限
- [ ] 定期进行存取审查
- [ ] 未使用凭证已轮换或移除

### 2. 密钥管理

#### 云端密钥管理器

```typescript
// ✅ 正确:使用云端密钥管理器
import { SecretsManager } from '@aws-sdk/client-secrets-manager';

const client = new SecretsManager({ region: 'us-east-1' });
const secret = await client.getSecretValue({ SecretId: 'prod/api-key' });
const apiKey = JSON.parse(secret.SecretString).key;

// ❌ 错误:写死或只在环境变数
const apiKey = process.env.API_KEY; // 未轮换、未稽核
```

#### 密钥轮换

```bash
# 为资料库凭证设定自动轮换
aws secretsmanager rotate-secret \
  --secret-id prod/db-password \
  --rotation-lambda-arn arn:aws:lambda:region:account:function:rotate \
  --rotation-rules AutomaticallyAfterDays=30
```

#### 验证步骤

- [ ] 所有密钥储存在云端密钥管理器(AWS Secrets Manager、Vercel Secrets)
- [ ] 资料库凭证启用自动轮换
- [ ] API 金钥至少每季轮换
- [ ] 程式码、日志或错误讯息中无密钥
- [ ] 密钥存取启用稽核日志

### 3. 网路安全

#### VPC 和防火墙设定

```terraform
# ✅ 正确:限制的安全群组
resource "aws_security_group" "app" {
  name = "app-sg"

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/16"]  # 只有内部 VPC
  }

  egress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]  # 只有 HTTPS 输出
  }
}

# ❌ 错误:对网际网路开放
resource "aws_security_group" "bad" {
  ingress {
    from_port   = 0
    to_port     = 65535
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]  # 所有埠、所有 IP!
  }
}
```

#### 验证步骤

- [ ] 资料库不可公开存取
- [ ] SSH/RDP 埠限制为 VPN/堡垒机
- [ ] 安全群组遵循最小权限
- [ ] 网路 ACL 已设定
- [ ] VPC 流量日志已启用

### 4. 日志与监控

#### CloudWatch/日志设定

```typescript
// ✅ 正确:全面日志记录
import { CloudWatchLogsClient, CreateLogStreamCommand } from '@aws-sdk/client-cloudwatch-logs';

const logSecurityEvent = async (event: SecurityEvent) => {
  await cloudwatch.putLogEvents({
    logGroupName: '/aws/security/events',
    logStreamName: 'authentication',
    logEvents: [{
      timestamp: Date.now(),
      message: JSON.stringify({
        type: event.type,
        userId: event.userId,
        ip: event.ip,
        result: event.result,
        // 永远不要记录敏感资料
      })
    }]
  });
};
```

#### 验证步骤

- [ ] 所有服务启用 CloudWatch/日志记录
- [ ] 失败的认证尝试被记录
- [ ] 管理员动作被稽核
- [ ] 日志保留已设定(合规需 90+ 天)
- [ ] 可疑活动设定警报
- [ ] 日志集中化且防篡改

### 5. CI/CD 管线安全

#### 安全管线设定

```yaml
# ✅ 正确:安全的 GitHub Actions 工作流程
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read  # 最小权限

    steps:
      - uses: actions/checkout@v4

      # 扫描密钥
      - name: Secret scanning
        uses: trufflesecurity/trufflehog@main

      # 依赖稽核
      - name: Audit dependencies
        run: npm audit --audit-level=high

      # 使用 OIDC,非长期 tokens
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole
          aws-region: us-east-1
```

#### 供应链安全

```json
// package.json - 使用 lock 档案和完整性检查
{
  "scripts": {
    "install": "npm ci",  // 使用 ci 以获得可重现建置
    "audit": "npm audit --audit-level=moderate",
    "check": "npm outdated"
  }
}
```

#### 验证步骤

- [ ] 使用 OIDC 而非长期凭证
- [ ] 管线中的密钥扫描
- [ ] 依赖漏洞扫描
- [ ] 容器映像扫描(如适用)
- [ ] 强制执行分支保护规则
- [ ] 合并前需要程式码审查
- [ ] 强制执行签署 commits

### 6. Cloudflare 与 CDN 安全

#### Cloudflare 安全设定

```typescript
// ✅ 正确:带安全标头的 Cloudflare Workers
export default {
  async fetch(request: Request): Promise {
    const response = await fetch(request);

    // 新增安全标头
    const headers = new Headers(response.headers);
    headers.set('X-Frame-Options', 'DENY');
    headers.set('X-Content-Type-Options', 'nosniff');
    headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
    headers.set('Permissions-Policy', 'geolocation=(), microphone=()');

    return new Response(response.body, {
      status: response.status,
      headers
    });
  }
};
```

#### WAF 规则

```bash
# 启用 Cloudflare WAF 管理规则
# - OWASP 核心规则集
# - Cloudflare 管理规则集
# - 速率限制规则
# - Bot 保护
```

#### 验证步骤

- [ ] WAF 启用 OWASP 规则
- [ ] 速率限制已设定
- [ ] Bot 保护启用
- [ ] DDoS 保护启用
- [ ] 安全标头已设定
- [ ] SSL/TLS 严格模式启用

### 7. 备份与灾难复原

#### 自动备份

```terraform
# ✅ 正确:自动 RDS 备份
resource "aws_db_instance" "main" {
  allocated_storage     = 20
  engine               = "postgres"

  backup_retention_period = 30  # 30 天保留
  backup_window          = "03:00-04:00"
  maintenance_window     = "mon:04:00-mon:05:00"

  enabled_cloudwatch_logs_exports = ["postgresql"]

  deletion_protection = true  # 防止意外删除
}
```

#### 验证步骤

- [ ] 已设定自动每日备份
- [ ] 备份保留符合合规要求
- [ ] 已启用时间点复原
- [ ] 每季执行备份测试
- [ ] 灾难复原计划已记录
- [ ] RPO 和 RTO 已定义并测试

## 部署前云端安全检查清单

任何生产云端部署前:

- [ ] **IAM**:不使用 root 帐户、启用 MFA、最小权限政策
- [ ] **密钥**:所有密钥在云端密钥管理器并有轮换
- [ ] **网路**:安全群组受限、无公开资料库
- [ ] **日志**:CloudWatch/日志启用并有保留
- [ ] **监控**:异常设定警报
- [ ] **CI/CD**:OIDC 认证、密钥扫描、依赖稽核
- [ ] **CDN/WAF**:Cloudflare WAF 启用 OWASP 规则
- [ ] **加密**:资料静态和传输中加密
- [ ] **备份**:自动备份并测试复原
- [ ] **合规**:符合 GDPR/HIPAA 要求(如适用)
- [ ] **文件**:基础设施已记录、建立操作手册
- [ ] **事件回应**:安全事件计划就位

## 常见云端安全错误设定

### S3 Bucket 暴露

```bash
# ❌ 错误:公开 bucket
aws s3api put-bucket-acl --bucket my-bucket --acl public-read

# ✅ 正确:私有 bucket 并有特定存取
aws s3api put-bucket-acl --bucket my-bucket --acl private
aws s3api put-bucket-policy --bucket my-bucket --policy file://policy.json
```

### RDS 公开存取

```terraform
# ❌ 错误
resource "aws_db_instance" "bad" {
  publicly_accessible = true  # 绝不这样做!
}

# ✅ 正确
resource "aws_db_instance" "good" {
  publicly_accessible = false
  vpc_security_group_ids = [aws_security_group.db.id]
}
```

## 资源

- [AWS Security Best Practices](https://aws.amazon.com/security/best-practices/)
- [CIS AWS Foundations Benchmark](https://www.cisecurity.org/benchmark/amazon_web_services)
- [Cloudflare Security Documentation](https://developers.cloudflare.com/security/)
- [OWASP Cloud Security](https://owasp.org/www-project-cloud-security/)
- [Terraform Security Best Practices](https://www.terraform.io/docs/cloud/guides/recommended-practices/)

**记住**:云端错误设定是资料外泄的主要原因。单一暴露的 S3 bucket 或过于宽松的 IAM 政策可能危及你的整个基础设施。总是遵循最小权限原则和深度防御。

安全性审查技能(SKILL)


---
name: security-review
description: Use this skill when adding authentication, handling user input, working with secrets, creating API endpoints, or implementing payment/sensitive features. Provides comprehensive security checklist and patterns.
---

# 安全性审查技能

此技能确保所有程式码遵循安全性最佳实务并识别潜在漏洞。

## 何时启用

- 实作认证或授权
- 处理使用者输入或档案上传
- 建立新的 API 端点
- 处理密钥或凭证
- 实作支付功能
- 储存或传输敏感资料
- 整合第三方 API

## 安全性检查清单

### 1. 密钥管理

#### ❌ 绝不这样做
```typescript
const apiKey = "sk-proj-xxxxx"  // 写死的密钥
const dbPassword = "password123" // 在原始码中
```

#### ✅ 总是这样做
```typescript
const apiKey = process.env.OPENAI_API_KEY
const dbUrl = process.env.DATABASE_URL

// 验证密钥存在
if (!apiKey) {
  throw new Error('OPENAI_API_KEY not configured')
}
```

#### 验证步骤
- [ ] 无写死的 API 金钥、Token 或密码
- [ ] 所有密钥在环境变数中
- [ ] `.env.local` 在 .gitignore 中
- [ ] git 历史中无密钥
- [ ] 生产密钥在托管平台(Vercel、Railway)中

### 2. 输入验证

#### 总是验证使用者输入
```typescript
import { z } from 'zod'

// 定义验证 schema
const CreateUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(1).max(100),
  age: z.number().int().min(0).max(150)
})

// 处理前验证
export async function createUser(input: unknown) {
  try {
    const validated = CreateUserSchema.parse(input)
    return await db.users.create(validated)
  } catch (error) {
    if (error instanceof z.ZodError) {
      return { success: false, errors: error.errors }
    }
    throw error
  }
}
```

#### 档案上传验证
```typescript
function validateFileUpload(file: File) {
  // 大小检查(最大 5MB)
  const maxSize = 5 * 1024 * 1024
  if (file.size > maxSize) {
    throw new Error('File too large (max 5MB)')
  }

  // 类型检查
  const allowedTypes = ['image/jpeg', 'image/png', 'image/gif']
  if (!allowedTypes.includes(file.type)) {
    throw new Error('Invalid file type')
  }

  // 副档名检查
  const allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif']
  const extension = file.name.toLowerCase().match(/\.[^.]+$/)?.[0]
  if (!extension || !allowedExtensions.includes(extension)) {
    throw new Error('Invalid file extension')
  }

  return true
}
```

#### 验证步骤
- [ ] 所有使用者输入以 schema 验证
- [ ] 档案上传受限(大小、类型、副档名)
- [ ] 查询中不直接使用使用者输入
- [ ] 白名单验证(非黑名单)
- [ ] 错误讯息不泄露敏感资讯

### 3. SQL 注入预防

#### ❌ 绝不串接 SQL
```typescript
// 危险 - SQL 注入漏洞
const query = `SELECT * FROM users WHERE email = '${userEmail}'`
await db.query(query)
```

#### ✅ 总是使用参数化查询
```typescript
// 安全 - 参数化查询
const { data } = await supabase
  .from('users')
  .select('*')
  .eq('email', userEmail)

// 或使用原始 SQL
await db.query(
  'SELECT * FROM users WHERE email = $1',
  [userEmail]
)
```

#### 验证步骤
- [ ] 所有资料库查询使用参数化查询
- [ ] SQL 中无字串串接
- [ ] ORM/查询建构器正确使用
- [ ] Supabase 查询正确净化

### 4. 认证与授权

#### JWT Token 处理
```typescript
// ❌ 错误:localStorage(易受 XSS 攻击)
localStorage.setItem('token', token)

// ✅ 正确:httpOnly cookies
res.setHeader('Set-Cookie',
  `token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`)
```

#### 授权检查
```typescript
export async function deleteUser(userId: string, requesterId: string) {
  // 总是先验证授权
  const requester = await db.users.findUnique({
    where: { id: requesterId }
  })

  if (requester.role !== 'admin') {
    return NextResponse.json(
      { error: 'Unauthorized' },
      { status: 403 }
    )
  }

  // 继续删除
  await db.users.delete({ where: { id: userId } })
}
```

#### Row Level Security(Supabase)
```sql
-- 在所有表格上启用 RLS
ALTER TABLE users ENABLE ROW LEVEL SECURITY;

-- 使用者只能查看自己的资料
CREATE POLICY "Users view own data"
  ON users FOR SELECT
  USING (auth.uid() = id);

-- 使用者只能更新自己的资料
CREATE POLICY "Users update own data"
  ON users FOR UPDATE
  USING (auth.uid() = id);
```

#### 验证步骤
- [ ] Token 储存在 httpOnly cookies(非 localStorage)
- [ ] 敏感操作前有授权检查
- [ ] Supabase 已启用 Row Level Security
- [ ] 已实作基于角色的存取控制
- [ ] 工作阶段管理安全

### 5. XSS 预防

#### 净化 HTML
```typescript
import DOMPurify from 'isomorphic-dompurify'

// 总是净化使用者提供的 HTML
function renderUserContent(html: string) {
  const clean = DOMPurify.sanitize(html, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p'],
    ALLOWED_ATTR: []
  })
  return 

策略性压缩(strategic-compact)


---
name: strategic-compact
description: Suggests manual context compaction at logical intervals to preserve context through task phases rather than arbitrary auto-compaction.
---

# 策略性压缩技能

在工作流程的策略点建议手动 `/compact`,而非依赖任意的自动压缩。

## 为什么需要策略性压缩?

自动压缩在任意点触发:
- 经常在任务中途,丢失重要上下文
- 不知道逻辑任务边界
- 可能中断复杂的多步骤操作

逻辑边界的策略性压缩:
- **探索后、执行前** - 压缩研究上下文,保留实作计划
- **完成里程碑后** - 为下一阶段重新开始
- **主要上下文转换前** - 在不同任务前清除探索上下文

## 运作方式

`suggest-compact.sh` 脚本在 PreToolUse(Edit/Write)执行并:

1. **追踪工具呼叫** - 计算工作阶段中的工具呼叫次数
2. **门槛侦测** - 在可设定门槛建议(预设:50 次呼叫)
3. **定期提醒** - 门槛后每 25 次呼叫提醒一次

## Hook 设定

新增到你的 `~/.claude/settings.json`:

```json
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "tool == \"Edit\" || tool == \"Write\"",
      "hooks": [{
        "type": "command",
        "command": "~/.claude/skills/strategic-compact/suggest-compact.sh"
      }]
    }]
  }
}
```

## 设定

环境变数:
- `COMPACT_THRESHOLD` - 第一次建议前的工具呼叫次数(预设:50)

## 最佳实务

1. **规划后压缩** - 计划确定后,压缩以重新开始
2. **除错后压缩** - 继续前清除错误解决上下文
3. **不要在实作中途压缩** - 为相关变更保留上下文
4. **阅读建议** - Hook 告诉你*何时*,你决定*是否*

## 相关

- [Longform Guide](https://x.com/affaanmustafa/status/2014040193557471352) - Token 优化章节
- 记忆持久性 hooks - 用于压缩后存活的状态

TDD工作流(tdd-workflow)


---
name: tdd-workflow
description: Use this skill when writing new features, fixing bugs, or refactoring code. Enforces test-driven development with 80%+ coverage including unit, integration, and E2E tests.
---

# 测试驱动开发工作流程

此技能确保所有程式码开发遵循 TDD 原则,并具有完整的测试覆盖率。

## 何时启用

- 撰写新功能或功能性程式码
- 修复 Bug 或问题
- 重构现有程式码
- 新增 API 端点
- 建立新元件

## 核心原则

### 1. 测试先于程式码
总是先写测试,然后实作程式码使测试通过。

### 2. 覆盖率要求
- 最低 80% 覆盖率(单元 + 整合 + E2E)
- 涵盖所有边界案例
- 测试错误情境
- 验证边界条件

### 3. 测试类型

#### 单元测试
- 个别函式和工具
- 元件逻辑
- 纯函式
- 辅助函式和工具

#### 整合测试
- API 端点
- 资料库操作
- 服务互动
- 外部 API 呼叫

#### E2E 测试(Playwright)
- 关键使用者流程
- 完整工作流程
- 浏览器自动化
- UI 互动

## TDD 工作流程步骤

### 步骤 1:撰写使用者旅程
```
身为 [角色],我想要 [动作],以便 [好处]

范例:
身为使用者,我想要语意搜寻市场,
以便即使没有精确关键字也能找到相关市场。
```

### 步骤 2:产生测试案例
为每个使用者旅程建立完整的测试案例:

```typescript
describe('Semantic Search', () => {
  it('returns relevant markets for query', async () => {
    // 测试实作
  })

  it('handles empty query gracefully', async () => {
    // 测试边界案例
  })

  it('falls back to substring search when Redis unavailable', async () => {
    // 测试回退行为
  })

  it('sorts results by similarity score', async () => {
    // 测试排序逻辑
  })
})
```

### 步骤 3:执行测试(应该失败)
```bash
npm test
# 测试应该失败 - 我们还没实作
```

### 步骤 4:实作程式码
撰写最少的程式码使测试通过:

```typescript
// 由测试引导的实作
export async function searchMarkets(query: string) {
  // 实作在此
}
```

### 步骤 5:再次执行测试
```bash
npm test
# 测试现在应该通过
```

### 步骤 6:重构
在保持测试通过的同时改善程式码品质:
- 移除重复
- 改善命名
- 优化效能
- 增强可读性

### 步骤 7:验证覆盖率
```bash
npm run test:coverage
# 验证达到 80%+ 覆盖率
```

## 测试模式

### 单元测试模式(Jest/Vitest)
```typescript
import { render, screen, fireEvent } from '@testing-library/react'
import { Button } from './Button'

describe('Button Component', () => {
  it('renders with correct text', () => {
    render(Click me)
    expect(screen.getByText('Click me')).toBeInTheDocument()
  })

  it('calls onClick when clicked', () => {
    const handleClick = jest.fn()
    render(Click)

    fireEvent.click(screen.getByRole('button'))

    expect(handleClick).toHaveBeenCalledTimes(1)
  })

  it('is disabled when disabled prop is true', () => {
    render(Click)
    expect(screen.getByRole('button')).toBeDisabled()
  })
})
```

### API 整合测试模式
```typescript
import { NextRequest } from 'next/server'
import { GET } from './route'

describe('GET /api/markets', () => {
  it('returns markets successfully', async () => {
    const request = new NextRequest('http://localhost/api/markets')
    const response = await GET(request)
    const data = await response.json()

    expect(response.status).toBe(200)
    expect(data.success).toBe(true)
    expect(Array.isArray(data.data)).toBe(true)
  })

  it('validates query parameters', async () => {
    const request = new NextRequest('http://localhost/api/markets?limit=invalid')
    const response = await GET(request)

    expect(response.status).toBe(400)
  })

  it('handles database errors gracefully', async () => {
    // Mock 资料库失败
    const request = new NextRequest('http://localhost/api/markets')
    // 测试错误处理
  })
})
```

### E2E 测试模式(Playwright)
```typescript
import { test, expect } from '@playwright/test'

test('user can search and filter markets', async ({ page }) => {
  // 导航到市场页面
  await page.goto('/')
  await page.click('a[href="/markets"]')

  // 验证页面载入
  await expect(page.locator('h1')).toContainText('Markets')

  // 搜寻市场
  await page.fill('input[placeholder="Search markets"]', 'election')

  // 等待 debounce 和结果
  await page.waitForTimeout(600)

  // 验证搜寻结果显示
  const results = page.locator('[data-testid="market-card"]')
  await expect(results).toHaveCount(5, { timeout: 5000 })

  // 验证结果包含搜寻词
  const firstResult = results.first()
  await expect(firstResult).toContainText('election', { ignoreCase: true })

  // 依状态筛选
  await page.click('button:has-text("Active")')

  // 验证筛选结果
  await expect(results).toHaveCount(3)
})

test('user can create a new market', async ({ page }) => {
  // 先登入
  await page.goto('/creator-dashboard')

  // 填写市场建立表单
  await page.fill('input[name="name"]', 'Test Market')
  await page.fill('textarea[name="description"]', 'Test description')
  await page.fill('input[name="endDate"]', '2025-12-31')

  // 提交表单
  await page.click('button[type="submit"]')

  // 验证成功讯息
  await expect(page.locator('text=Market created successfully')).toBeVisible()

  // 验证重导向到市场页面
  await expect(page).toHaveURL(/\/markets\/test-market/)
})
```

## 测试档案组织

```
src/
├── components/
│   ├── Button/
│   │   ├── Button.tsx
│   │   ├── Button.test.tsx          # 单元测试
│   │   └── Button.stories.tsx       # Storybook
│   └── MarketCard/
│       ├── MarketCard.tsx
│       └── MarketCard.test.tsx
├── app/
│   └── api/
│       └── markets/
│           ├── route.ts
│           └── route.test.ts         # 整合测试
└── e2e/
    ├── markets.spec.ts               # E2E 测试
    ├── trading.spec.ts
    └── auth.spec.ts
```

## Mock 外部服务

### Supabase Mock
```typescript
jest.mock('@/lib/supabase', () => ({
  supabase: {
    from: jest.fn(() => ({
      select: jest.fn(() => ({
        eq: jest.fn(() => Promise.resolve({
          data: [{ id: 1, name: 'Test Market' }],
          error: null
        }))
      }))
    }))
  }
}))
```

### Redis Mock
```typescript
jest.mock('@/lib/redis', () => ({
  searchMarketsByVector: jest.fn(() => Promise.resolve([
    { slug: 'test-market', similarity_score: 0.95 }
  ])),
  checkRedisHealth: jest.fn(() => Promise.resolve({ connected: true }))
}))
```

### OpenAI Mock
```typescript
jest.mock('@/lib/openai', () => ({
  generateEmbedding: jest.fn(() => Promise.resolve(
    new Array(1536).fill(0.1) // Mock 1536 维嵌入向量
  ))
}))
```

## 测试覆盖率验证

### 执行覆盖率报告
```bash
npm run test:coverage
```

### 覆盖率门槛
```json
{
  "jest": {
    "coverageThresholds": {
      "global": {
        "branches": 80,
        "functions": 80,
        "lines": 80,
        "statements": 80
      }
    }
  }
}
```

## 常见测试错误避免

### ❌ 错误:测试实作细节
```typescript
// 不要测试内部状态
expect(component.state.count).toBe(5)
```

### ✅ 正确:测试使用者可见行为
```typescript
// 测试使用者看到的内容
expect(screen.getByText('Count: 5')).toBeInTheDocument()
```

### ❌ 错误:脆弱的选择器
```typescript
// 容易坏掉
await page.click('.css-class-xyz')
```

### ✅ 正确:语意选择器
```typescript
// 对变更有弹性
await page.click('button:has-text("Submit")')
await page.click('[data-testid="submit-button"]')
```

### ❌ 错误:无测试隔离
```typescript
// 测试互相依赖
test('creates user', () => { /* ... */ })
test('updates same user', () => { /* 依赖前一个测试 */ })
```

### ✅ 正确:独立测试
```typescript
// 每个测试设置自己的资料
test('creates user', () => {
  const user = createTestUser()
  // 测试逻辑
})

test('updates user', () => {
  const user = createTestUser()
  // 更新逻辑
})
```

## 持续测试

### 开发期间的 Watch 模式
```bash
npm test -- --watch
# 档案变更时自动执行测试
```

### Pre-Commit Hook
```bash
# 每次 commit 前执行
npm test && npm run lint
```

### CI/CD 整合
```yaml
# GitHub Actions
- name: Run Tests
  run: npm test -- --coverage
- name: Upload Coverage
  uses: codecov/codecov-action@v3
```

## 最佳实务

1. **先写测试** - 总是 TDD
2. **一个测试一个断言** - 专注单一行为
3. **描述性测试名称** - 解释测试内容
4. **Arrange-Act-Assert** - 清晰的测试结构
5. **Mock 外部依赖** - 隔离单元测试
6. **测试边界案例** - Null、undefined、空值、大值
7. **测试错误路径** - 不只是快乐路径
8. **保持测试快速** - 单元测试每个 < 50ms
9. **测试后清理** - 无副作用
10. **检视覆盖率报告** - 识别缺口

## 成功指标

- 达到 80%+ 程式码覆盖率
- 所有测试通过(绿色)
- 无跳过或停用的测试
- 快速测试执行(单元测试 < 30s)
- E2E 测试涵盖关键使用者流程
- 测试在生产前捕捉 Bug

---

**记住**:测试不是可选的。它们是实现自信重构、快速开发和生产可靠性的安全网。

验证循环(verification-loop)


# 验证循环技能

Claude Code 工作阶段的完整验证系统。

## 何时使用

在以下情况呼叫此技能:
- 完成功能或重大程式码变更后
- 建立 PR 前
- 想确保品质门槛通过时
- 重构后

## 验证阶段

### 阶段 1:建置验证
```bash
# 检查专案是否建置
npm run build 2>&1 | tail -20
# 或
pnpm build 2>&1 | tail -20
```

如果建置失败,停止并在继续前修复。

### 阶段 2:型别检查
```bash
# TypeScript 专案
npx tsc --noEmit 2>&1 | head -30

# Python 专案
pyright . 2>&1 | head -30
```

报告所有型别错误。继续前修复关键错误。

### 阶段 3:Lint 检查
```bash
# JavaScript/TypeScript
npm run lint 2>&1 | head -30

# Python
ruff check . 2>&1 | head -30
```

### 阶段 4:测试套件
```bash
# 执行带覆盖率的测试
npm run test -- --coverage 2>&1 | tail -50

# 检查覆盖率门槛
# 目标:最低 80%
```

报告:
- 总测试数:X
- 通过:X
- 失败:X
- 覆盖率:X%

### 阶段 5:安全扫描
```bash
# 检查密钥
grep -rn "sk-" --include="*.ts" --include="*.js" . 2>/dev/null | head -10
grep -rn "api_key" --include="*.ts" --include="*.js" . 2>/dev/null | head -10

# 检查 console.log
grep -rn "console.log" --include="*.ts" --include="*.tsx" src/ 2>/dev/null | head -10
```

### 阶段 6:差异审查
```bash
# 显示变更内容
git diff --stat
git diff HEAD~1 --name-only
```

审查每个变更的档案:
- 非预期变更
- 缺少错误处理
- 潜在边界案例

## 输出格式

执行所有阶段后,产生验证报告:

```
验证报告
==================

建置:     [PASS/FAIL]
型别:     [PASS/FAIL](X 个错误)
Lint:     [PASS/FAIL](X 个警告)
测试:     [PASS/FAIL](X/Y 通过,Z% 覆盖率)
安全性:   [PASS/FAIL](X 个问题)
差异:     [X 个档案变更]

整体:     [READY/NOT READY] for PR

待修复问题:
1. ...
2. ...
```

## 持续模式

对于长时间工作阶段,每 15 分钟或重大变更后执行验证:

```markdown
设定心理检查点:
- 完成每个函式后
- 完成元件后
- 移至下一个任务前

执行:/verify
```

## 与 Hooks 整合

此技能补充 PostToolUse hooks 但提供更深入的验证。
Hooks 立即捕捉问题;此技能提供全面审查。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐