## Purpose 定义项目代码质量门禁、格式化检查、快速检查和完整验证命令的行为要求,确保开发者可以通过文档化命令稳定验证源码质量、基础测试和生产构建。 ## Requirements ### Requirement: ESLint 代码质量门禁 项目 SHALL 提供 ESLint 代码质量门禁,用于审查 TypeScript、React 前端、脚本和测试代码中的质量问题。ESLint 配置 SHALL 包括 `@eslint/js` recommended 规则、`typescript-eslint` recommended-type-checked 和 stylistic-type-checked 规则、`eslint-plugin-perfectionist` 导入排序规则、`eslint-plugin-import` 导入验证规则,以及精选的单项类型安全和风格规则。 #### Scenario: 运行 lint 检查 - **WHEN** 开发者运行文档化的 lint 命令 - **THEN** 系统 SHALL 使用 ESLint 检查项目源码、脚本和测试代码,并在发现违规时以非零状态退出 #### Scenario: 检查 React Hooks 规则 - **WHEN** 前端 React 代码违反 Hooks 调用规则 - **THEN** lint 命令 MUST 失败并报告对应违规 #### Scenario: 保护前后端边界 - **WHEN** `src/web` 前端代码导入 `src/server` 后端运行时实现 - **THEN** lint 命令 MUST 失败并报告前后端边界违规 #### Scenario: 检测类型安全违规 - **WHEN** 代码中存在浮动的 Promise 未 await、any 类型泄漏到明确类型位置、模板字符串中包含非字符串化对象等类型安全隐患 - **THEN** lint 命令 MUST 失败并报告对应 `@typescript-eslint` 规则违规 #### Scenario: 检测导入路径错误 - **WHEN** 代码中导入路径指向不存在的文件或已废弃的路径 - **THEN** lint 命令 MUST 失败并报告 `import/no-unresolved` 或 `import/no-deprecated` 错误 #### Scenario: 排除第三方模板目录 - **WHEN** ESLint 运行检查 - **THEN** 系统 MUST 排除 `.agents/` 等第三方模板目录,不检查其中的代码 #### Scenario: 排除生成产物和锁文件 - **WHEN** ESLint 运行检查 - **THEN** 系统 MUST 排除 `dist/`、`.build/`、`node_modules/`、`openspec/`、`.opencode/`、`.claude/`、`.codex/`、`*.bun-build`、`bun.lock`、`data/` 等非源码目录 ### Requirement: Prettier 代码格式门禁 项目 SHALL 通过 `eslint-plugin-prettier` 将 Prettier 格式检查集成为 ESLint 规则,使 `lint` 命令同时覆盖代码质量和格式检查(原独立的 `format:check` 命令不再存在,格式检查统一通过 `lint` 完成)。项目仍保留独立的 `format` 命令(`prettier --write`)用于快速格式化。Prettier 配置 SHALL 显式声明 `printWidth`、`semi`、`singleQuote`、`trailingComma`、`bracketSpacing`、`arrowParens`、`endOfLine`、`tabWidth`、`useTabs` 全部格式化参数。 #### Scenario: 检查代码格式 - **WHEN** 开发者运行 `bun run lint` - **THEN** ESLint SHALL 通过 eslint-plugin-prettier 检查受管理文件格式,并在发现未格式化文件时以非零状态退出 #### Scenario: 自动格式化代码 - **WHEN** 开发者运行 `bun run format` 或 `eslint --fix` - **THEN** 系统 SHALL 使用 Prettier 重写受管理文件的格式 #### Scenario: 排除 OpenSpec 文档和生成产物 - **WHEN** Prettier 格式化或格式检查运行(通过 ESLint 或独立 Prettier 命令) - **THEN** 系统 MUST 排除 `openspec/`、`dist/`、`.build/`、`node_modules/`、`bun.lock`、`skills-lock.json`、`.agents/`、`data/`、`*.bun-build`、`.opencode/`、`.claude/`、`.codex/` 和临时构建产物 #### Scenario: 格式化配置一致性 - **WHEN** 不同开发者在不同操作系统上运行格式化(通过 ESLint 或独立 Prettier) - **THEN** 由于所有格式化参数均显式定义,产物 SHALL 完全一致 ### Requirement: TypeScript 未使用变量检测 项目 SHALL 启用 TypeScript `noUnusedLocals` 编译选项,将未使用的局部变量检测为编译错误。 #### Scenario: 存在未使用的局部变量 - **WHEN** TypeScript 代码中存在声明但未被引用的局部变量 - **THEN** `tsc --noEmit` MUST 以非零状态退出并报告未使用变量 ### Requirement: TypeScript 索引签名属性访问检测 项目 SHALL 启用 TypeScript `noPropertyAccessFromIndexSignature` 编译选项,禁止通过点号访问未显式声明的属性。 #### Scenario: 通过点号访问 Record 动态属性 - **WHEN** 代码通过 `.property` 点号语法访问 `Record` 类型或索引签名类型的属性 - **THEN** `tsc --noEmit` MUST 以非零状态退出,强制使用 `["property"]` 括号语法显式访问 ### Requirement: ESLint 导入自动排序 项目 SHALL 通过 `eslint-plugin-perfectionist` 对导入语句进行自动排序,确保导入顺序一致性。 #### Scenario: 导入语句无序排列 - **WHEN** 文件中导入语句未按要求排序 - **THEN** `eslint --fix` SHALL 自动重排 import 声明和 named imports 内部顺序 #### Scenario: type import 与 value import 混合 - **WHEN** 文件中同时存在 `import type` 和 `import` 语句 - **THEN** perfectionist SHALL 正确识别并分别排序,不将 type 和 value 导入混淆 ### Requirement: ESLint 导入路径验证 项目 SHALL 通过 `eslint-plugin-import` 验证导入路径的有效性和一致性。 #### Scenario: 导入不存在的模块路径 - **WHEN** 代码中导入了不存在或路径错误的模块 - **THEN** lint 命令 MUST 失败并报告 `import/no-unresolved` 错误 #### Scenario: 存在重复导入 - **WHEN** 同一个模块在同一文件中被多次导入 - **THEN** `eslint --fix` SHALL 自动合并重复导入为目标模块的单条导入 #### Scenario: 存在循环依赖 - **WHEN** 模块 A 导入模块 B,同时模块 B 导入模块 A - **THEN** lint 命令 MUST 报告 `import/no-cycle` 警告 ### Requirement: 快速检查命令 项目 SHALL 提供快速 `check` 命令,用于日常开发期间验证代码质量和基础行为。 #### Scenario: 运行快速检查 - **WHEN** 开发者运行 `bun run check` - **THEN** 系统 SHALL 依次执行 schema 检查、类型检查、lint(含格式)和单元/组件测试(`bun test`) #### Scenario: 快速检查失败 - **WHEN** `check` 中任一子检查失败 - **THEN** `check` MUST 以非零状态退出且不静默忽略失败 ### Requirement: 分层测试运行命令 项目 SHALL 提供分层的测试运行命令,支持按需执行不同层级的测试。 #### Scenario: 运行全部单元和组件测试 - **WHEN** 开发者运行 `bun test` - **THEN** 系统 SHALL 执行 `tests/` 目录下所有 `*.test.ts` 和 `*.test.tsx` 文件 ### Requirement: 完整验证命令 项目 SHALL 提供完整 `verify` 命令,用于提交前或发布前验证当前源码、测试和生产构建。原 executable smoke test 暂时移除,后续通过独立变更重新设计。 #### Scenario: 运行完整验证 - **WHEN** 开发者运行 `bun run verify` - **THEN** 系统 SHALL 先运行 `check`,再运行生产构建 #### Scenario: 完整验证失败 - **WHEN** `verify` 中任一阶段失败 - **THEN** `verify` MUST 以非零状态退出且不能继续声明验证成功 ### Requirement: 测试代码 ESLint 禁用最小化 项目测试代码 SHALL 优先通过类型化 helper、类型化 mock、显式 no-op 和受控断言模式满足已启用的 ESLint 类型感知规则。受本变更审计的项目自有测试文件 MUST NOT 保留用于压制可通过代码结构解决的 `eslint-disable` 指令。 #### Scenario: 消除组件测试文件级禁用 - **WHEN** ESLint 检查 `tests/web/components/App.test.tsx` - **THEN** 该文件 MUST 不使用文件级 `eslint-disable` 关闭 `@typescript-eslint/no-require-imports` 或 `@typescript-eslint/no-unsafe-*` 规则,并且测试中的 hook mock SHALL 使用类型化引用或等价方式访问 mock API #### Scenario: 消除配置加载测试重复 await 禁用 - **WHEN** `tests/server/checker/config-loader.test.ts` 断言 `loadConfig()` 异步失败 - **THEN** 测试 SHALL 使用 helper 或显式 try/catch 断言错误实例与消息,MUST 不通过逐行 `eslint-disable-next-line @typescript-eslint/await-thenable` 压制 Bun `expect(...).rejects` 类型不匹配 #### Scenario: 测试环境 no-op polyfill 保持可解释 - **WHEN** `tests/setup.ts` 为 jsdom 测试环境定义浏览器 API polyfill - **THEN** intentional no-op SHALL 使用显式可解释写法表达,MUST 不通过文件级 `eslint-disable @typescript-eslint/no-empty-function` 关闭空函数检查 #### Scenario: release 测试拦截 process.exit 保持窄作用域 - **WHEN** `tests/scripts/release.test.ts` 验证无效 release target 会触发 `process.exit(1)` - **THEN** 测试 SHALL 使用受控 mock 或等价窄作用域替换并在断言后恢复,MUST 不通过 `eslint-disable-next-line @typescript-eslint/unbound-method` 保存未绑定方法 #### Scenario: 质量门禁验证禁用清理 - **WHEN** 开发者运行 `bun run lint` - **THEN** ESLint MUST 检查项目自有测试代码并在无上述 `eslint-disable` 指令的情况下通过