Code Review Skill Transform code reviews from gatekeeping to knowledge sharing through constructive feedback, systematic analysis, and collaborative improvement. When to Use This Skill - Reviewing pull requests and code changes - Establishing code review standards for teams - Mentoring junior developers through reviews - Conducting architecture reviews - Creating review checklists and guidelines - Improving team collaboration - Reducing code review cycle time - Maintaining code quality standards Core Principles 1. The Review Mindset Goals of Code Review: - Catch bugs and edge cases - Ensure c…

: '\u003crootDir>/src/$1',\n },\n};\nexport default config;\n```\n\n### 类型测试(tsd / expect-type)\n\n```typescript\n// ✅ 使用 expect-type 验证类型推断\nimport { expectTypeOf } from 'vitest';\n\nfunction getFirst\u003cT>(arr: T[]): T | undefined {\n return arr[0];\n}\n\nit('should infer correct return type', () => {\n const result = getFirst([1, 2, 3]);\n expectTypeOf(result).toEqualTypeOf\u003cnumber | undefined>();\n});\n\n// ✅ 使用 expect-type 验证函数签名\nconst fn = (a: string, b: number) => a.repeat(b);\nexpectTypeOf(fn).parameters.toEqualTypeOf\u003c[string, number]>();\nexpectTypeOf(fn).returns.toBeString();\n\n// ❌ 类型错误会在编译时被捕获\nconst result = getFirst(['a', 'b']);\n// @ts-expect-error: 类型不匹配\nexpectTypeOf(result).toEqualTypeOf\u003cnumber>();\n```\n\n### Snapshot 测试最佳实践\n\n```typescript\n// ✅ Snapshot 适合:稳定的输出结构、配置对象、错误消息\nit('should match serialized config', () => {\n const config = createAppConfig();\n expect(config).toMatchSnapshot();\n});\n\n// ❌ 避免:大对象、动态数据、随机值\nit('should not snapshot large payloads', () => {\n const hugePayload = { users: generateRandomUsers(1000) };\n // 太长的 snapshot 难以审查,变更时不知道意图\n});\n\n// ✅ 使用 inline snapshot 处理小片段\nit('should generate correct error message', () => {\n expect(formatError('INVALID_INPUT')).toMatchInlineSnapshot(\n `\"Error: Invalid input provided\"`\n );\n});\n\n// ✅ 使用 snapshot 属性匹配器处理动态值\nit('should match user with generated id', () => {\n expect(createUser('Alice')).toMatchSnapshot({\n id: expect.any(String),\n createdAt: expect.any(Date),\n });\n});\n```\n\n### Mock 策略\n\n```typescript\n// ✅ Vitest: vi.mock 自动 hoist\nimport { vi, describe, it, expect } from 'vitest';\n\nvi.mock('./api', () => ({\n fetchUser: vi.fn().mockResolvedValue({ id: 1, name: 'Alice' }),\n}));\n\nit('should display user', async () => {\n const { fetchUser } = await import('./api');\n const user = await fetchUser('1');\n expect(user.name).toBe('Alice');\n});\n\n// ✅ Jest: jest.mock 同样自动 hoist\njest.mock('./database', () => ({\n query: jest.fn().mockResolvedValue([{ id: 1 }]),\n}));\n\n// ❌ 避免部分 Mock——测试的是 Mock 而非真实行为\njest.mock('./utils', () => ({\n ...jest.requireActual('./utils'),\n calculateTotal: jest.fn(), // 其他函数是真实的,这个是假的\n}));\n```\n\n### 测试辅助工具\n\n```typescript\n// ✅ 使用 testing-library 进行 DOM 测试\nimport { render, screen } from '@testing-library/react';\nimport userEvent from '@testing-library/user-event';\n\nit('should submit form', async () => {\n render(\u003cLoginForm />);\n await userEvent.type(screen.getByLabelText('Email'), '[email protected]');\n await userEvent.click(screen.getByRole('button', { name: 'Submit' }));\n expect(screen.getByText('Welcome, Alice!')).toBeInTheDocument();\n});\n\n// ✅ 使用 MSW 进行 API mock(Mock Service Worker)\nimport { http, HttpResponse } from 'msw';\nimport { setupServer } from 'msw/node';\n\nconst server = setupServer(\n http.get('/api/users/:id', ({ params }) => {\n return HttpResponse.json({ id: params.id, name: 'Alice' });\n })\n);\n\nbeforeAll(() => server.listen());\nafterEach(() => server.resetHandlers());\nafterAll(() => server.close());\n```\n\n---\n\n## 模块解析\n\n### ESM vs CJS 差异和陷阱\n\n```typescript\n// ❌ CJS 风格在 ESM 中不可用\n// package.json: \"type\": \"module\"\nconst fs = require('fs'); // Error: require is not defined\nmodule.exports = { foo: 'bar' }; // Error: module is not defined\n\n// ✅ ESM 正确写法\nimport fs from 'node:fs';\nexport const foo = 'bar';\n\n// ✅ 在 ESM 中获取 __dirname\nimport { fileURLToPath } from 'node:url';\nimport { dirname } from 'node:path';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n// ❌ ESM 中动态 require\nconst moduleName = 'lodash';\nconst _ = require(moduleName); // Error!\n\n// ✅ ESM 动态 import\nconst _ = await import(moduleName);\n```\n\n### tsconfig paths 与 path aliases\n\n```json\n// tsconfig.json\n{\n \"compilerOptions\": {\n \"baseUrl\": \".\",\n \"paths\": {\n \"@/*\": [\"./src/*\"],\n \"@components/*\": [\"./src/components/*\"],\n \"@utils/*\": [\"./src/utils/*\"]\n }\n }\n}\n```\n\n```typescript\n// ✅ 使用别名前\nimport { Button } from '../../components/ui/Button';\nimport { formatDate } from '../../../utils/date';\n\n// ✅ 使用别名后——清晰且不易因文件移动而断裂\nimport { Button } from '@components/ui/Button';\nimport { formatDate } from '@utils/date';\n```\n\n```typescript\n// ⚠️ tsconfig paths 只影响 TS 编译,不影响运行时\n// 需要配合打包工具(Vite、webpack)或 tsx 的别名解析\n\n// vite.config.ts\nimport { resolve } from 'node:path';\n\nexport default defineConfig({\n resolve: {\n alias: {\n '@': resolve(__dirname, 'src'),\n },\n },\n});\n\n// ⚠️ 发布 npm 包时,tsconfig paths 不会自动解析\n// 需要 tsc-alias 或 tsconfig-paths 处理\n```\n\n### package.json exports field\n\n```json\n// package.json\n{\n \"name\": \"my-library\",\n \"exports\": {\n \".\": {\n \"import\": \"./dist/index.mjs\",\n \"require\": \"./dist/index.cjs\",\n \"types\": \"./dist/index.d.ts\"\n },\n \"./utils\": {\n \"import\": \"./dist/utils.mjs\",\n \"require\": \"./dist/utils.cjs\",\n \"types\": \"./dist/utils.d.ts\"\n },\n \"./*\": \"./dist/*\"\n }\n}\n```\n\n```typescript\n// ✅ 消费者使用\nimport { foo } from 'my-library'; // 解析到 \".\" 条件\nimport { bar } from 'my-library/utils'; // 解析到 \"./utils\" 条件\n\n// ❌ 没有 exports 映射的路径无法访问\nimport { secret } from 'my-library/internal'; // Error!\n```\n\n### 动态 import() 和代码分割\n\n```typescript\n// ✅ 条件加载模块\nasync function loadChartLibrary() {\n if (typeof window === 'undefined') return null; // SSR 跳过\n const { Chart } = await import('chart.js');\n return Chart;\n}\n\n// ✅ React 懒加载组件\nconst AdminPanel = lazy(() => import('./AdminPanel'));\n// 配合 Suspense 使用\n\u003cSuspense fallback={\u003cLoading />}>\n \u003cAdminPanel />\n\u003c/Suspense>\n\n// ✅ 带错误处理\nconst AdminPanel = lazy(() =>\n import('./AdminPanel').catch(() => ({\n default: () => \u003cErrorFallback />,\n }))\n);\n```\n\n---\n\n## TS 4.9+ / 5.x 新特性\n\n### satisfies 关键字(TS 4.9+)\n\n```typescript\n// ❌ 没有 satisfies:类型太宽泛\nconst palette = {\n red: '#ff0000',\n green: '#00ff00',\n blue: '#0000ff',\n};\n// palette.red 类型是 string,丢失了 '#ff0000' 的精确值\n\n// ✅ satisfies 保留字面量类型,同时验证结构\nconst palette = {\n red: '#ff0000',\n green: '#00ff00',\n blue: '#0000ff',\n} satisfies Record\u003cstring, `#${string}`>;\n\n// palette.red 类型是 '#ff0000'(不是 string)\n// 但添加新属性时仍会验证格式\n```\n\n```typescript\n// ✅ satisfies 用于验证对象符合接口\ninterface UserConfig {\n theme: 'light' | 'dark';\n locale: string;\n}\n\nconst config = {\n theme: 'dark',\n locale: 'en-US',\n} satisfies UserConfig;\n// config.theme 类型是 'dark'(不是 'light' | 'dark')\n// 所有属性都通过 satisfies 类型检查\n```\n\n### const 类型参数(TS 5.0+)\n\n```typescript\n// ❌ 之前:需要 as const 断言\nfunction getRoutes\u003cT extends readonly string[]>(routes: T) {\n return routes;\n}\nconst routes = getRoutes(['home', 'about'] as const);\n\n// ✅ TS 5.0+:const 类型参数\nfunction getRoutes\u003cconst T extends readonly string[]>(routes: T) {\n return routes;\n}\nconst routes = getRoutes(['home', 'about']);\n// routes 类型是 readonly ['home', 'about']\n```\n\n```typescript\n// ✅ 真实场景:类型安全的配置对象\ndeclare function createConfig\u003cconst T extends Record\u003cstring, unknown>>(\n config: T\n): T;\n\nconst config = createConfig({\n api: { url: 'https://api.example.com', version: 2 },\n features: { newDashboard: true },\n});\n// config.api.url 类型是 'https://api.example.com'(字面量)\n```\n\n### 装饰器(Stage 3 Decorators, TS 5.0+)\n\n```typescript\n// ✅ Stage 3 装饰器(TS 5.0+,experimentalDecorators 不再需要)\nfunction logged\u003cThis, Args extends unknown[], Return>(\n target: (this: This, ...args: Args) => Return,\n context: ClassMethodDecoratorContext\n) {\n return function (this: This, ...args: Args): Return {\n console.log(`Calling ${String(context.name)} with`, args);\n return target.apply(this, args);\n };\n}\n\nclass Calculator {\n @logged\n add(a: number, b: number): number {\n return a + b;\n }\n}\n\n// 输出: Calling add with [1, 2]\nnew Calculator().add(1, 2);\n```\n\n```typescript\n// ⚠️ Stage 3 装饰器与旧版 experimentalDecorators 不同\n// 旧版:tsconfig 中需要 \"experimentalDecorators\": true\n// 新版(TS 5.0+):默认支持,无需额外配置\n\n// ❌ 旧版装饰器签名(仍支持但标记为 legacy)\nfunction deprecated\u003cT extends { new (...args: any[]): {} }>(constructor: T) {\n return class extends constructor { /* ... */ };\n}\n\n// ✅ 新版装饰器按类型区分 context\nfunction sealed\u003cT extends { new (...args: any[]): {} }>(\n target: T,\n context: ClassDecoratorContext\n) {\n // context.kind === 'class'\n}\n```\n\n### using 声明(显式资源管理,TS 5.2+)\n\n```typescript\n// ✅ 使用 Symbol.dispose 实现自动清理\nclass TempFile implements Disposable {\n private path: string;\n\n constructor() {\n this.path = `/tmp/file-${Date.now()}`;\n }\n\n write(data: string) { /* ... */ }\n\n [Symbol.dispose]() {\n // 自动清理——无论函数如何退出(正常/异常)\n fs.unlinkSync(this.path);\n console.log(`Cleaned up: ${this.path}`);\n }\n}\n\nfunction processFile() {\n using file = new TempFile(); // using 声明\n file.write('data');\n // 作用域结束时自动调用 file[Symbol.dispose]()\n}\n```\n\n```typescript\n// ✅ AsyncDisposable 用于异步资源(TS 5.2+)\nclass DatabaseConnection implements AsyncDisposable {\n private db: sqlite3.Database;\n\n async connect() {\n this.db = new sqlite3.Database(':memory:');\n }\n\n async [Symbol.asyncDispose]() {\n await this.db.close();\n }\n}\n\nasync function query() {\n await using conn = new DatabaseConnection(); // await using\n await conn.connect();\n // 作用域结束时自动 await conn[Symbol.asyncDispose]()\n}\n```\n\n### 枚举改进(TS 5.0+)\n\n```typescript\n// ✅ 所有枚举现在都是 union 枚举(TS 5.0+)\nenum Color {\n Red = 'RED',\n Green = 'GREEN',\n}\n\n// 之前:Color 作为类型时行为不一致\n// 现在:Color 完全作为字符串字面量联合类型\nconst color: Color = Color.Red; // TypeScript 现在对 Color 类型有更好的推断\n```\n\n## Review Checklist\n\n### 类型系统\n- [ ] 没有使用 `any`(使用 `unknown` + 类型守卫代替)\n- [ ] 接口和类型定义完整且有意义的命名\n- [ ] 使用泛型提高代码复用性\n- [ ] 联合类型有正确的类型收窄\n- [ ] 善用工具类型(Partial、Pick、Omit 等)\n\n### 泛型\n- [ ] 泛型有适当的约束(extends)\n- [ ] 泛型参数有合理的默认值\n- [ ] 避免过度泛型化(KISS 原则)\n\n### Strict 模式\n- [ ] tsconfig.json 启用了 strict: true\n- [ ] 启用了 noUncheckedIndexedAccess\n- [ ] 没有使用 @ts-ignore(改用 @ts-expect-error)\n\n### 异步代码\n- [ ] async 函数有错误处理\n- [ ] Promise rejection 被正确处理\n- [ ] 没有 floating promises(未处理的 Promise)\n- [ ] 并发请求使用 Promise.all 或 Promise.allSettled\n- [ ] 竞态条件使用 AbortController 处理\n\n### 不可变性\n- [ ] 不直接修改函数参数\n- [ ] 使用 spread 操作符创建新对象/数组\n- [ ] 考虑使用 readonly 修饰符\n\n### ESLint\n- [ ] 使用 @typescript-eslint/recommended\n- [ ] 没有 ESLint 警告或错误\n- [ ] 使用 consistent-type-imports\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":24572,"content_sha256":"fca6e0e384c5542123a2781adc833599129e24d9881aa9ec7c802a42d8e13bf4"},{"filename":"reference/vue.md","content":"# Vue 3 Code Review Guide\n\n> Vue 3 Composition API 代码审查指南,覆盖响应性系统、Props/Emits、Watchers、Composables、Vue 3.5 新特性等核心主题。\n\n## 目录\n\n- [响应性系统](#响应性系统)\n- [Props & Emits](#props--emits)\n- [Vue 3.5 新特性](#vue-35-新特性)\n- [Watchers](#watchers)\n- [模板最佳实践](#模板最佳实践)\n- [Composables](#composables)\n- [性能优化](#性能优化)\n- [Review Checklist](#review-checklist)\n\n---\n\n## 响应性系统\n\n### ref vs reactive 选择\n\n```vue\n\u003c!-- ✅ 基本类型用 ref -->\n\u003cscript setup lang=\"ts\">\nconst count = ref(0)\nconst name = ref('Vue')\n\n// ref 需要 .value 访问\ncount.value++\n\u003c/script>\n\n\u003c!-- ✅ 对象/数组用 reactive(可选)-->\n\u003cscript setup lang=\"ts\">\nconst state = reactive({\n user: null,\n loading: false,\n error: null\n})\n\n// reactive 直接访问\nstate.loading = true\n\u003c/script>\n\n\u003c!-- 💡 现代最佳实践:全部使用 ref,保持一致性 -->\n\u003cscript setup lang=\"ts\">\nconst user = ref\u003cUser | null>(null)\nconst loading = ref(false)\nconst error = ref\u003cError | null>(null)\n\u003c/script>\n```\n\n### 解构 reactive 对象\n\n```vue\n\u003c!-- ❌ 解构 reactive 会丢失响应性 -->\n\u003cscript setup lang=\"ts\">\nconst state = reactive({ count: 0, name: 'Vue' })\nconst { count, name } = state // 丢失响应性!\n\u003c/script>\n\n\u003c!-- ✅ 使用 toRefs 保持响应性 -->\n\u003cscript setup lang=\"ts\">\nconst state = reactive({ count: 0, name: 'Vue' })\nconst { count, name } = toRefs(state) // 保持响应性\n// 或者直接使用 ref\nconst count = ref(0)\nconst name = ref('Vue')\n\u003c/script>\n```\n\n### computed 副作用\n\n```vue\n\u003c!-- ❌ computed 中产生副作用 -->\n\u003cscript setup lang=\"ts\">\nconst fullName = computed(() => {\n console.log('Computing...') // 副作用!\n otherRef.value = 'changed' // 修改其他状态!\n return `${firstName.value} ${lastName.value}`\n})\n\u003c/script>\n\n\u003c!-- ✅ computed 只用于派生状态 -->\n\u003cscript setup lang=\"ts\">\nconst fullName = computed(() => {\n return `${firstName.value} ${lastName.value}`\n})\n// 副作用放在 watch 或事件处理中\nwatch(fullName, (name) => {\n console.log('Name changed:', name)\n})\n\u003c/script>\n```\n\n### shallowRef 优化\n\n```vue\n\u003c!-- ❌ 大型对象使用 ref 会深度转换 -->\n\u003cscript setup lang=\"ts\">\nconst largeData = ref(hugeNestedObject) // 深度响应式,性能开销大\n\u003c/script>\n\n\u003c!-- ✅ 使用 shallowRef 避免深度转换 -->\n\u003cscript setup lang=\"ts\">\nconst largeData = shallowRef(hugeNestedObject)\n\n// 整体替换才会触发更新\nfunction updateData(newData) {\n largeData.value = newData // ✅ 触发更新\n}\n\n// ❌ 修改嵌套属性不会触发更新\n// largeData.value.nested.prop = 'new'\n\n// 需要手动触发时使用 triggerRef\nimport { triggerRef } from 'vue'\nlargeData.value.nested.prop = 'new'\ntriggerRef(largeData)\n\u003c/script>\n```\n\n---\n\n## Props & Emits\n\n### 直接修改 props\n\n```vue\n\u003c!-- ❌ 直接修改 props -->\n\u003cscript setup lang=\"ts\">\nconst props = defineProps\u003c{ user: User }>()\nprops.user.name = 'New Name' // 永远不要直接修改 props!\n\u003c/script>\n\n\u003c!-- ✅ 使用 emit 通知父组件更新 -->\n\u003cscript setup lang=\"ts\">\nconst props = defineProps\u003c{ user: User }>()\nconst emit = defineEmits\u003c{\n update: [name: string]\n}>()\nconst updateName = (name: string) => emit('update', name)\n\u003c/script>\n```\n\n### defineProps 类型声明\n\n```vue\n\u003c!-- ❌ defineProps 缺少类型声明 -->\n\u003cscript setup lang=\"ts\">\nconst props = defineProps(['title', 'count']) // 无类型检查\n\u003c/script>\n\n\u003c!-- ✅ 使用类型声明 + withDefaults -->\n\u003cscript setup lang=\"ts\">\ninterface Props {\n title: string\n count?: number\n items?: string[]\n}\nconst props = withDefaults(defineProps\u003cProps>(), {\n count: 0,\n items: () => [] // 对象/数组默认值需要工厂函数\n})\n\u003c/script>\n```\n\n### defineEmits 类型安全\n\n```vue\n\u003c!-- ❌ defineEmits 缺少类型 -->\n\u003cscript setup lang=\"ts\">\nconst emit = defineEmits(['update', 'delete']) // 无类型检查\nemit('update', someValue) // 参数类型不安全\n\u003c/script>\n\n\u003c!-- ✅ 完整的类型定义 -->\n\u003cscript setup lang=\"ts\">\nconst emit = defineEmits\u003c{\n update: [id: number, value: string]\n delete: [id: number]\n 'custom-event': [payload: CustomPayload]\n}>()\n\n// 现在有完整的类型检查\nemit('update', 1, 'new value') // ✅\nemit('update', 'wrong') // ❌ TypeScript 报错\n\u003c/script>\n```\n\n---\n\n## Vue 3.5 新特性\n\n### Reactive Props Destructure (3.5+)\n\n```vue\n\u003c!-- Vue 3.5 之前:解构会丢失响应性 -->\n\u003cscript setup lang=\"ts\">\nconst props = defineProps\u003c{ count: number }>()\n// 需要使用 props.count 或 toRefs\n\u003c/script>\n\n\u003c!-- ✅ Vue 3.5+:解构保持响应性 -->\n\u003cscript setup lang=\"ts\">\nconst { count, name = 'default' } = defineProps\u003c{\n count: number\n name?: string\n}>()\n\n// count 和 name 自动保持响应性!\n// 可以直接在模板和 watch 中使用\nwatch(() => count, (newCount) => {\n console.log('Count changed:', newCount)\n})\n\u003c/script>\n\n\u003c!-- ✅ 配合默认值使用 -->\n\u003cscript setup lang=\"ts\">\nconst {\n title,\n count = 0,\n items = () => [] // 函数作为默认值(对象/数组)\n} = defineProps\u003c{\n title: string\n count?: number\n items?: () => string[]\n}>()\n\u003c/script>\n```\n\n### defineModel (3.4+)\n\n```vue\n\u003c!-- ❌ 传统 v-model 实现:冗长 -->\n\u003cscript setup lang=\"ts\">\nconst props = defineProps\u003c{ modelValue: string }>()\nconst emit = defineEmits\u003c{ 'update:modelValue': [value: string] }>()\n\n// 需要 computed 来双向绑定\nconst value = computed({\n get: () => props.modelValue,\n set: (val) => emit('update:modelValue', val)\n})\n\u003c/script>\n\n\u003c!-- ✅ defineModel:简洁的 v-model 实现 -->\n\u003cscript setup lang=\"ts\">\n// 自动处理 props 和 emit\nconst model = defineModel\u003cstring>()\n\n// 直接使用\nmodel.value = 'new value' // 自动 emit\n\u003c/script>\n\u003ctemplate>\n \u003cinput v-model=\"model\" />\n\u003c/template>\n\n\u003c!-- ✅ 命名 v-model -->\n\u003cscript setup lang=\"ts\">\n// v-model:title 的实现\nconst title = defineModel\u003cstring>('title')\n\n// 带默认值和选项\nconst count = defineModel\u003cnumber>('count', {\n default: 0,\n required: false\n})\n\u003c/script>\n\n\u003c!-- ✅ 多个 v-model -->\n\u003cscript setup lang=\"ts\">\nconst firstName = defineModel\u003cstring>('firstName')\nconst lastName = defineModel\u003cstring>('lastName')\n\u003c/script>\n\u003ctemplate>\n \u003c!-- 父组件使用:\u003cMyInput v-model:first-name=\"first\" v-model:last-name=\"last\" /> -->\n\u003c/template>\n\n\u003c!-- ✅ v-model 修饰符 -->\n\u003cscript setup lang=\"ts\">\nconst [model, modifiers] = defineModel\u003cstring>()\n\n// 检查修饰符\nif (modifiers.capitalize) {\n // 处理 .capitalize 修饰符\n}\n\u003c/script>\n```\n\n### useTemplateRef (3.5+)\n\n```vue\n\u003c!-- 传统方式:ref 属性与变量同名 -->\n\u003cscript setup lang=\"ts\">\nconst inputRef = ref\u003cHTMLInputElement | null>(null)\n\u003c/script>\n\u003ctemplate>\n \u003cinput ref=\"inputRef\" />\n\u003c/template>\n\n\u003c!-- ✅ useTemplateRef:更清晰的模板引用 -->\n\u003cscript setup lang=\"ts\">\nimport { useTemplateRef } from 'vue'\n\nconst input = useTemplateRef\u003cHTMLInputElement>('my-input')\n\nonMounted(() => {\n input.value?.focus()\n})\n\u003c/script>\n\u003ctemplate>\n \u003cinput ref=\"my-input\" />\n\u003c/template>\n\n\u003c!-- ✅ 动态 ref -->\n\u003cscript setup lang=\"ts\">\nconst refKey = ref('input-a')\nconst dynamicInput = useTemplateRef\u003cHTMLInputElement>(refKey)\n\u003c/script>\n```\n\n### useId (3.5+)\n\n```vue\n\u003c!-- ❌ 手动生成 ID 可能冲突 -->\n\u003cscript setup lang=\"ts\">\nconst id = `input-${Math.random()}` // SSR 不一致!\n\u003c/script>\n\n\u003c!-- ✅ useId:SSR 安全的唯一 ID -->\n\u003cscript setup lang=\"ts\">\nimport { useId } from 'vue'\n\nconst id = useId() // 例如:'v-0'\n\u003c/script>\n\u003ctemplate>\n \u003clabel :for=\"id\">Name\u003c/label>\n \u003cinput :id=\"id\" />\n\u003c/template>\n\n\u003c!-- ✅ 表单组件中使用 -->\n\u003cscript setup lang=\"ts\">\nconst inputId = useId()\nconst errorId = useId()\n\u003c/script>\n\u003ctemplate>\n \u003clabel :for=\"inputId\">Email\u003c/label>\n \u003cinput\n :id=\"inputId\"\n :aria-describedby=\"errorId\"\n />\n \u003cspan :id=\"errorId\" class=\"error\">{{ error }}\u003c/span>\n\u003c/template>\n```\n\n### onWatcherCleanup (3.5+)\n\n```vue\n\u003c!-- 传统方式:watch 第三个参数 -->\n\u003cscript setup lang=\"ts\">\nwatch(source, async (value, oldValue, onCleanup) => {\n const controller = new AbortController()\n onCleanup(() => controller.abort())\n // ...\n})\n\u003c/script>\n\n\u003c!-- ✅ onWatcherCleanup:更灵活的清理 -->\n\u003cscript setup lang=\"ts\">\nimport { onWatcherCleanup } from 'vue'\n\nwatch(source, async (value) => {\n const controller = new AbortController()\n onWatcherCleanup(() => controller.abort())\n\n // 可以在任意位置调用,不限于回调开头\n if (someCondition) {\n const anotherResource = createResource()\n onWatcherCleanup(() => anotherResource.dispose())\n }\n\n await fetchData(value, controller.signal)\n})\n\u003c/script>\n```\n\n### Deferred Teleport (3.5+)\n\n```vue\n\u003c!-- ❌ Teleport 目标必须在挂载时存在 -->\n\u003ctemplate>\n \u003cTeleport to=\"#modal-container\">\n \u003c!-- 如果 #modal-container 不存在会报错 -->\n \u003c/Teleport>\n\u003c/template>\n\n\u003c!-- ✅ defer 属性延迟挂载 -->\n\u003ctemplate>\n \u003cTeleport to=\"#modal-container\" defer>\n \u003c!-- 等待目标元素存在后再挂载 -->\n \u003cModal />\n \u003c/Teleport>\n\u003c/template>\n```\n\n---\n\n## Watchers\n\n### watch vs watchEffect\n\n```vue\n\u003cscript setup lang=\"ts\">\n// ✅ watch:明确指定依赖,惰性执行\nwatch(\n () => props.userId,\n async (userId) => {\n user.value = await fetchUser(userId)\n }\n)\n\n// ✅ watchEffect:自动收集依赖,立即执行\nwatchEffect(async () => {\n // 自动追踪 props.userId\n user.value = await fetchUser(props.userId)\n})\n\n// 💡 选择指南:\n// - 需要旧值?用 watch\n// - 需要惰性执行?用 watch\n// - 依赖复杂?用 watchEffect\n\u003c/script>\n```\n\n### watch 清理函数\n\n```vue\n\u003c!-- ❌ watch 缺少清理函数,可能内存泄漏 -->\n\u003cscript setup lang=\"ts\">\nwatch(searchQuery, async (query) => {\n const controller = new AbortController()\n const data = await fetch(`/api/search?q=${query}`, {\n signal: controller.signal\n })\n results.value = await data.json()\n // 如果 query 快速变化,旧请求不会被取消!\n})\n\u003c/script>\n\n\u003c!-- ✅ 使用 onCleanup 清理副作用 -->\n\u003cscript setup lang=\"ts\">\nwatch(searchQuery, async (query, _, onCleanup) => {\n const controller = new AbortController()\n onCleanup(() => controller.abort()) // 取消旧请求\n\n try {\n const data = await fetch(`/api/search?q=${query}`, {\n signal: controller.signal\n })\n results.value = await data.json()\n } catch (e) {\n if (e.name !== 'AbortError') throw e\n }\n})\n\u003c/script>\n```\n\n### watch 选项\n\n```vue\n\u003cscript setup lang=\"ts\">\n// ✅ immediate:立即执行一次\nwatch(\n userId,\n async (id) => {\n user.value = await fetchUser(id)\n },\n { immediate: true }\n)\n\n// ✅ deep:深度监听(性能开销大,谨慎使用)\nwatch(\n state,\n (newState) => {\n console.log('State changed deeply')\n },\n { deep: true }\n)\n\n// ✅ flush: 'post':DOM 更新后执行\nwatch(\n source,\n () => {\n // 可以安全访问更新后的 DOM\n // nextTick 不再需要\n },\n { flush: 'post' }\n)\n\n// ✅ once: true (Vue 3.4+):只执行一次\nwatch(\n source,\n (value) => {\n console.log('只会执行一次:', value)\n },\n { once: true }\n)\n\u003c/script>\n```\n\n### 监听多个源\n\n```vue\n\u003cscript setup lang=\"ts\">\n// ✅ 监听多个 ref\nwatch(\n [firstName, lastName],\n ([newFirst, newLast], [oldFirst, oldLast]) => {\n console.log(`Name changed from ${oldFirst} ${oldLast} to ${newFirst} ${newLast}`)\n }\n)\n\n// ✅ 监听 reactive 对象的特定属性\nwatch(\n () => [state.count, state.name],\n ([count, name]) => {\n console.log(`count: ${count}, name: ${name}`)\n }\n)\n\u003c/script>\n```\n\n---\n\n## 模板最佳实践\n\n### v-for 的 key\n\n```vue\n\u003c!-- ❌ v-for 中使用 index 作为 key -->\n\u003ctemplate>\n \u003cli v-for=\"(item, index) in items\" :key=\"index\">\n {{ item.name }}\n \u003c/li>\n\u003c/template>\n\n\u003c!-- ✅ 使用唯一标识作为 key -->\n\u003ctemplate>\n \u003cli v-for=\"item in items\" :key=\"item.id\">\n {{ item.name }}\n \u003c/li>\n\u003c/template>\n\n\u003c!-- ✅ 复合 key(当没有唯一 ID 时)-->\n\u003ctemplate>\n \u003cli v-for=\"(item, index) in items\" :key=\"`${item.name}-${item.type}-${index}`\">\n {{ item.name }}\n \u003c/li>\n\u003c/template>\n```\n\n### v-if 和 v-for 优先级\n\n```vue\n\u003c!-- ❌ v-if 和 v-for 同时使用 -->\n\u003ctemplate>\n \u003cli v-for=\"user in users\" v-if=\"user.active\" :key=\"user.id\">\n {{ user.name }}\n \u003c/li>\n\u003c/template>\n\n\u003c!-- ✅ 使用 computed 过滤 -->\n\u003cscript setup lang=\"ts\">\nconst activeUsers = computed(() =>\n users.value.filter(user => user.active)\n)\n\u003c/script>\n\u003ctemplate>\n \u003cli v-for=\"user in activeUsers\" :key=\"user.id\">\n {{ user.name }}\n \u003c/li>\n\u003c/template>\n\n\u003c!-- ✅ 或用 template 包裹 -->\n\u003ctemplate>\n \u003ctemplate v-for=\"user in users\" :key=\"user.id\">\n \u003cli v-if=\"user.active\">\n {{ user.name }}\n \u003c/li>\n \u003c/template>\n\u003c/template>\n```\n\n### 事件处理\n\n```vue\n\u003c!-- ❌ 内联复杂逻辑 -->\n\u003ctemplate>\n \u003cbutton @click=\"items = items.filter(i => i.id !== item.id); count--\">\n Delete\n \u003c/button>\n\u003c/template>\n\n\u003c!-- ✅ 使用方法 -->\n\u003cscript setup lang=\"ts\">\nconst deleteItem = (id: number) => {\n items.value = items.value.filter(i => i.id !== id)\n count.value--\n}\n\u003c/script>\n\u003ctemplate>\n \u003cbutton @click=\"deleteItem(item.id)\">Delete\u003c/button>\n\u003c/template>\n\n\u003c!-- ✅ 事件修饰符 -->\n\u003ctemplate>\n \u003c!-- 阻止默认行为 -->\n \u003cform @submit.prevent=\"handleSubmit\">...\u003c/form>\n\n \u003c!-- 阻止冒泡 -->\n \u003cbutton @click.stop=\"handleClick\">...\u003c/button>\n\n \u003c!-- 只执行一次 -->\n \u003cbutton @click.once=\"handleOnce\">...\u003c/button>\n\n \u003c!-- 键盘修饰符 -->\n \u003cinput @keyup.enter=\"submit\" @keyup.esc=\"cancel\" />\n\u003c/template>\n```\n\n---\n\n## Composables\n\n### Composable 设计原则\n\n```typescript\n// ✅ 好的 composable 设计\nexport function useCounter(initialValue = 0) {\n const count = ref(initialValue)\n\n const increment = () => count.value++\n const decrement = () => count.value--\n const reset = () => count.value = initialValue\n\n // 返回响应式引用和方法\n return {\n count: readonly(count), // 只读防止外部修改\n increment,\n decrement,\n reset\n }\n}\n\n// ❌ 不要返回 .value\nexport function useBadCounter() {\n const count = ref(0)\n return {\n count: count.value // ❌ 丢失响应性!\n }\n}\n```\n\n### Props 传递给 composable\n\n```vue\n\u003c!-- ❌ 传递 props 到 composable 丢失响应性 -->\n\u003cscript setup lang=\"ts\">\nconst props = defineProps\u003c{ userId: string }>()\nconst { user } = useUser(props.userId) // 丢失响应性!\n\u003c/script>\n\n\u003c!-- ✅ 使用 toRef 或 computed 保持响应性 -->\n\u003cscript setup lang=\"ts\">\nconst props = defineProps\u003c{ userId: string }>()\nconst userIdRef = toRef(props, 'userId')\nconst { user } = useUser(userIdRef) // 保持响应性\n// 或使用 computed\nconst { user } = useUser(computed(() => props.userId))\n\n// ✅ Vue 3.5+:直接解构使用\nconst { userId } = defineProps\u003c{ userId: string }>()\nconst { user } = useUser(() => userId) // getter 函数\n\u003c/script>\n```\n\n### 异步 Composable\n\n```typescript\n// ✅ 异步 composable 模式\nexport function useFetch\u003cT>(url: MaybeRefOrGetter\u003cstring>) {\n const data = ref\u003cT | null>(null)\n const error = ref\u003cError | null>(null)\n const loading = ref(false)\n\n const execute = async () => {\n loading.value = true\n error.value = null\n\n try {\n const response = await fetch(toValue(url))\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`)\n }\n data.value = await response.json()\n } catch (e) {\n error.value = e as Error\n } finally {\n loading.value = false\n }\n }\n\n // 响应式 URL 时自动重新获取\n watchEffect(() => {\n toValue(url) // 追踪依赖\n execute()\n })\n\n return {\n data: readonly(data),\n error: readonly(error),\n loading: readonly(loading),\n refetch: execute\n }\n}\n\n// 使用\nconst { data, loading, error, refetch } = useFetch\u003cUser[]>('/api/users')\n```\n\n### 生命周期与清理\n\n```typescript\n// ✅ Composable 中正确处理生命周期\nexport function useEventListener(\n target: MaybeRefOrGetter\u003cEventTarget>,\n event: string,\n handler: EventListener\n) {\n // 组件挂载后添加\n onMounted(() => {\n toValue(target).addEventListener(event, handler)\n })\n\n // 组件卸载时移除\n onUnmounted(() => {\n toValue(target).removeEventListener(event, handler)\n })\n}\n\n// ✅ 使用 effectScope 管理副作用\nexport function useFeature() {\n const scope = effectScope()\n\n scope.run(() => {\n // 所有响应式效果都在这个 scope 内\n const state = ref(0)\n watch(state, () => { /* ... */ })\n watchEffect(() => { /* ... */ })\n })\n\n // 清理所有效果\n onUnmounted(() => scope.stop())\n\n return { /* ... */ }\n}\n```\n\n---\n\n## 性能优化\n\n### v-memo\n\n```vue\n\u003c!-- ✅ v-memo:缓存子树,避免重复渲染 -->\n\u003ctemplate>\n \u003cdiv v-for=\"item in list\" :key=\"item.id\" v-memo=\"[item.id === selected]\">\n \u003c!-- 只有当 item.id === selected 变化时才重新渲染 -->\n \u003cExpensiveComponent :item=\"item\" :selected=\"item.id === selected\" />\n \u003c/div>\n\u003c/template>\n\n\u003c!-- ✅ 配合 v-for 使用 -->\n\u003ctemplate>\n \u003cdiv\n v-for=\"item in list\"\n :key=\"item.id\"\n v-memo=\"[item.name, item.status]\"\n >\n \u003c!-- 只有 name 或 status 变化时重新渲染 -->\n \u003c/div>\n\u003c/template>\n```\n\n### defineAsyncComponent\n\n```vue\n\u003cscript setup lang=\"ts\">\nimport { defineAsyncComponent } from 'vue'\n\n// ✅ 懒加载组件\nconst HeavyChart = defineAsyncComponent(() =>\n import('./components/HeavyChart.vue')\n)\n\n// ✅ 带加载和错误状态\nconst AsyncModal = defineAsyncComponent({\n loader: () => import('./components/Modal.vue'),\n loadingComponent: LoadingSpinner,\n errorComponent: ErrorDisplay,\n delay: 200, // 延迟显示 loading(避免闪烁)\n timeout: 3000 // 超时时间\n})\n\u003c/script>\n```\n\n### KeepAlive\n\n```vue\n\u003ctemplate>\n \u003c!-- ✅ 缓存动态组件 -->\n \u003cKeepAlive>\n \u003ccomponent :is=\"currentTab\" />\n \u003c/KeepAlive>\n\n \u003c!-- ✅ 指定缓存的组件 -->\n \u003cKeepAlive include=\"TabA,TabB\">\n \u003ccomponent :is=\"currentTab\" />\n \u003c/KeepAlive>\n\n \u003c!-- ✅ 限制缓存数量 -->\n \u003cKeepAlive :max=\"10\">\n \u003ccomponent :is=\"currentTab\" />\n \u003c/KeepAlive>\n\u003c/template>\n\n\u003cscript setup lang=\"ts\">\n// KeepAlive 组件的生命周期钩子\nonActivated(() => {\n // 组件被激活时(从缓存恢复)\n refreshData()\n})\n\nonDeactivated(() => {\n // 组件被停用时(进入缓存)\n pauseTimers()\n})\n\u003c/script>\n```\n\n### 虚拟列表\n\n```vue\n\u003c!-- ✅ 大型列表使用虚拟滚动 -->\n\u003cscript setup lang=\"ts\">\nimport { useVirtualList } from '@vueuse/core'\n\nconst { list, containerProps, wrapperProps } = useVirtualList(\n items,\n { itemHeight: 50 }\n)\n\u003c/script>\n\u003ctemplate>\n \u003cdiv v-bind=\"containerProps\" style=\"height: 400px; overflow: auto\">\n \u003cdiv v-bind=\"wrapperProps\">\n \u003cdiv v-for=\"item in list\" :key=\"item.data.id\" style=\"height: 50px\">\n {{ item.data.name }}\n \u003c/div>\n \u003c/div>\n \u003c/div>\n\u003c/template>\n```\n\n---\n\n## Review Checklist\n\n### 响应性系统\n- [ ] ref 用于基本类型,reactive 用于对象(或统一用 ref)\n- [ ] 没有解构 reactive 对象(或使用了 toRefs)\n- [ ] props 传递给 composable 时保持了响应性\n- [ ] shallowRef/shallowReactive 用于大型对象优化\n- [ ] computed 中没有副作用\n\n### Props & Emits\n- [ ] defineProps 使用 TypeScript 类型声明\n- [ ] 复杂默认值使用 withDefaults + 工厂函数\n- [ ] defineEmits 有完整的类型定义\n- [ ] 没有直接修改 props\n- [ ] 考虑使用 defineModel 简化 v-model(Vue 3.4+)\n\n### Vue 3.5 新特性(如适用)\n- [ ] 使用 Reactive Props Destructure 简化 props 访问\n- [ ] 使用 useTemplateRef 替代 ref 属性\n- [ ] 表单使用 useId 生成 SSR 安全的 ID\n- [ ] 使用 onWatcherCleanup 处理复杂清理逻辑\n\n### Watchers\n- [ ] watch/watchEffect 有适当的清理函数\n- [ ] 异步 watch 处理了竞态条件\n- [ ] flush: 'post' 用于 DOM 操作的 watcher\n- [ ] 避免过度使用 watcher(优先用 computed)\n- [ ] 考虑 once: true 用于一次性监听\n\n### 模板\n- [ ] v-for 使用唯一且稳定的 key\n- [ ] v-if 和 v-for 没有在同一元素上\n- [ ] 事件处理使用方法而非内联复杂逻辑\n- [ ] 大型列表使用虚拟滚动\n\n### Composables\n- [ ] 相关逻辑提取到 composables\n- [ ] composables 返回响应式引用(不是 .value)\n- [ ] 纯函数不要包装成 composable\n- [ ] 副作用在组件卸载时清理\n- [ ] 使用 effectScope 管理复杂副作用\n\n### 性能\n- [ ] 大型组件拆分为小组件\n- [ ] 使用 defineAsyncComponent 懒加载\n- [ ] 避免不必要的响应式转换\n- [ ] v-memo 用于昂贵的列表渲染\n- [ ] KeepAlive 用于缓存动态组件\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":20499,"content_sha256":"49686f8c59b2069f0b3b322a97555615ec071d991412d98a70b8425dbe94bdd4"},{"filename":"reference/zig.md","content":"# Zig Code Review Guide\n\n> Code review guidelines for Zig focusing on explicit memory ownership, error unions, defer/errdefer cleanup, comptime usage, safety-checked operations, C interop, and tests.\n\n## Table of Contents\n\n- [Memory & Allocators](#memory--allocators)\n- [Errors & Cleanup](#errors--cleanup)\n- [Pointers, Slices & Optionals](#pointers-slices--optionals)\n- [Comptime & Generics](#comptime--generics)\n- [Safety, Undefined Behavior & Casts](#safety-undefined-behavior--casts)\n- [C Interop](#c-interop)\n- [Testing](#testing)\n- [Style & API Design](#style--api-design)\n- [Review Checklist](#review-checklist)\n- [References](#references)\n\n---\n\n## Memory & Allocators\n\n### Make Allocator Ownership Explicit\n\nZig code should make allocation policy visible. Libraries should usually accept an `std.mem.Allocator` from the caller rather than creating a global allocator internally.\n\n```zig\nconst std = @import(\"std\");\n\n// ❌ Bad: hides allocation policy and lifetime from callers.\nfn readNamesBad() ![][]const u8 {\n var gpa = std.heap.DebugAllocator(.{}){};\n const allocator = gpa.allocator();\n return try allocator.alloc([]const u8, 10);\n}\n\n// ✅ Good: caller chooses allocator and owns the returned memory.\nfn readNames(allocator: std.mem.Allocator) ![][]const u8 {\n return try allocator.alloc([]const u8, 10);\n}\n```\n\nReview questions:\n- Does the caller know who owns allocated memory?\n- Does the API document whether returned slices must be freed?\n- Is the allocator parameter passed through instead of replaced by an internal global allocator?\n\n### Pair Allocations With Cleanup\n\nEvery allocation path should have a visible cleanup path. Look for missing `defer`, missing `errdefer`, and containers that are initialized but never deinitialized.\n\n```zig\nconst std = @import(\"std\");\n\nfn collectBad(allocator: std.mem.Allocator) ![]u8 {\n // ❌ Bad: returns a slice whose backing memory is freed as the function exits.\n var bad_list: std.ArrayListUnmanaged(u8) = .empty;\n defer bad_list.deinit(allocator);\n try bad_list.append(allocator, 'a');\n return bad_list.items;\n}\n\nfn collect(allocator: std.mem.Allocator) ![]u8 {\n // ✅ Good: `errdefer` cleans up only on failure; success transfers ownership.\n var list: std.ArrayListUnmanaged(u8) = .empty;\n errdefer list.deinit(allocator);\n\n try list.append(allocator, 'a');\n try list.append(allocator, 'b');\n return try list.toOwnedSlice(allocator);\n}\n```\n\nReview questions:\n- Is cleanup registered immediately after acquisition?\n- Is `errdefer` used when ownership transfers only on success?\n- Are `deinit` calls paired with all container initializations?\n\n### Choose Allocators Deliberately\n\nAllocator choice is part of the design. A review should flag broad use of a debug/general-purpose allocator where a fixed buffer, arena, page allocator, or caller-provided allocator better matches the lifetime.\n\n```zig\n// ❌ Bad: allocation lifetime is scattered across many individual frees.\nconst user = try allocator.create(User);\nconst events = try allocator.alloc(Event, event_count);\n\n// ✅ Good: request/frame-scoped allocations are freed together.\nvar arena = std.heap.ArenaAllocator.init(parent_allocator);\ndefer arena.deinit();\nconst allocator = arena.allocator();\n```\n\nReview questions:\n- Are arena allocations freed at a clear lifetime boundary?\n- Is a test using `std.testing.allocator` to catch leaks?\n- Is a library avoiding policy decisions that belong to its caller?\n\n---\n\n## Errors & Cleanup\n\n### Keep Error Sets Useful\n\nAvoid flattening meaningful errors into `anyerror` unless the boundary genuinely needs it. Specific error sets improve API contracts and make callers handle expected failures.\n\n```zig\n// ❌ Bad: erases the expected parse failures behind `anyerror`.\nfn parseDigitAny(input: []const u8) anyerror!u8 {\n if (input.len == 0) return error.EmptyInput;\n if (input[0] \u003c '0' or input[0] > '9') return error.InvalidDigit;\n return input[0] - '0';\n}\n\n// ✅ Good: names the domain failures that callers should handle.\nconst ParseError = error{\n EmptyInput,\n InvalidDigit,\n};\n\nfn parseDigit(input: []const u8) ParseError!u8 {\n if (input.len == 0) return error.EmptyInput;\n if (input[0] \u003c '0' or input[0] > '9') return error.InvalidDigit;\n return input[0] - '0';\n}\n```\n\nReview questions:\n- Are expected domain failures named explicitly?\n- Is `anyerror` only used at integration boundaries?\n- Does the caller preserve context when converting errors?\n\n### Use `try`, `catch`, and `errdefer` Intentionally\n\nBlind `catch unreachable` is a code smell unless the invariant is mechanically guaranteed. Prefer propagating errors with `try`, converting them at boundaries, or adding a comment for unreachable invariants.\n\n```zig\n// ❌ Bad: hides a real allocation failure.\nconst buffer_bad = allocator.alloc(u8, size) catch unreachable;\n\n// ✅ Good: caller can handle OutOfMemory.\nconst buffer = try allocator.alloc(u8, size);\n```\n\nReview questions:\n- Does `catch unreachable` mask I/O, allocation, parsing, or user-input errors?\n- Are errors converted close to a boundary where the abstraction changes?\n- Does `errdefer` undo partial state changes on failure?\n\n---\n\n## Pointers, Slices & Optionals\n\n### Prefer Slices Over Pointer Plus Length\n\nSlices carry pointer and length together, improving bounds checking and API clarity. Raw pointer plus length should be reserved for FFI or very low-level code.\n\n```zig\n// ❌ Bad: easy to mismatch pointer and length.\nfn checksumRaw(ptr: [*]const u8, len: usize) u32 {\n var sum: u32 = 0;\n for (ptr[0..len]) |byte| sum += byte;\n return sum;\n}\n\n// ✅ Good: one value represents the buffer.\nfn checksum(bytes: []const u8) u32 {\n var sum: u32 = 0;\n for (bytes) |byte| sum += byte;\n return sum;\n}\n```\n\nReview questions:\n- Can a raw pointer API become a slice API?\n- Is nullability modeled with `?T` instead of sentinel values?\n- Are pointer lifetimes clear after returning from a function?\n\n### Avoid Returning Pointers to Stack Data\n\nReview returned slices and pointers carefully. Zig makes many lifetime issues visible, but reviewers should still check that returned data outlives the function.\n\n```zig\n// ❌ Bad: returned slice points to stack memory.\nfn labelStack() []const u8 {\n var buf: [16]u8 = undefined;\n _ = &buf;\n return buf[0..];\n}\n\n// ✅ Good: caller owns the allocated result and can free it.\nfn label(allocator: std.mem.Allocator) ![]u8 {\n return try allocator.dupe(u8, \"ready\");\n}\n```\n\nReview questions:\n- Does returned memory come from the caller, allocator, static storage, or a stable owner?\n- Does a slice escape after its backing buffer is mutated or freed?\n- Is aliasing intentional and documented for mutable slices?\n\n---\n\n## Comptime & Generics\n\n### Keep Comptime Work Bounded and Readable\n\n`comptime` is powerful, but complex compile-time code can make error messages and build times worse. Prefer small generic helpers with clear type contracts.\n\n```zig\nfn RingBuffer(comptime T: type, comptime capacity: usize) type {\n // ✅ Good: invalid generic parameters fail with an actionable message.\n if (capacity == 0) @compileError(\"capacity must be greater than zero\");\n\n return struct {\n items: [capacity]T = undefined,\n len: usize = 0,\n };\n}\n```\n\nReview questions:\n- Does `comptime` enforce a real invariant?\n- Are compile errors explicit and actionable?\n- Is reflection code isolated from ordinary runtime logic?\n\n### Avoid Overly Broad `anytype`\n\n`anytype` can make APIs flexible, but it can also hide required capabilities. Add comptime checks or prefer concrete interfaces when possible.\n\n```zig\n// ✅ Good: the required writer capability is obvious at the call site.\nfn writeAll(writer: anytype, bytes: []const u8) !void {\n try writer.writeAll(bytes);\n}\n```\n\nReview questions:\n- Is the required shape of `anytype` clear from the function body or docs?\n- Would a concrete type or smaller helper be easier to review?\n- Are compile errors understandable when the wrong type is passed?\n\n---\n\n## Safety, Undefined Behavior & Casts\n\n### Treat `undefined`, `unreachable`, and Casts as Review Hotspots\n\nZig exposes low-level control directly. Review every `undefined`, `unreachable`, `@ptrCast`, `@alignCast`, `@intCast`, and pointer/int conversion.\n\n```zig\n// ❌ Bad: assumes data layout, byte order, length, and alignment without proof.\nconst header: *const Header = @ptrCast(@alignCast(bytes.ptr));\n\n// ✅ Good: parse fields explicitly and check length before reading.\nif (bytes.len \u003c 4) return error.ShortInput;\nconst magic = std.mem.readInt(u16, bytes[0..2], .little);\nconst flags = std.mem.readInt(u16, bytes[2..4], .little);\n```\n\nReview questions:\n- Is `undefined` overwritten before being read?\n- Is `unreachable` only used for impossible states, with a nearby explanation when non-obvious?\n- Are casts preceded by checks for layout, range, alignment, size, byte order, and nullability?\n\n### Prefer Checked Arithmetic Unless Wrapping Is Intentional\n\nWrapping operators such as `+%` and `-%` are useful, but they should communicate a deliberate modular arithmetic choice.\n\n```zig\n// ❌ Bad: wrapping silences overflow that should expose a logic bug.\nindex = index +% 1;\n\n// ✅ Good: checked arithmetic traps on unexpected overflow.\nsum += byte;\n\n// ✅ Good: wrapping is intentional for modular hash behavior.\nhash = hash *% 16777619;\nhash = hash +% byte;\n```\n\nReview questions:\n- Is wrapping arithmetic required by an algorithm?\n- Would overflow indicate invalid input or a bug?\n- Are integer width changes explicit and tested around boundaries?\n\n---\n\n## C Interop\n\n### Contain C Boundaries\n\nKeep `@cImport`, C pointer handling, and ABI assumptions close to a wrapper layer. Convert C data into Zig types before it spreads through the codebase.\n\n```zig\nconst std = @import(\"std\");\n\n// ❌ Bad: uses C strlen when no external C boundary is needed.\nfn strlenC(input: [*:0]const u8) usize {\n const c = @cImport({\n @cInclude(\"string.h\");\n });\n\n return c.strlen(input);\n}\n\n// ✅ Good: use Zig's sentinel-aware standard library helper.\nfn strlenZ(input: [*:0]const u8) usize {\n return std.mem.len(input);\n}\n```\n\nReview questions:\n- Are C pointers represented with the correct Zig pointer type?\n- Are sentinel-terminated strings modeled as sentinel pointers or slices?\n- Are ownership and cleanup rules from the C library documented?\n- Are C error codes converted into Zig error unions near the boundary?\n\n---\n\n## Testing\n\n### Use `std.testing` Assertions and Leak Detection\n\nTests that allocate should use `std.testing.allocator` where practical so leaks are reported by the test runner.\n\n```zig\nconst std = @import(\"std\");\n\ntest \"collect returns owned memory on success\" {\n const allocator = std.testing.allocator;\n const names = try collect(allocator);\n defer allocator.free(names);\n\n try std.testing.expectEqual(@as(usize, 2), names.len);\n}\n\ntest \"collect handles allocation failures cleanly\" {\n var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{\n .fail_index = 0,\n });\n try std.testing.expectError(error.OutOfMemory, collect(failing.allocator()));\n}\n```\n\nReview questions:\n- Are allocation-heavy paths covered by tests?\n- Are error paths tested with `std.testing.expectError`?\n- Do tests cover boundary sizes such as empty input, one element, max capacity, and invalid encodings?\n\n### Test Build Modes and Targets When Relevant\n\nBehavior can differ across safety modes, targets, and ABIs. For low-level code, ask whether the PR was tested with the intended target and optimization mode.\n\nReview questions:\n- Does code rely on debug-only safety checks?\n- Does packed/aligned/extern layout code have target-aware tests?\n- Are endian, pointer-width, and ABI assumptions tested or documented?\n\n---\n\n## Style & API Design\n\n### Follow Zig Naming Conventions\n\nUse the official style guide as the baseline: `TitleCase` for types, `camelCase` for functions, and `snake_case` for variables. Avoid redundant words such as `Value`, `Data`, `Manager`, or `State` when the surrounding namespace already provides that meaning.\n\n```zig\n// ❌ Bad: redundant namespace and vague type name.\npub const json_bad = struct {\n pub const JsonValueManager = struct {};\n};\n\n// ✅ Good: name is meaningful in its fully-qualified namespace.\npub const json = struct {\n pub const Value = union(enum) {};\n};\n```\n\nReview questions:\n- Does the fully-qualified name read naturally?\n- Are file and directory names consistent with the style guide?\n- Are underscore-prefixed declarations avoided unless they come from an external convention?\n- Are public APIs documented with doc comments where helpful?\n\n### Keep Public APIs Small\n\nZig modules often expose declarations directly from files and structs. Review public declarations for accidental exports.\n\nReview questions:\n- Should this declaration be `pub`?\n- Is the public API stable enough to expose?\n- Are implementation details hidden behind a smaller surface?\n\n---\n\n## Review Checklist\n\n### Memory & Lifetime\n- [ ] Allocator ownership is explicit.\n- [ ] Allocations have matching `free`, `deinit`, `defer`, or `errdefer`.\n- [ ] Returned slices and pointers outlive the function.\n- [ ] Arena or temporary allocations have a clear lifetime boundary.\n\n### Errors & Cleanup\n- [ ] Expected failures use specific error sets where practical.\n- [ ] `catch unreachable` does not hide real runtime failures.\n- [ ] Partial initialization is rolled back with `errdefer`.\n- [ ] Error conversions happen at abstraction boundaries.\n\n### Pointers & Safety\n- [ ] Raw pointers are justified; slices are used for ordinary buffers.\n- [ ] Casts check size, range, alignment, and nullability.\n- [ ] `undefined` is not read before initialization.\n- [ ] Wrapping arithmetic is intentional and tested.\n\n### Comptime & API Design\n- [ ] `comptime` logic enforces useful invariants.\n- [ ] `anytype` usage has clear expectations.\n- [ ] Public declarations are intentional.\n- [ ] Names follow Zig style conventions.\n\n### C Interop & Portability\n- [ ] C boundaries are isolated behind wrappers.\n- [ ] C ownership and cleanup rules are documented.\n- [ ] Target, endian, pointer-width, and ABI assumptions are tested or documented.\n\n### Tests\n- [ ] Tests use `std.testing.allocator` for allocation-heavy code.\n- [ ] Error paths use `std.testing.expectError`.\n- [ ] Boundary cases are covered.\n- [ ] Relevant build modes and targets are considered.\n\n---\n\n## References\n\n- [Zig 0.16.0 Language Reference](https://ziglang.org/documentation/0.16.0/)\n- [Zig 0.16.0 Standard Library documentation](https://ziglang.org/documentation/0.16.0/std/)\n- [Zig 0.16.0 Style Guide](https://ziglang.org/documentation/0.16.0/#Style-Guide)\n- [Choosing an Allocator](https://ziglang.org/documentation/0.16.0/#Choosing-an-Allocator)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":14845,"content_sha256":"9a5e9b727d206d91d687ec3cf3caf3ed85e1cbf92b2df5675f82476abe45e017"},{"filename":"scripts/pr-analyzer.py","content":"#!/usr/bin/env python3\n\"\"\"\nPR Analyzer - Analyze PR complexity and suggest review approach.\n\nUsage:\n python pr-analyzer.py [--diff-file FILE] [--stats]\n\n Or pipe diff directly:\n git diff main...HEAD | python pr-analyzer.py\n\"\"\"\n\nimport os\nimport sys\nimport re\nimport argparse\nfrom collections import defaultdict\nfrom dataclasses import dataclass\nfrom typing import List, Dict, Optional\n\nRISK_NO_TESTS = \"NO_TEST_CHANGES\"\n\n\n@dataclass\nclass FileStats:\n \"\"\"Statistics for a single file.\"\"\"\n filename: str\n additions: int = 0\n deletions: int = 0\n is_test: bool = False\n is_config: bool = False\n language: str = \"unknown\"\n\n\n@dataclass\nclass PRAnalysis:\n \"\"\"Complete PR analysis results.\"\"\"\n total_files: int\n total_additions: int\n total_deletions: int\n files: List[FileStats]\n complexity_score: float\n size_category: str\n estimated_review_time: int\n risk_factors: List[str]\n suggestions: List[str]\n\n\ndef detect_language(filename: str) -> str:\n \"\"\"Detect programming language from filename.\"\"\"\n _, ext = os.path.splitext(filename)\n extensions = {\n '.py': 'Python',\n '.js': 'JavaScript',\n '.ts': 'TypeScript',\n '.tsx': 'TypeScript/React',\n '.jsx': 'JavaScript/React',\n '.rs': 'Rust',\n '.go': 'Go',\n '.c': 'C',\n '.h': 'C/C++',\n '.cpp': 'C++',\n '.hpp': 'C++',\n '.cc': 'C++',\n '.cxx': 'C++',\n '.hh': 'C++',\n '.hxx': 'C++',\n '.java': 'Java',\n '.kt': 'Kotlin',\n '.swift': 'Swift',\n '.rb': 'Ruby',\n '.php': 'PHP',\n '.cs': 'C#',\n '.vue': 'Vue',\n '.svelte': 'Svelte',\n '.sql': 'SQL',\n '.md': 'Markdown',\n '.json': 'JSON',\n '.yaml': 'YAML',\n '.yml': 'YAML',\n '.toml': 'TOML',\n '.css': 'CSS',\n '.scss': 'SCSS',\n '.less': 'Less',\n '.html': 'HTML',\n '.zig': 'Zig',\n '.ex': 'Elixir',\n '.exs': 'Elixir',\n '.erl': 'Erlang',\n '.scala': 'Scala',\n '.lua': 'Lua',\n }\n return extensions.get(ext.lower(), 'unknown')\n\n\ndef is_test_file(filename: str) -> bool:\n \"\"\"Check if file is a test file.\"\"\"\n test_patterns = [\n r'(?:^|/)test_[^/]+\\.py

Code Review Skill Transform code reviews from gatekeeping to knowledge sharing through constructive feedback, systematic analysis, and collaborative improvement. When to Use This Skill - Reviewing pull requests and code changes - Establishing code review standards for teams - Mentoring junior developers through reviews - Conducting architecture reviews - Creating review checklists and guidelines - Improving team collaboration - Reducing code review cycle time - Maintaining code quality standards Core Principles 1. The Review Mindset Goals of Code Review: - Catch bugs and edge cases - Ensure c…

, # Python: test_handler.py (anchored to path segment)\n r'[^/]+_test\\.', # *_test.\u003cext> — Rust, Go, etc.\n r'[^/]+\\.test\\.(js|ts|tsx|jsx)

Code Review Skill Transform code reviews from gatekeeping to knowledge sharing through constructive feedback, systematic analysis, and collaborative improvement. When to Use This Skill - Reviewing pull requests and code changes - Establishing code review standards for teams - Mentoring junior developers through reviews - Conducting architecture reviews - Creating review checklists and guidelines - Improving team collaboration - Reducing code review cycle time - Maintaining code quality standards Core Principles 1. The Review Mindset Goals of Code Review: - Catch bugs and edge cases - Ensure c…

, # JS/TS: handler.test.ts\n r'[^/]+\\.spec\\.(js|ts|tsx|jsx)

Code Review Skill Transform code reviews from gatekeeping to knowledge sharing through constructive feedback, systematic analysis, and collaborative improvement. When to Use This Skill - Reviewing pull requests and code changes - Establishing code review standards for teams - Mentoring junior developers through reviews - Conducting architecture reviews - Creating review checklists and guidelines - Improving team collaboration - Reducing code review cycle time - Maintaining code quality standards Core Principles 1. The Review Mindset Goals of Code Review: - Catch bugs and edge cases - Ensure c…

, # JS/TS: handler.spec.ts\n r'(?:^|/)tests?/', # tests/ or test/ directory\n r'(?:^|/)__tests__/', # __tests__/ directory\n r'(?:^|/)spec/', # spec/ directory (Ruby, etc.)\n ]\n return any(re.search(p, filename) for p in test_patterns)\n\n\ndef is_config_file(filename: str) -> bool:\n \"\"\"Check if file is a configuration file.\n\n Uses explicit known-name matching to avoid flagging data files\n like data.json, openapi.yaml, or swagger.json as config.\n \"\"\"\n basename = os.path.basename(filename)\n\n # Env files (.env, .env.local, .env.production, …)\n if basename.startswith('.env'):\n return True\n\n # Known config filenames (exact match)\n known_config_names = {\n 'package.json', 'package-lock.json',\n 'tsconfig.json', 'jsconfig.json',\n 'babel.config.json', 'babel.config.js',\n 'webpack.config.js', 'webpack.config.ts',\n 'rollup.config.js', 'vite.config.ts', 'vite.config.js',\n '.eslintrc.json', '.eslintrc.js', '.eslintrc.yml',\n '.prettierrc.json', '.prettierrc.yml', '.prettierrc.js',\n 'jest.config.js', 'jest.config.ts',\n 'vitest.config.ts', 'vitest.config.js',\n 'tailwind.config.js', 'tailwind.config.ts',\n 'postcss.config.js',\n 'docker-compose.yml', 'docker-compose.yaml',\n 'Dockerfile',\n 'Makefile', 'CMakeLists.txt',\n 'pyproject.toml', 'poetry.toml', 'Pipfile',\n 'setup.cfg', 'setup.py', 'tox.ini',\n 'Cargo.toml', 'Cargo.lock',\n 'go.mod', 'go.sum',\n 'Gemfile', 'Gemfile.lock',\n 'composer.json', 'composer.lock',\n 'Podfile', 'Package.swift',\n '.gitignore', '.gitattributes',\n 'gradle.properties', 'build.gradle', 'build.gradle.kts',\n 'settings.gradle', 'settings.gradle.kts',\n }\n if basename in known_config_names:\n return True\n\n # Known config path patterns\n config_path_patterns = [\n r'\\.github/workflows/[^/]+\\.ya?ml

Code Review Skill Transform code reviews from gatekeeping to knowledge sharing through constructive feedback, systematic analysis, and collaborative improvement. When to Use This Skill - Reviewing pull requests and code changes - Establishing code review standards for teams - Mentoring junior developers through reviews - Conducting architecture reviews - Creating review checklists and guidelines - Improving team collaboration - Reducing code review cycle time - Maintaining code quality standards Core Principles 1. The Review Mindset Goals of Code Review: - Catch bugs and edge cases - Ensure c…

,\n r'\\.vscode/',\n r'\\.idea/',\n ]\n if any(re.search(p, filename) for p in config_path_patterns):\n return True\n\n # Files with \"config\" in the name (e.g. app.config.ts, database_config.yml)\n if re.search(r'config\\.', basename):\n return True\n\n # Files under a config/ directory\n if re.search(r'(?:^|/)config/', filename):\n return True\n\n return False\n\n\ndef parse_diff(diff_content: str) -> List[FileStats]:\n \"\"\"Parse git diff output and extract file statistics.\"\"\"\n files = []\n current_file = None\n\n for line in diff_content.split('\\n'):\n # New file header\n if line.startswith('diff --git'):\n if current_file:\n files.append(current_file)\n # \"diff --git a/\u003cpath> b/\u003cpath>\" — match the b/ side via a\n # backreference so a literal \"b/\" inside paths like lib/, web/ or\n # db/ can't be mistaken for the prefix. Renames have differing\n # paths, so fall back to the b/ side after the separating space.\n match = re.match(r'diff --git a/(.+?) b/\\1', line)\n if not match:\n match = re.search(r' b/(.+)

Code Review Skill Transform code reviews from gatekeeping to knowledge sharing through constructive feedback, systematic analysis, and collaborative improvement. When to Use This Skill - Reviewing pull requests and code changes - Establishing code review standards for teams - Mentoring junior developers through reviews - Conducting architecture reviews - Creating review checklists and guidelines - Improving team collaboration - Reducing code review cycle time - Maintaining code quality standards Core Principles 1. The Review Mindset Goals of Code Review: - Catch bugs and edge cases - Ensure c…

, line)\n if match:\n filename = match.group(1)\n current_file = FileStats(\n filename=filename,\n language=detect_language(filename),\n is_test=is_test_file(filename),\n is_config=is_config_file(filename),\n )\n else:\n current_file = None\n elif current_file:\n if line.startswith('+') and not line.startswith('+++'):\n current_file.additions += 1\n elif line.startswith('-') and not line.startswith('---'):\n current_file.deletions += 1\n\n if current_file:\n files.append(current_file)\n\n return files\n\n\ndef calculate_complexity(files: List[FileStats]) -> float:\n \"\"\"Calculate complexity score (0-1 scale).\"\"\"\n if not files:\n return 0.0\n\n total_changes = sum(f.additions + f.deletions for f in files)\n\n # Base complexity from size\n size_factor = min(total_changes / 1000, 1.0)\n\n # Factor for number of files\n file_factor = min(len(files) / 20, 1.0)\n\n # Factor for non-test code ratio\n test_lines = sum(f.additions + f.deletions for f in files if f.is_test)\n non_test_ratio = 1 - (test_lines / max(total_changes, 1))\n\n # Factor for language diversity\n languages = set(f.language for f in files if f.language != 'unknown')\n lang_factor = min(len(languages) / 5, 1.0)\n\n complexity = (\n size_factor * 0.4 +\n file_factor * 0.2 +\n non_test_ratio * 0.2 +\n lang_factor * 0.2\n )\n\n return round(complexity, 2)\n\n\ndef categorize_size(total_changes: int) -> str:\n \"\"\"Categorize PR size.\"\"\"\n if total_changes \u003c 50:\n return \"XS (Extra Small)\"\n elif total_changes \u003c 200:\n return \"S (Small)\"\n elif total_changes \u003c 400:\n return \"M (Medium)\"\n elif total_changes \u003c 800:\n return \"L (Large)\"\n else:\n return \"XL (Extra Large) - Consider splitting\"\n\n\ndef estimate_review_time(files: List[FileStats], complexity: float) -> int:\n \"\"\"Estimate review time in minutes.\"\"\"\n total_changes = sum(f.additions + f.deletions for f in files)\n\n # Base time: ~1 minute per 20 lines\n base_time = total_changes / 20\n\n # Adjust for complexity\n adjusted_time = base_time * (1 + complexity)\n\n # Minimum 5 minutes, maximum 120 minutes\n return max(5, min(120, int(adjusted_time)))\n\n\ndef identify_risk_factors(files: List[FileStats]) -> List[str]:\n \"\"\"Identify potential risk factors in the PR.\"\"\"\n risks = []\n\n total_changes = sum(f.additions + f.deletions for f in files)\n test_changes = sum(f.additions + f.deletions for f in files if f.is_test)\n\n if total_changes > 400:\n risks.append(\"Large PR (>400 lines) - harder to review thoroughly\")\n\n if test_changes == 0 and total_changes > 50:\n risks.append(f\"{RISK_NO_TESTS}: No test changes - verify test coverage\")\n\n if total_changes > 100 and test_changes / max(total_changes, 1) \u003c 0.2:\n risks.append(\"Low test ratio (\u003c20%) - consider adding more tests\")\n\n # Security-sensitive files\n security_patterns = ['.env', 'auth', 'security', 'password', 'token', 'secret']\n for f in files:\n if any(p in f.filename.lower() for p in security_patterns):\n risks.append(f\"Security-sensitive file: {f.filename}\")\n break\n\n # Database changes\n for f in files:\n if 'migration' in f.filename.lower() or f.language == 'SQL':\n risks.append(\"Database changes detected - review carefully\")\n break\n\n # Config changes\n config_files = [f for f in files if f.is_config]\n if config_files:\n risks.append(f\"Configuration changes in {len(config_files)} file(s)\")\n\n return risks\n\n\ndef generate_suggestions(files: List[FileStats], complexity: float, risks: List[str]) -> List[str]:\n \"\"\"Generate review suggestions.\"\"\"\n suggestions = []\n\n total_changes = sum(f.additions + f.deletions for f in files)\n\n if total_changes > 800:\n suggestions.append(\"Consider splitting this PR into smaller, focused changes\")\n\n if complexity > 0.7:\n suggestions.append(\"High complexity - allocate extra review time\")\n suggestions.append(\"Consider pair reviewing for critical sections\")\n\n if any(RISK_NO_TESTS in r for r in risks):\n suggestions.append(\"Request test additions before approval\")\n\n # Language-specific suggestions\n languages = set(f.language for f in files)\n if 'TypeScript' in languages or 'TypeScript/React' in languages:\n suggestions.append(\"Check for proper type usage (avoid 'any')\")\n if 'Rust' in languages:\n suggestions.append(\"Check for unwrap() usage and error handling\")\n if 'C' in languages or 'C++' in languages or 'C/C++' in languages:\n suggestions.append(\"Check for memory safety, bounds checks, and UB risks\")\n if 'SQL' in languages:\n suggestions.append(\"Review for SQL injection and query performance\")\n\n if not suggestions:\n suggestions.append(\"Standard review process should suffice\")\n\n return suggestions\n\n\ndef analyze_pr(diff_content: str) -> PRAnalysis:\n \"\"\"Perform complete PR analysis.\"\"\"\n files = parse_diff(diff_content)\n\n total_additions = sum(f.additions for f in files)\n total_deletions = sum(f.deletions for f in files)\n total_changes = total_additions + total_deletions\n\n complexity = calculate_complexity(files)\n risks = identify_risk_factors(files)\n suggestions = generate_suggestions(files, complexity, risks)\n\n return PRAnalysis(\n total_files=len(files),\n total_additions=total_additions,\n total_deletions=total_deletions,\n files=files,\n complexity_score=complexity,\n size_category=categorize_size(total_changes),\n estimated_review_time=estimate_review_time(files, complexity),\n risk_factors=risks,\n suggestions=suggestions,\n )\n\n\ndef print_analysis(analysis: PRAnalysis, show_files: bool = False):\n \"\"\"Print analysis results.\"\"\"\n print(\"\\n\" + \"=\" * 60)\n print(\"PR ANALYSIS REPORT\")\n print(\"=\" * 60)\n\n print(f\"\\n📊 SUMMARY\")\n print(f\" Files changed: {analysis.total_files}\")\n print(f\" Additions: +{analysis.total_additions}\")\n print(f\" Deletions: -{analysis.total_deletions}\")\n print(f\" Total changes: {analysis.total_additions + analysis.total_deletions}\")\n\n print(f\"\\n📏 SIZE: {analysis.size_category}\")\n print(f\" Complexity score: {analysis.complexity_score}/1.0\")\n print(f\" Estimated review time: ~{analysis.estimated_review_time} minutes\")\n\n if analysis.risk_factors:\n print(f\"\\n⚠️ RISK FACTORS:\")\n for risk in analysis.risk_factors:\n print(f\" • {risk}\")\n\n print(f\"\\n💡 SUGGESTIONS:\")\n for suggestion in analysis.suggestions:\n print(f\" • {suggestion}\")\n\n if show_files:\n print(f\"\\n📁 FILES:\")\n # Group by language\n by_lang: Dict[str, List[FileStats]] = defaultdict(list)\n for f in analysis.files:\n by_lang[f.language].append(f)\n\n for lang, lang_files in sorted(by_lang.items()):\n print(f\"\\n [{lang}]\")\n for f in lang_files:\n prefix = \"🧪\" if f.is_test else \"⚙️\" if f.is_config else \"📄\"\n print(f\" {prefix} {f.filename} (+{f.additions}/-{f.deletions})\")\n\n print(\"\\n\" + \"=\" * 60)\n\n\ndef main():\n parser = argparse.ArgumentParser(description='Analyze PR complexity')\n parser.add_argument('--diff-file', '-f', help='Path to diff file')\n parser.add_argument('--stats', '-s', action='store_true', help='Show file details')\n args = parser.parse_args()\n\n # Read diff from file or stdin\n try:\n if args.diff_file:\n with open(args.diff_file, 'r', encoding='utf-8', errors='replace') as f:\n diff_content = f.read()\n elif not sys.stdin.isatty():\n diff_content = sys.stdin.buffer.read().decode('utf-8', errors='replace')\n else:\n print(\"Usage: git diff main...HEAD | python pr-analyzer.py\")\n print(\" python pr-analyzer.py -f diff.txt\")\n sys.exit(1)\n except OSError as e:\n print(f\"Error reading diff input: {e}\", file=sys.stderr)\n sys.exit(1)\n\n if not diff_content.strip():\n print(\"No diff content provided\")\n sys.exit(1)\n\n analysis = analyze_pr(diff_content)\n print_analysis(analysis, show_files=args.stats)\n\n\nif __name__ == '__main__':\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":14214,"content_sha256":"cdaadc3f937f1c6a6fb4a5920904f3af4a88bee07032c997e20e1be0e8ef48c8"},{"filename":"scripts/test_pr_analyzer.py","content":"#!/usr/bin/env python3\n\"\"\"Tests for pr-analyzer.py (stdlib unittest, no extra deps).\"\"\"\n\nimport importlib.util\nimport os\nimport unittest\n\n# The script has a hyphen in its name, so load it by path.\n_HERE = os.path.dirname(os.path.abspath(__file__))\n_spec = importlib.util.spec_from_file_location(\n 'pr_analyzer', os.path.join(_HERE, 'pr-analyzer.py')\n)\npr_analyzer = importlib.util.module_from_spec(_spec)\n_spec.loader.exec_module(pr_analyzer)\n\n# Convenient aliases\nFileStats = pr_analyzer.FileStats\n\n\n# ═══════════════════════════════════════════════════════════════\n# parse_diff — filename extraction (existing tests)\n# ═══════════════════════════════════════════════════════════════\n\nclass ParseDiffFilenameTest(unittest.TestCase):\n def test_lib_prefixed_path(self):\n # \"lib/\" embeds a literal \"b/\" that the old regex swallowed.\n diff = (\n \"diff --git a/lib/foo.py b/lib/foo.py\\n\"\n \"index 1234567..89abcde 100644\\n\"\n \"--- a/lib/foo.py\\n\"\n \"+++ b/lib/foo.py\\n\"\n \"@@ -1,2 +1,3 @@\\n\"\n \" unchanged\\n\"\n \"+added line\\n\"\n \"-removed line\\n\"\n )\n files = pr_analyzer.parse_diff(diff)\n self.assertEqual(len(files), 1)\n self.assertEqual(files[0].filename, 'lib/foo.py')\n self.assertEqual(files[0].additions, 1)\n self.assertEqual(files[0].deletions, 1)\n\n def test_normal_path(self):\n diff = (\n \"diff --git a/src/main.py b/src/main.py\\n\"\n \"index 1111111..2222222 100644\\n\"\n \"--- a/src/main.py\\n\"\n \"+++ b/src/main.py\\n\"\n \"@@ -0,0 +1 @@\\n\"\n \"+print('hi')\\n\"\n )\n files = pr_analyzer.parse_diff(diff)\n self.assertEqual(len(files), 1)\n self.assertEqual(files[0].filename, 'src/main.py')\n\n def test_other_embedded_b_slash_prefixes(self):\n # web/ and db/ also contain a literal \"b/\".\n diff = (\n \"diff --git a/web/x.js b/web/x.js\\n\"\n \"+++ b/web/x.js\\n\"\n \"+console.log(1)\\n\"\n \"diff --git a/db/y.sql b/db/y.sql\\n\"\n \"+++ b/db/y.sql\\n\"\n \"+SELECT 1;\\n\"\n )\n files = pr_analyzer.parse_diff(diff)\n self.assertEqual([f.filename for f in files], ['web/x.js', 'db/y.sql'])\n\n def test_rename_falls_back_to_b_side(self):\n diff = (\n \"diff --git a/old/name.py b/new/name.py\\n\"\n \"similarity index 100%\\n\"\n \"rename from old/name.py\\n\"\n \"rename to new/name.py\\n\"\n )\n files = pr_analyzer.parse_diff(diff)\n self.assertEqual(len(files), 1)\n self.assertEqual(files[0].filename, 'new/name.py')\n\n\n# ═══════════════════════════════════════════════════════════════\n# detect_language\n# ═══════════════════════════════════════════════════════════════\n\nclass DetectLanguageTest(unittest.TestCase):\n def test_common_extensions(self):\n cases = {\n 'app.py': 'Python',\n 'index.ts': 'TypeScript',\n 'main.rs': 'Rust',\n 'handler.go': 'Go',\n 'App.java': 'Java',\n 'Activity.kt': 'Kotlin',\n 'ViewController.swift': 'Swift',\n 'app.tsx': 'TypeScript/React',\n 'style.css': 'CSS',\n 'query.sql': 'SQL',\n }\n for filename, expected in cases.items():\n with self.subTest(filename=filename):\n self.assertEqual(pr_analyzer.detect_language(filename), expected)\n\n def test_unknown_extension(self):\n self.assertEqual(pr_analyzer.detect_language('data.xyz'), 'unknown')\n self.assertEqual(pr_analyzer.detect_language('Makefile'), 'unknown')\n\n def test_cpp_variants(self):\n for ext in ('.cpp', '.hpp', '.cc', '.cxx', '.hh', '.hxx'):\n with self.subTest(ext=ext):\n self.assertEqual(pr_analyzer.detect_language(f'file{ext}'), 'C++')\n\n\n# ═══════════════════════════════════════════════════════════════\n# is_test_file\n# ═══════════════════════════════════════════════════════════════\n\nclass IsTestFileTest(unittest.TestCase):\n def test_python_test_prefix(self):\n self.assertTrue(pr_analyzer.is_test_file('tests/test_handler.py'))\n self.assertTrue(pr_analyzer.is_test_file('test_utils.py'))\n\n def test_python_test_suffix(self):\n self.assertTrue(pr_analyzer.is_test_file('handler_test.py'))\n\n def test_rust_test_suffix(self):\n self.assertTrue(pr_analyzer.is_test_file('src/my_module_test.rs'))\n self.assertTrue(pr_analyzer.is_test_file('parser_test.rs'))\n\n def test_go_test_suffix(self):\n self.assertTrue(pr_analyzer.is_test_file('handler_test.go'))\n self.assertTrue(pr_analyzer.is_test_file('pkg/auth_test.go'))\n\n def test_js_ts_test_and_spec(self):\n self.assertTrue(pr_analyzer.is_test_file('handler.test.ts'))\n self.assertTrue(pr_analyzer.is_test_file('utils.spec.js'))\n self.assertTrue(pr_analyzer.is_test_file('App.test.tsx'))\n\n def test_tests_directory(self):\n self.assertTrue(pr_analyzer.is_test_file('tests/conftest.py'))\n self.assertTrue(pr_analyzer.is_test_file('test/helpers.js'))\n\n def test_dunder_tests_directory(self):\n self.assertTrue(pr_analyzer.is_test_file('__tests__/Button.test.tsx'))\n\n def test_non_test_files_rejected(self):\n self.assertFalse(pr_analyzer.is_test_file('handler.go'))\n self.assertFalse(pr_analyzer.is_test_file('module.rs'))\n self.assertFalse(pr_analyzer.is_test_file('src/utils.py'))\n self.assertFalse(pr_analyzer.is_test_file('lib/parser.js'))\n self.assertFalse(pr_analyzer.is_test_file('contest.py'))\n\n def test_test_substring_not_matched(self):\n \"\"\"Files containing 'test_' as substring must NOT be flagged.\"\"\"\n self.assertFalse(pr_analyzer.is_test_file('latest_report.py'))\n self.assertFalse(pr_analyzer.is_test_file('contest_utils.py'))\n self.assertFalse(pr_analyzer.is_test_file('src/latest_handler.py'))\n\n\n# ═══════════════════════════════════════════════════════════════\n# is_config_file\n# ═══════════════════════════════════════════════════════════════\n\nclass IsConfigFileTest(unittest.TestCase):\n def test_known_json_configs(self):\n self.assertTrue(pr_analyzer.is_config_file('package.json'))\n self.assertTrue(pr_analyzer.is_config_file('tsconfig.json'))\n self.assertTrue(pr_analyzer.is_config_file('.eslintrc.json'))\n\n def test_known_yaml_configs(self):\n self.assertTrue(pr_analyzer.is_config_file('docker-compose.yml'))\n self.assertTrue(pr_analyzer.is_config_file('.github/workflows/ci.yml'))\n self.assertTrue(pr_analyzer.is_config_file('.prettierrc.yml'))\n\n def test_known_toml_configs(self):\n self.assertTrue(pr_analyzer.is_config_file('Cargo.toml'))\n self.assertTrue(pr_analyzer.is_config_file('pyproject.toml'))\n\n def test_env_files(self):\n self.assertTrue(pr_analyzer.is_config_file('.env'))\n self.assertTrue(pr_analyzer.is_config_file('.env.local'))\n self.assertTrue(pr_analyzer.is_config_file('.env.production'))\n\n def test_config_in_filename(self):\n self.assertTrue(pr_analyzer.is_config_file('app.config.ts'))\n self.assertTrue(pr_analyzer.is_config_file('database_config.yml'))\n\n def test_data_files_rejected(self):\n \"\"\"Data files must NOT be flagged as config.\"\"\"\n self.assertFalse(pr_analyzer.is_config_file('data.json'))\n self.assertFalse(pr_analyzer.is_config_file('openapi.yaml'))\n self.assertFalse(pr_analyzer.is_config_file('swagger.json'))\n self.assertFalse(pr_analyzer.is_config_file('fixtures/sample.yml'))\n self.assertFalse(pr_analyzer.is_config_file('translations.json'))\n self.assertFalse(pr_analyzer.is_config_file('schema.toml'))\n\n def test_config_directory(self):\n self.assertTrue(pr_analyzer.is_config_file('config/settings.yaml'))\n self.assertTrue(pr_analyzer.is_config_file('config/database.yml'))\n self.assertTrue(pr_analyzer.is_config_file('src/config/settings.json'))\n\n def test_source_files_rejected(self):\n self.assertFalse(pr_analyzer.is_config_file('src/index.ts'))\n self.assertFalse(pr_analyzer.is_config_file('lib/utils.py'))\n\n\n# ═══════════════════════════════════════════════════════════════\n# calculate_complexity\n# ═══════════════════════════════════════════════════════════════\n\nclass CalculateComplexityTest(unittest.TestCase):\n def test_empty_files(self):\n self.assertEqual(pr_analyzer.calculate_complexity([]), 0.0)\n\n def test_small_simple_change(self):\n files = [FileStats(filename='app.py', additions=5, deletions=2,\n language='Python')]\n score = pr_analyzer.calculate_complexity(files)\n self.assertLess(score, 0.3)\n\n def test_large_multi_language_change(self):\n files = [\n FileStats(filename='app.py', additions=300, deletions=100, language='Python'),\n FileStats(filename='main.rs', additions=200, deletions=50, language='Rust'),\n FileStats(filename='index.ts', additions=150, deletions=80, language='TypeScript'),\n FileStats(filename='handler.go', additions=100, deletions=30, language='Go'),\n FileStats(filename='App.tsx', additions=50, deletions=20, language='TypeScript/React'),\n ]\n score = pr_analyzer.calculate_complexity(files)\n self.assertGreater(score, 0.5)\n\n def test_test_heavy_change_is_lower(self):\n \"\"\"Changes with high test ratio should have lower complexity.\"\"\"\n prod_files = [FileStats(filename='app.py', additions=100, deletions=50,\n language='Python')]\n test_files = [\n FileStats(filename='app.py', additions=100, deletions=50,\n language='Python'),\n FileStats(filename='tests/test_app.py', additions=100, deletions=0,\n language='Python', is_test=True),\n ]\n score_prod = pr_analyzer.calculate_complexity(prod_files)\n score_test = pr_analyzer.calculate_complexity(test_files)\n self.assertLess(score_test, score_prod)\n\n\n# ═══════════════════════════════════════════════════════════════\n# identify_risk_factors\n# ═══════════════════════════════════════════════════════════════\n\nclass IdentifyRiskFactorsTest(unittest.TestCase):\n def test_large_pr_flagged(self):\n files = [FileStats(filename='big.py', additions=300, deletions=200,\n language='Python')]\n risks = pr_analyzer.identify_risk_factors(files)\n self.assertTrue(any('Large PR' in r for r in risks))\n\n def test_no_tests_flagged(self):\n files = [FileStats(filename='app.py', additions=40, deletions=20,\n language='Python')]\n risks = pr_analyzer.identify_risk_factors(files)\n self.assertTrue(any(pr_analyzer.RISK_NO_TESTS in r for r in risks))\n\n def test_with_tests_not_flagged(self):\n files = [\n FileStats(filename='app.py', additions=40, deletions=20,\n language='Python'),\n FileStats(filename='tests/test_app.py', additions=30, deletions=0,\n language='Python', is_test=True),\n ]\n risks = pr_analyzer.identify_risk_factors(files)\n self.assertFalse(any(pr_analyzer.RISK_NO_TESTS in r for r in risks))\n\n def test_security_sensitive_file(self):\n files = [FileStats(filename='src/auth/login.py', additions=10, deletions=5,\n language='Python')]\n risks = pr_analyzer.identify_risk_factors(files)\n self.assertTrue(any('Security-sensitive' in r for r in risks))\n\n def test_database_migration(self):\n files = [FileStats(filename='migrations/001_init.sql', additions=20, deletions=0,\n language='SQL')]\n risks = pr_analyzer.identify_risk_factors(files)\n self.assertTrue(any('Database' in r for r in risks))\n\n def test_test_substring_files_still_flag_no_tests(self):\n \"\"\"Files like latest_report.py must not suppress NO_TEST_CHANGES.\"\"\"\n files = [\n FileStats(filename='latest_report.py', additions=40, deletions=20,\n language='Python'),\n FileStats(filename='contest_utils.py', additions=30, deletions=10,\n language='Python'),\n ]\n risks = pr_analyzer.identify_risk_factors(files)\n self.assertTrue(any(pr_analyzer.RISK_NO_TESTS in r for r in risks))\n\n\n# ═══════════════════════════════════════════════════════════════\n# generate_suggestions\n# ═══════════════════════════════════════════════════════════════\n\nclass GenerateSuggestionsTest(unittest.TestCase):\n def test_returns_list(self):\n files = [FileStats(filename='app.py', additions=10, deletions=5,\n language='Python')]\n result = pr_analyzer.generate_suggestions(files, 0.1, [])\n self.assertIsInstance(result, list)\n self.assertGreater(len(result), 0)\n\n def test_large_pr_split_suggestion(self):\n files = [FileStats(filename='big.py', additions=600, deletions=300,\n language='Python')]\n result = pr_analyzer.generate_suggestions(files, 0.3, [])\n self.assertTrue(any('splitting' in s.lower() for s in result))\n\n def test_no_tests_suggestion(self):\n files = [FileStats(filename='app.py', additions=40, deletions=20,\n language='Python')]\n risks = [f\"{pr_analyzer.RISK_NO_TESTS}: no tests\"]\n result = pr_analyzer.generate_suggestions(files, 0.2, risks)\n self.assertTrue(any('test' in s.lower() for s in result))\n\n def test_rust_suggestion(self):\n files = [FileStats(filename='lib.rs', additions=10, deletions=5,\n language='Rust')]\n result = pr_analyzer.generate_suggestions(files, 0.1, [])\n self.assertTrue(any('unwrap' in s.lower() for s in result))\n\n\n# ═══════════════════════════════════════════════════════════════\n# analyze_pr — end-to-end integration\n# ═══════════════════════════════════════════════════════════════\n\nclass AnalyzePRTest(unittest.TestCase):\n def test_end_to_end(self):\n diff = (\n \"diff --git a/src/app.py b/src/app.py\\n\"\n \"index 1111111..2222222 100644\\n\"\n \"--- a/src/app.py\\n\"\n \"+++ b/src/app.py\\n\"\n \"@@ -1,3 +1,5 @@\\n\"\n \" import os\\n\"\n \"+import sys\\n\"\n \"+import json\\n\"\n \" def main():\\n\"\n \"+ print('hello')\\n\"\n \"+ return 0\\n\"\n \"diff --git a/tests/test_app.py b/tests/test_app.py\\n\"\n \"new file mode 100644\\n\"\n \"--- /dev/null\\n\"\n \"+++ b/tests/test_app.py\\n\"\n \"@@ -0,0 +1,3 @@\\n\"\n \"+from app import main\\n\"\n \"+def test_main():\\n\"\n \"+ assert main() == 0\\n\"\n )\n analysis = pr_analyzer.analyze_pr(diff)\n\n self.assertEqual(analysis.total_files, 2)\n self.assertEqual(analysis.total_additions, 7) # 4 + 3\n self.assertEqual(analysis.total_deletions, 0)\n self.assertGreater(len(analysis.suggestions), 0)\n self.assertIn('XS (Extra Small)', analysis.size_category)\n\n # Verify test file was detected\n test_file = [f for f in analysis.files if 'test' in f.filename][0]\n self.assertTrue(test_file.is_test)\n\n # Verify no \"NO_TEST_CHANGES\" risk since tests are present\n self.assertFalse(\n any(pr_analyzer.RISK_NO_TESTS in r for r in analysis.risk_factors)\n )\n\n def test_empty_diff(self):\n analysis = pr_analyzer.analyze_pr(\"\")\n self.assertEqual(analysis.total_files, 0)\n self.assertEqual(analysis.complexity_score, 0.0)\n\n\nif __name__ == '__main__':\n unittest.main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":17910,"content_sha256":"c2f2204f369c7bdd8978ded74a2159312e1b72f64dc57cef0767c2f489d69aba"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Code Review Skill","type":"text"}]},{"type":"paragraph","content":[{"text":"Transform code reviews from gatekeeping to knowledge sharing through constructive feedback, systematic analysis, and collaborative improvement.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Use This Skill","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reviewing pull requests and code changes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Establishing code review standards for teams","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Mentoring junior developers through reviews","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Conducting architecture reviews","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Creating review checklists and guidelines","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Improving team collaboration","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reducing code review cycle time","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Maintaining code quality standards","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core Principles","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1. The Review Mindset","type":"text"}]},{"type":"paragraph","content":[{"text":"Goals of Code Review:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Catch bugs and edge cases","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ensure code maintainability","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Share knowledge across team","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Enforce coding standards","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Improve design and architecture","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Build team culture","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Not the Goals:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Show off knowledge","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Nitpick formatting (use linters)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Block progress unnecessarily","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Rewrite to your preference","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2. Effective Feedback","type":"text"}]},{"type":"paragraph","content":[{"text":"Good Feedback is:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Specific and actionable","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Educational, not judgmental","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Focused on the code, not the person","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Balanced (praise good work too)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prioritized (critical vs nice-to-have)","type":"text"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"❌ Bad: \"This is wrong.\"\n✅ Good: \"This could cause a race condition when multiple users\n access simultaneously. Consider using a mutex here.\"\n\n❌ Bad: \"Why didn't you use X pattern?\"\n✅ Good: \"Have you considered the Repository pattern? It would\n make this easier to test. Here's an example: [link]\"\n\n❌ Bad: \"Rename this variable.\"\n✅ Good: \"[nit] Consider `userCount` instead of `uc` for\n clarity. Not blocking if you prefer to keep it.\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3. Review Scope","type":"text"}]},{"type":"paragraph","content":[{"text":"What to Review:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Logic correctness and edge cases","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Security vulnerabilities","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Performance implications","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Test coverage and quality","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Error handling","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Documentation and comments","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"API design and naming","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Architectural fit","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"What Not to Review Manually:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Code formatting (use Prettier, Black, etc.)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Import organization","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Linting violations","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Simple typos","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Review Process","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 1: Context Gathering (2-3 minutes)","type":"text"}]},{"type":"paragraph","content":[{"text":"Before diving into code, understand:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read PR description and linked issue","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check PR size (>400 lines? Ask to split)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Review CI/CD status (tests passing?)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Understand the business requirement","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Note any relevant architectural decisions","type":"text"}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"For large diffs, pipe the diff through ","type":"text"},{"text":"scripts/pr-analyzer.py","type":"text","marks":[{"type":"link","attrs":{"href":"scripts/pr-analyzer.py","title":null}},{"type":"code_inline"}]},{"text":" (","type":"text"},{"text":"git diff main...HEAD | python scripts/pr-analyzer.py","type":"text","marks":[{"type":"code_inline"}]},{"text":") to triage complexity and get a suggested review approach before reading.","type":"text"}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 2: High-Level Review (5-10 minutes)","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Architecture & Design","type":"text","marks":[{"type":"strong"}]},{"text":" - Does the solution fit the problem?","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For significant changes, consult ","type":"text"},{"text":"Architecture Review Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/architecture-review-guide.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check: SOLID principles, coupling/cohesion, anti-patterns","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Performance Assessment","type":"text","marks":[{"type":"strong"}]},{"text":" - Are there performance concerns?","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For performance-critical code, consult ","type":"text"},{"text":"Performance Review Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/performance-review-guide.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check: Algorithm complexity, N+1 queries, memory usage","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"File Organization","type":"text","marks":[{"type":"strong"}]},{"text":" - Are new files in the right places?","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Testing Strategy","type":"text","marks":[{"type":"strong"}]},{"text":" - Are there tests covering edge cases?","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 3: Line-by-Line Review (10-20 minutes)","type":"text"}]},{"type":"paragraph","content":[{"text":"For each file, check:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Logic & Correctness","type":"text","marks":[{"type":"strong"}]},{"text":" - Edge cases, off-by-one, null checks, race conditions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Security","type":"text","marks":[{"type":"strong"}]},{"text":" - Input validation, injection risks, XSS, sensitive data","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Performance","type":"text","marks":[{"type":"strong"}]},{"text":" - N+1 queries, unnecessary loops, memory leaks","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Maintainability","type":"text","marks":[{"type":"strong"}]},{"text":" - Clear names, single responsibility, comments","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reuse","type":"text","marks":[{"type":"strong"}]},{"text":" - Before accepting new code, search for existing utilities/helpers that could replace it. Check adjacent files and shared modules for similar patterns. See ","type":"text"},{"text":"Universal Quality Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/code-quality-universal.md","title":null}}]},{"text":" for anti-patterns like parameter sprawl, leaky abstractions, nested conditionals, stringly-typed code, TOCTOU, and no-op updates.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 4: Summary & Decision (2-3 minutes)","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Summarize key concerns","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Highlight what you liked","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Make clear decision:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Approve","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"💬 Comment (minor suggestions)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"🔄 Request Changes (must address)","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Offer to pair if complex","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Review Techniques","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Technique 1: The Checklist Method","type":"text"}]},{"type":"paragraph","content":[{"text":"Use checklists for consistent reviews. See ","type":"text"},{"text":"Security Review Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/security-review-guide.md","title":null}}]},{"text":" for comprehensive security checklist.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Technique 2: The Question Approach","type":"text"}]},{"type":"paragraph","content":[{"text":"Instead of stating problems, ask questions:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"❌ \"This will fail if the list is empty.\"\n✅ \"What happens if `items` is an empty array?\"\n\n❌ \"You need error handling here.\"\n✅ \"How should this behave if the API call fails?\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Technique 3: Suggest, Don't Command","type":"text"}]},{"type":"paragraph","content":[{"text":"Use collaborative language:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"❌ \"You must change this to use async/await\"\n✅ \"Suggestion: async/await might make this more readable. What do you think?\"\n\n❌ \"Extract this into a function\"\n✅ \"This logic appears in 3 places. Would it make sense to extract it?\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Technique 4: Differentiate Severity","type":"text"}]},{"type":"paragraph","content":[{"text":"Use labels to indicate priority:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"🔴 ","type":"text"},{"text":"[blocking]","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Must fix before merge","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"🟡 ","type":"text"},{"text":"[important]","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Should fix, discuss if disagree","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"🟢 ","type":"text"},{"text":"[nit]","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Nice to have, not blocking","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"💡 ","type":"text"},{"text":"[suggestion]","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Alternative approach to consider","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"📚 ","type":"text"},{"text":"[learning]","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Educational comment, no action needed","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"🎉 ","type":"text"},{"text":"[praise]","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Good work, keep it up!","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Severity levels:","type":"text","marks":[{"type":"strong"}]},{"text":" 🔴 / 🟡 / 🟢 are the three severity tiers used as the standard across all guides in this skill — 🔴 blocks the merge, 🟡 should be addressed, 🟢 is optional. The remaining markers (💡 / 📚 / 🎉) are non-blocking annotations.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Language-Specific Guides","type":"text"}]},{"type":"paragraph","content":[{"text":"根据审查的代码语言,查阅对应的详细指南:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Language/Framework","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reference File","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Key Topics","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"React","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"React Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/react.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Hooks, useEffect, React 19 Actions, RSC, Suspense, TanStack Query v5","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Vue 3","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Vue Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/vue.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Composition API, 响应性系统, Props/Emits, Watchers, Composables","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Angular 17+","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Angular Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/angular.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Signals, Standalone, RxJS, Zoneless, 模板优化, 测试, 路由守卫, HttpInterceptor","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Rust","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Rust Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/rust.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"所有权/借用, Unsafe 审查, 异步代码, 取消安全性, 错误处理","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TypeScript","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TypeScript Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/typescript.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"类型安全, async/await, 不可变性, 测试, 模块解析, TS 5.x","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Python","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Python Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/python.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"可变默认参数, 异常处理, 类属性","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Django / DRF","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Django Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/django.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"安全审查, N+1 查询, Serializer 反模式, ViewSet, 异步视图","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"FastAPI","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"FastAPI Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/fastapi.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Depends, Pydantic v2 validation, async correctness, sessions/N+1, auth vs authorization, test-driven verification","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Java","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Java Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/java.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Java 17/21 新特性, Spring Boot 3, 虚拟线程, Stream/Optional","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Java 8 / Legacy","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Java 8 Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/java8.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Java 8, Spring Boot 2, javax.*, Stream/Optional, java.time, CompletableFuture","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"PHP","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"PHP Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/php.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"PHP 8.x type system, PDO, security review, Composer, PHPUnit/PHPStan","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Ruby / Rails","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Ruby Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/ruby.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Ruby semantics, Rails 8, Active Record, Active Job, security, testing","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"C# / .NET","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"C# Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/csharp.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"C# 12 特性, 异步编程, EF Core 性能, ASP.NET Core, LINQ","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Go","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Go Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/go.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"错误处理, goroutine/channel, context, 接口设计","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Kotlin / Android","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Kotlin Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/kotlin.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"协程, Flow, Jetpack Compose, 空安全, 内存泄漏, 架构模式","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Swift / SwiftUI","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Swift Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/swift.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Optionals, Swift Concurrency, Sendable/actors, SwiftUI property wrappers, value vs reference types, API design","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Dart / Flutter","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Dart Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/dart.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Widget rebuilds, const constructors, null safety, isolates, async in build, Riverpod/Bloc, platform channels, keys, disposal","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"NestJS","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"NestJS Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/nestjs.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"依赖注入, 分层架构, DTO 验证, Guard/Interceptor, 循环依赖","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Svelte / SvelteKit","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Svelte Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/svelte.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Runes, Load 函数, Form Actions, Store 迁移, SSR/CSR 边界","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"C","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"C Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/c.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"指针/缓冲区, 内存安全, UB, 安全编码, 可移植性, 测试","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"C++","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"C++ Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/cpp.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"RAII, 智能指针, C++20/23, constexpr, 测试","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Zig","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Zig Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/zig.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Allocators, error unions, defer/errdefer, comptime, C interop","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CSS/Less/Sass","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CSS Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/css-less-sass.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"变量规范, !important, 性能优化, 响应式, 兼容性","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Qt","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Qt Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/qt.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"对象模型, 信号/槽, Model/View, QML, Qt6 迁移, 测试","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Cross-Cutting Guides","type":"text"}]},{"type":"paragraph","content":[{"text":"Language-agnostic patterns applicable to all code reviews:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Topic","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reference File","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Key Topics","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Architecture Review","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Architecture Review Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/architecture-review-guide.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SOLID, anti-patterns, coupling/cohesion, dependency direction","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Performance Review","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Performance Review Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/performance-review-guide.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Web Vitals, N+1, algorithm complexity, memory leaks, caching","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Security Review","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Security Review Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/security-review-guide.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SQLi, XSS, CSRF, SSRF, IDOR, 命令注入, 跨语言示例","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Universal Quality","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Universal Quality Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/code-quality-universal.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reuse audit, parameter sprawl, leaky abstractions, nested conditionals, stringly-typed code, TOCTOU, no-op updates, redundant state","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Common Bugs","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Common Bugs Checklist","type":"text","marks":[{"type":"link","attrs":{"href":"reference/common-bugs-checklist.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Language-specific bug patterns, common pitfalls","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SQL Injection Prevention","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SQL Injection Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/cross-cutting/sql-injection-prevention.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Parameterized queries, ORM safety, 6 languages, dynamic identifiers, detection","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"XSS Prevention","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"XSS Prevention Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/cross-cutting/xss-prevention.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Output encoding, CSP, 5 frameworks, input validation vs encoding, detection","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"N+1 Queries","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"N+1 Queries Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/cross-cutting/n-plus-one-queries.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Eager loading, batch fetching, DataLoader, 5 languages, detection","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Error Handling","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Error Handling Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/cross-cutting/error-handling-principles.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fail fast, error hierarchy, 7 languages, anti-patterns, logging","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Async & Concurrency","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Concurrency Guide","type":"text","marks":[{"type":"link","attrs":{"href":"reference/cross-cutting/async-concurrency-patterns.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Goroutines, async/await, actors, structured concurrency, 7 languages","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Review Best Practices","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Code Review Best Practices","type":"text","marks":[{"type":"link","attrs":{"href":"reference/code-review-best-practices.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Communication, reviewer mindset, giving feedback, severity labels","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Additional Resources","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"PR Review Template","type":"text","marks":[{"type":"link","attrs":{"href":"assets/pr-review-template.md","title":null}}]},{"text":" - PR 审查评论模板","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Review Checklist","type":"text","marks":[{"type":"link","attrs":{"href":"assets/review-checklist.md","title":null}}]},{"text":" - 快速参考清单","type":"text"}]}]}]}]},"metadata":{"date":"2026-11-02","name":"code-review-skill","author":"@skillopedia","source":{"stars":2179,"repo_name":"code-review-skill","origin_url":"https://github.com/awesome-skills/code-review-skill/blob/HEAD/SKILL.md","repo_owner":"awesome-skills","body_sha256":"69a5d837bbf641bcf9fbc0d25b6bb1dfd90b04efbd8de1c3dcd9e2b508171a63","cluster_key":"0d41481bba456c111579e710e7f63cdd2d8313f2384b8ac3f74832bf73aabd0f","clean_bundle":{"format":"clean-skill-bundle-v1","source":"awesome-skills/code-review-skill/SKILL.md","text_bytes":687978,"source_commit":"4850184dfd7765ea7b3c5c4e9b6cf8e0815ee7cb","skills_sh_rank":5036,"attachment_count":39,"text_attachments":39,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"software-engineering","category_label":"Engineering"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"software-engineering","import_tag":"skills-sh-top20k-2026-11","description":"Provides comprehensive code review guidance for React 19, Vue 3, Angular 17+, Svelte 5,\nRust, TypeScript, Java, Java 8, PHP, Ruby, Rails, Python, Django, FastAPI, Go, C#/.NET, Kotlin, Swift,\nDart, Flutter, NestJS, C/C++, Zig, CSS/Less/Sass, Qt, and more.\nCovers architecture review, performance review, security audit, code quality anti-patterns,\nand common bugs across all ecosystems.\nUse when: reviewing pull requests, conducting PR reviews, code review, reviewing code changes,\nestablishing review standards, mentoring developers, architecture reviews, security audits,\nperformance reviews, checking code quality, finding bugs, giving feedback on code.\n","allowed-tools":["Read","Grep","Glob","Bash","WebFetch"]}},"renderedAt":1793665981455}

Code Review Skill Transform code reviews from gatekeeping to knowledge sharing through constructive feedback, systematic analysis, and collaborative improvement. When to Use This Skill - Reviewing pull requests and code changes - Establishing code review standards for teams - Mentoring junior developers through reviews - Conducting architecture reviews - Creating review checklists and guidelines - Improving team collaboration - Reducing code review cycle time - Maintaining code quality standards Core Principles 1. The Review Mindset Goals of Code Review: - Catch bugs and edge cases - Ensure c…