# Repository Guidelines VOKO Agent 世界(voko-world)。pnpm 11 + Turbo monorepo,Node >=24,ESM JavaScript + JSDoc。应用在 `apps/*`,可复用包在 `packages/*`。 本仓库于 2026-10-10 从 shadiao-2d 搬入 19 个包后独立演进;搬入来源与后续同步记录见 `docs/voko/upstream.md`。产品与设计文档见 `docs/voko/README.md`。 ## 应用与端口 | app | 说明 | dev 端口 | | --- | --- | --- | | @voko/server | VOKO API(`/v1` 公开接口、`/api/voko` 网页与后台接口),连 `voko` 库 | 9201 | | @voko/worker | 定时任务与 Webhook 投递(与 server 同目录,独立进程) | — | | @voko/web | 员工端与 `/admin` 后台,`/api` → 9201 | 5201 | ## 命令 - `pnpm install`;`pnpm mysql:up` 启动本地 MySQL 并确保 `voko` 库存在(与 shadiao-2d 共用同一个本地 MySQL 服务也可以,库名不同)。 - `pnpm lint` / `pnpm test`:架构红线与全量测试。CI 即 install → lint → test → schema:check。 ## 文档入口 - 产品、设计、计划:`docs/voko/README.md`。 - 执行启动、数据库、验收或部署任务时,先从[运行目标索引](docs/runbooks/README.md)进入对应手册。 - 新增或修改可执行流程时,必须新增或更新运行手册(`docs/runbooks/`)并登记到索引。只增加脚本或 `package.json` 命令不算完成。 ## 边界(ESLint 强制) - app 之间禁止源码级互相导入;跨应用只走 HTTP/API、contracts 或 packages 公开能力。 - 包一律从 `@drama/` 公开入口导入(各包 API 从 `src/index.js` 汇出),禁止深挖别包 `src/*`。 - 数据库和外部服务连接只在 composition root 创建;包内禁止顶层创建连接。运行参数在实际使用点读取 `process.env`,禁止为转发配置增加通用配置对象和无业务逻辑的参数链路。 - 包职责:`contracts` 接口 schema 与快照;`auth`/`account`/`billing`/`payment` 业务域;`core`/`render-pixi`/`player-vue` 播放链路;`ui` 基础组件与主题 token;`config` 共享预设。 ## 数据库 - 本产品使用独立库 `voko`,不连接 shadiao-2d 的 `idearun` / `idearun_visual_game`。 - 结构事实源:`packages/*/src/db/tables/*.cjs`(当前完整结构)+ `apps/voko-server/src/db/migrations/*.js`(版本间变更)。迁移历史表 `voko_schema_migrations`。服务启动不执行 DDL。 - 迁移只在合入主干后,才允许在 stage / 生产等共享库执行;开发只连本地库。 - 时间列一律 `DATETIME(3)`,值为 UTC;写入传 Date 对象,API 返回 ISO 8601 字符串。 - 全链路 snake_case:库列、函数参数、返回对象、contracts、请求/响应体、前端状态同名透传,不做任何大小写转换或多名兜底。 ## 环境变量 - 连接与基础设施配置由各 app 的 `src/env.js` 统一解析;业务运行参数由所属模块直接读取。 - 真实密钥放各 app gitignored 的 `.env`,不提交;也不提交本地数据库文件和 `node_modules`。 ## 代码 - 两空格缩进、单引号、无分号;类型说明用 JSDoc,不写 TypeScript 语法。 - 工艺规范唯一权威源 `~/.codex/code-standards.md`;写代码、改代码、写脚本、做评审前必须读取并遵守该文件。评审对照它逐条举反例,lint 干净不作质量证据。 - 服务方法把 `ctx` 作为显式第一参数一路传到 service/repo(`packages/observability/CONVENTION.md`),不用 AsyncLocalStorage 或全局单例。 ## 代码生成硬规则 ### 心智模型(最高优先级,先读这个) 系统分为入口和内部。入口有且只有四类: 1. HTTP controller 的请求入参 2. 消息/任务消费的 payload 3. 第三方 API / 外部服务的响应 4. DB row 读取后转成业务数据的那一次转换 数据在入口处被一次性校验并构造成正确形状,校验失败立即 throw。 过了入口的数据无条件信任:内部代码不判空、不兜底、不猜字段、不怀疑。 内部代码觉得数据可疑 = 入口漏了或构造函数没把不变量执行到位, 去修入口和构造函数,禁止在现场打补丁。 ### 事实源 - 写任何代码前,先定位:数据生产者、schema/migration、现有调用方、相关测试。 以生产者和 schema 为唯一事实源。 - 字段结构不确定时,读代码、读 migration、查真实数据。查完仍不确定就停下来问。 - 禁止一切猜测式读取:`a || b || c`、`x?.foo ?? x?.bar`、 `typeof x.foo === 'string'` 探测、`Array.isArray` 兜底分支,全部不许。 ### 数据形状 - 每类核心业务数据(订单、会员段、积分流水等)有唯一构造点, 普通函数即可(如 orderFromRow):非法输入当场 throw, 带数据 id 和具体哪个字段的问题。 - 状态相关的必有字段由构造点按 status 保证(paid 必有 paidAt,缺失即 throw), 下游读取零判空——内部代码出现判空,视为构造点没做到位,回去修构造点。 - 核心格式使用 JSDoc / d.ts 说明清楚。 ### 状态消费 - 消费 status 的 switch 必须穷举所有已知状态,且必须写 default 分支, default 只许 `throw new Error("Unhandled status: <值>, id: <数据id>")`。 禁止 default 里给默认行为或静默跳过。 - 同一个 status 的 switch 散落超过两三处时,把行为收进该数据的变换函数 或 reducer,不许继续复制分支。 ### 数据库约束 - 唯一性、非空、状态与字段的组合合法性,一律下沉为 DB 约束: NOT NULL / UNIQUE / CHECK(如 `CHECK (status != 'refunded' OR refunded_at IS NOT NULL)`)。 - 禁止用"先 SELECT 判断再 INSERT/UPDATE"在应用层模拟约束——有并发窗口,不可靠。 ### 错误处理 - 区分两类失败: - 预期失败(用户输错、余额不足、并发冲突):用显式返回值或明确的业务错误建模, 调用方必须处理; - 程序 bug(不变量被违反、不可能的状态):带完整上下文 throw, 交给全局 error handler 记日志、报警、返回 500。 - 中间层禁止 try-catch。禁止 catch 后吞掉、catch 后返回默认值、 catch 后包一层泛化错误重抛。 - try-catch 只允许出现在三处:全局 error handler、入口校验处、 对外部 IO(第三方 API、文件、网络)的最小包裹。 - 原则:宁可当场 500,不许静默写脏数据。涉及金额、会员权益的路径尤其如此。 ### 兼容与迁移 - 新代码禁止同时支持新旧两套数据形状。禁止:多字段兜底、大小写兜底、 旧字段别名、猜测式解析、默认值吞错、空数组/空对象兜底、遗留分支保留。 - 发现存量数据、线上接口、已发版客户端依赖旧形状时:停止,报告冲突, 给出迁移方案(migration 脚本 + 切换步骤 + 回滚点),等确认后执行。 禁止自行改线上 schema、删在用字段,也禁止用兼容层绕过。 ### 测试与诊断 - 生产、开发、测试、诊断、验收代码一律禁止 fake / mock / stub / spy、内存替身、 伪造第三方响应、伪造数据库结果、手工插入业务终态,也禁止 broken implementation 或测试专用开关。外部系统只用真实服务或官方 sandbox/test mode;真实依赖不可用就明确 `BLOCKED` / 未验证,不准用替身补绿。任务触达范围内的既有替身必须删除并改走真实链路。 - 禁止无用纯单元测试。纯单元测试只有在直接调用现有生产纯算法、零替身、验证明确业务 不变量/数学公式/完整状态全集,且同任务已有真实端到端链路时才允许;字段搬运、配置映射、 默认值、getter/setter、调用次数、mock 交互和代码存在性测试一律禁止。 - 测试数据必须符合生产 schema 的真实形状,禁止 mock 出宽容的假结构 为兼容代码制造存在理由。 - 涉及状态机的测试,必须覆盖全部已知 status 值—— 没有编译器做穷尽检查,测试就是穷尽检查。 - 诊断脚本必须验证具体问题,复用生产 schema 和生产函数; 失败就直接失败,失败信息要能定位到数据,不写通用兼容层。负路径必须把真实非法输入 或真实失败状态送入同一套生产入口,禁止另造错误实现证明脚本会红。 ## 前端 - 文案走 `vue-i18n`,新增 key 补齐 `en/zh/ja/es/id/pt`。 - 颜色/间距/圆角/阴影用 `var(--drama-*)` token(`packages/ui/src/theme/`),不硬编码 hex/rgba。 - 优先复用 `@drama/ui` 的 Button/Card/Modal/ToastHost;页面只做装配,不写鉴权协议、SQL、厂商 SDK。 - 设计参考 `docs/design/IMPLEMENTATION-STANDARD.md`、`docs/design/screen-to-module-map.md`。 ## 测试 - Vitest 是自动化测试套件的唯一 runner,用例放各 workspace 的 `test/*.test.js`;真实链路验收脚本放对应 app/package 的 `tools/scenarios/`,通过明确的 `node` 或 `pnpm` 命令运行,不伪装成纯单元测试。 - 自动化测试禁止 `CREATE DATABASE` / `DROP DATABASE`,也禁止随机生成数据库名。真实链路场景只连接本地 `voko` 库,通过真实业务入口写入生产形状数据并按场景精确清理。 - 改哪个包先 `pnpm --filter test`;改共享契约、路由守卫、支付、鉴权、播放链路再跑全量 `pnpm test` + `pnpm lint`。这些命令只作回归门禁,不能替代真实链路验收。 ## 提交与 PR - 提交信息用短 scope 风格(`fix(server): ...`),写清变更对象和动作。 - PR 带变更摘要与验证命令结果;UI 改动附截图;contract/schema 改动说明迁移影响。