Add documentation for the three-stage development workflow and CLI management strategy; create a new notes file for additional insights.

This commit is contained in:
yuxuanhui
2026-08-03 16:17:16 +08:00
parent 91861565bb
commit 6e0c5c35fc
14 changed files with 3122 additions and 61 deletions
@@ -0,0 +1,84 @@
---
id: 20260725-extend-tracker-skill-additively
title: 扩展 Issue Tracker Skill 时保留完整基线并做最小增量
created: 2026-07-25
updated: 2026-07-25
status: validated
scope: global
category: skill-design
confidence: high
last_verified: 2026-07-25
promotion_target: none
projects:
- matt-pocock-skills-feishu
tags:
- skill
- issue-tracker
- progressive-disclosure
- regression-prevention
---
# 扩展 Issue Tracker Skill 时保留完整基线并做最小增量
## Trigger
当用户要求“在现有 setup skill 基础上增加一个 tracker/provider 选项”,并期望新 skill 保留原工作流和原模板行为时,召回这条经验。
## Context
第一次实现把飞书版 skill 写成了一个压缩后的独立工作流,并通过相邻路径引用原 setup skill。它在概念上覆盖了原流程,但没有完整保留原 `SKILL.md` 的文字约束和五个 seed 文件。用户明确纠正:新 skill 应先完整对齐原 Matt skill,再增加 `references/issue-tracker-feishu.md`,并把原来的三个正式 tracker 模板扩展为第四个飞书选项。
最终实现以原 skill 整个目录为基线,保持 GitHub、GitLab、local、triage labels 和 domain 模板不变,仅在 `SKILL.md` 的 tracker 介绍、选择、确认、写入和完成验证处加入飞书条件分支,并把飞书细节放入一层 reference。
同日的第二个扩展场景把该原则应用到三个运行期 skill:`to-spec-feishu`、`to-tickets-feishu`、`triage-feishu`。每个新 skill 以对应原 Matt skill 的完整正文为基线,只增加一条必须读取 `references/feishu.md` 的接缝;`triage` 的 `AGENT-BRIEF.md` 和 `OUT-OF-SCOPE.md` 保持逐字节一致。飞书的发布顺序、查重、两遍关系写入、时间门禁和 Wiki 降级策略全部留在 reference 中。
## Evidence
- 用户在 2026-07-25 两次指出结构要求:保留 `issue-tracker-feishu.md`;新 skill 必须完整对齐 `/Users/yuxuanhui/.agents/skills/setup-matt-pocock-skills` 后再补充飞书。
- 最终目录中的 `issue-tracker-github.md`、`issue-tracker-gitlab.md`、`issue-tracker-local.md`、`triage-labels.md`、`domain.md` 与原 skill 逐字节一致。
- `SKILL.md` 的 diff 只包含名称/描述兼容调整和飞书在 Section A、确认、模板、完成验证中的增量。
- `quick_validate.py` 输出 `Skill is valid!`,且目录无 `TODO` 占位符。
- 原 skill 使用的旧 frontmatter 字段 `disable-model-invocation` 被当前 validator 拒绝;新 skill 通过 `agents/openai.yaml` 的 `policy.allow_implicit_invocation: false` 保留等价行为。
- `to-spec-feishu`、`to-tickets-feishu`、`triage-feishu` 均通过 `quick_validate.py`,无 `TODO`;三个 `agents/openai.yaml` 均显式禁止隐式调用。
- 三个原 Matt skills 未修改;新 skill 主正文只新增飞书 reference 读取接缝,原有交互门禁、测试 seam、拆票确认和分诊角色语义保留。
- `triage-feishu/AGENT-BRIEF.md` 与原文件、`triage-feishu/OUT-OF-SCOPE.md` 与原文件分别通过字节级比较。
- 真实 POC 验证 reference 不是静态说明:Spec 发布、Ticket 两遍关系写入、Triage `needs-info → feedback → ready-for-agent` 均按新 reference 执行并回读;独立 Wiki 新建受平台限制时,降级和未满足项也按 reference 记录。
## Root cause
已验证:把“在原 skill 基础上增加”理解为“运行时引用原 skill 并重写一个更短版本”,会丢失用户期望的文本级约束、配套模板和可独立运行性。概念等价不等于产物级对齐。
推断:tracker 是一个可变适配点,但 setup 的探索、交互顺序、文件选择和消费者契约属于稳定基线。若同时重写两者,未来很难区分是 provider 变化还是基础流程回归。
已验证:同一“完整基线 + 单一 provider 接缝 + provider reference”结构不仅适用于 setup,也适用于依赖 tracker 的运行期 skills。这样可以分别验证 Matt 核心语义和外部系统副作用,而不把 CLI 细节散落到主流程。
## Failed approaches
- 将飞书操作链全部塞进主 `SKILL.md`:主文件偏离原 setup,且 provider 细节挤占上下文。
- 让新 skill 只读取相邻原 skill:减少了重复,但不满足用户要求的完整基线和独立配套资源。
- 手工重述原流程:即使语义接近,也会产生措辞、边界和模板缺失。
## Preferred action
1. 先复制原 skill 的完整目录作为新 skill 基线,包括主文件和全部 seed/template 文件。
2. 对不属于新 skill 身份的 metadata 做最小兼容调整;若旧字段被当前 validator 拒绝,用当前受支持的等价配置替代并记录原因。
3. 只在明确的变体接缝增加 provider:选项列表、provider 输入、写前草稿、模板路由和 provider 专属完成门槛。
4. 把长篇 CLI 命令、schema、状态机、已知版本边界放进 `references/<provider>.md`;在 `SKILL.md` 中明确何时必须完整读取它。
5. 保留 `Other` 作为自由格式兜底,不把它误算为正式模板。原三个正式模板加飞书等于四个受支持模板。
6. 用三类检查防止回归:
- `diff`:确认主 skill 只改了预期接缝;
- `cmp`:确认原 seed 文件逐字节一致;
- `quick_validate.py` 与占位符搜索:确认结构有效且无残留模板内容。
7. 对带附属模板的 skill,再对每个模板做字节级比较;不要只比较 `SKILL.md`。
8. 用一个最小真实 POC 验证 reference 中的外部副作用顺序。校验器通过只能证明结构有效,不能证明 provider 工作流可运行。
## Boundaries
- 当用户明确接受依赖式组合、且基线 skill 会持续独立升级时,可以只引用基线 skill;不要默认复制。
- 当 provider 需要改变基础探索、交互顺序或消费者语义时,不能强行维持最小 diff,应先确认这是新工作流还是原工作流的变体。
- 不要为了“完整对齐”复制当前校验器明确拒绝的旧 metadata;应保留行为等价性并记录兼容差异。
- provider reference 可以定义外部系统的失败降级,但不能削弱原 skill 的用户确认门禁,也不能把未完成的外部产物描述为成功。
## Promotion record
- Not promoted. 该原则已在 setup 与三个运行期 tracker skills 两类场景落地并通过真实 POC,学习状态提升为 validated;尚未扩展第二种 provider,因此暂不写入全局 skill-creator guardrail。
@@ -0,0 +1,96 @@
---
id: 20260725-feishu-cli-base-wiki-tracker
title: 用飞书 CLI 将 Base 与 Wiki 组合为可验证的 Issue Tracker
created: 2026-07-25
updated: 2026-07-25
status: validated
scope: global
category: workflow
confidence: high
last_verified: 2026-07-25
promotion_target: none
projects:
- matt-pocock-skills-feishu
tags:
- feishu
- lark-cli
- issue-tracker
- base
- wiki
---
# 用飞书 CLI 将 Base 与 Wiki 组合为可验证的 Issue Tracker
## Trigger
当工作流要用飞书多维表格管理 Issue、任务或产物状态,同时用飞书知识库承载 PRD、Spec、Map、研究、诊断、ADR 等长文档时,召回这条经验。
## Context
两轮真实 POC 使用 `lark-cli` 连接同一 Base 表格和 Wiki 根节点。第一轮完成 setup、字段骨架、工作流文档和单条验收记录;第二轮分别运行 Spec、Ticket 依赖图和 Triage 时间状态机。关键不是“命令返回成功”,而是形成从身份验证、资源解析、增量写入到逐层回读的闭环,并保留平台限制导致的降级证据。
Base 适合保存一行一个产物及其可查询状态;Wiki/Docs 适合保存长文本。两者通过 Base 中的规范文档链接字段关联。CLI 命令和资源坐标可以进入工作流文档,但凭证、访问令牌和不必要的组织数据不能进入知识库。
## Evidence
- 2026-07-25,在 `lark-cli 1.0.76` 上完成用户身份验证、Base URL 解析、表/视图/字段回读和 Wiki 根节点解析。
- 在不删除或转换原字段的前提下,将目标表验证为共 23 个字段,包含状态、类型、负责人、进度、证据、父项和依赖等工作流字段。
- 创建并分段追加 Wiki Docx,最终回读 revision 6,确认写入内容可取回。
- 创建 Base POC 记录并用真实 record ID 回读,确认标题、文档链接、状态、完成度、验收标准和验证证据。
- 执行经验固化位置:`/Users/yuxuanhui/.agents/skills/setup-matt-pocock-skills-feishu/references/issue-tracker-feishu.md`。
- 已省略实际 Base/Wiki URL、token、record ID 和组织信息;这些值只属于目标环境,不属于通用经验。
- 第二轮将原文本字段原地重命名为“产物文档”,保持同一 field ID、`text/plain` 类型和既有链接值;新增来源链接、外部编号、报告人、最后反馈时间、最后分诊时间后,完整字段回读为 28。
- 第二轮创建并回读 1 条 Spec、2 条 Ticket 和 1 条 Issue;Ticket 采用“两遍写入”,先建所有记录,再写父项和 blocker,逐条确认链接字段。
- Triage POC 保存 `needs-info` 阶段的时间快照,完整 `record-list` 返回 `has_more=false`,证明报告人反馈晚于上一轮分诊;随后发布 Agent Brief,并把最终再分诊时间推进到反馈之后。
- 并行执行同一 Base 的四个 `record-search` 时,三个请求返回 `800004135 OpenAPISearchRecord limited`;改为串行查询或一次完整 `record-list` 后客户端分组。
- `wiki +node-create` 返回 `131001 rpc fail`;`docs +create`(含正文与空文档两种)均返回 `10071 Document version limit reached`。搜索确认没有孤儿文档后,在已获授权的既有 Wiki 文档中追加独立 Spec/Triage 章节并回读,且明确记录该降级不等同于“新文档创建成功”。
## Root cause
已验证:飞书 CLI 集成最容易出现的可靠性缺口不是单条 API 调用,而是把“命令成功”误当成“工作流已配置”。若没有先解析真实资源、只创建缺失字段、搜索业务键防重、使用 ID 回读记录,并 fetch Wiki 文档,最终状态可能与预期不一致。
已验证:`+record-upsert` 不应被当作按业务标题自动去重。创建前必须搜索真实主字段;更新必须使用真实 record ID。
已验证:在 `lark-cli 1.0.76` 中,同表双向链接可能返回未独立出现在字段列表中的反向 ID。自动化应以正向 `所属父项` 和 `前置依赖` 为事实来源,除非当前版本回读证明反向字段可单独操作。
已验证:同一 Base 上并行发起多次 `record-search` 会触发搜索接口限流。查重和队列读取默认串行;小表可以一次完整分页读取后在客户端精确匹配,但必须检查 `has_more`。
已验证:Wiki 节点创建失败与 Docs 文档创建配额/版本限制是两个不同失败层。只有 `docs +create` 成功后才能尝试 `wiki +move`;若创建本身返回 10071,不应删除用户文档或宣称已创建,只能在授权范围内使用既有文档章节降级,或请求管理员解除限制。
## Preferred action
按以下顺序配置和验收:
1. 用 `lark-cli auth status --json --verify` 验证用户身份,不在文档中保存凭证。
2. 从用户给出的 Base table/view URL 和 Wiki root URL 解析真实 Base token、table ID、view ID、主字段、space ID、node token 和对象类型;不要按名称猜资源。
3. 在写入前完整读取 Base、table、view 和 field list,计算“仅缺失字段”的增量草稿,并让用户确认外部写入范围。
4. 串行执行 `base +field-create --json ...`。遵循每次响应中的 `field_get_recommended` 和 `next_step`,最后重新读取完整 field list。已明确授权的字段改名先用完整字段定义 `field-update --dry-run`,再以 `--yes` 写入,最后同时回读字段 ID、类型和既有单元格值。
5. 用 `wiki +node-create` 在已解析根节点下创建空白 Docx;失败时可以尝试 `docs +create` 后 `wiki +move`。若 `docs +create` 返回文档限制错误,停止创建路径,不做清理;只有既有文档更新也在授权范围内时,才使用独立章节降级。写入后用 `docs +fetch --detail with-ids` 回读内容和 revision,不对已有文档使用 `overwrite`。
6. 创建 Base 记录前,按真实主字段串行精确搜索标题。零个匹配才创建;已有记录只用真实 record ID 更新。小表改用完整 `record-list` 时必须分页到底并在客户端精确比较,不能把“第一页无匹配”当作不存在。
7. 关系图采用两遍写入:第一遍创建所有节点,第二遍使用真实 record ID 写父项和依赖,最后逐条 `record-get` 验证边。Triage 时间比较保留“反馈前的最后分诊时间”快照,最终处理后再更新最后分诊时间。
8. 创建关联 Wiki 文档的 POC 记录,包含明确验收标准和验证证据,再用 `record-get` 回读关键字段。身份字段无法可靠表达时留空并报告,不伪造用户值。
9. 只有在身份、资源、字段、Wiki 内容、POC 记录和生成的 repo 配置全部回读成功后,才宣布对应部分完成。验证被阻塞或使用降级时明确写出未满足项和原因。
10. 将实际执行过的命令写入 Wiki 工作流文档,删除凭证;保留有诊断价值的失败命令及修正版本。
建议使用的命令族:
```text
lark-cli auth status --json --verify
lark-cli base +url-resolve / +base-get / +table-get / +view-get / +field-list
lark-cli base +field-create / +field-get
lark-cli wiki +node-get / +node-create
lark-cli docs +update / +fetch
lark-cli base +record-search / +record-upsert / +record-get
```
## Boundaries
- 这条经验适用于 Base 作为结构化状态表、Wiki/Docs 作为长文档库的组合,不等同于飞书审批、项目或任务产品的通用集成方案。
- 字段 JSON、用户字段值、链接字段属性和命令参数必须以当前安装版本的 skill reference、`lark-cli --help` 和当前文档为准。
- 资源 token 和 ID 可能不是凭证,但仍应按最小披露原则处理;中央知识库只保留通用证据。
- 外部写入、删除、覆盖、权限调整和发布仍需遵守当前授权边界。
- “既有 Wiki 文档中的独立章节”能验证 Docs 写入和 Base 链接,但不能替代“成功新建独立 Wiki 文档”的验收证据。
## Promotion record
- Not promoted. Setup POC 与 Spec/Ticket/Triage POC 已提供两轮独立流程证据,学习状态提升为 validated;仍未跨第二个 Base/租户验证,因此暂不提升为全局执行规则。
@@ -0,0 +1,129 @@
---
id: 20260803-usercenter-react-directory-convention
title: React 管理后台按应用组装、路由、业务切片与共享能力分目录
created: 2026-08-03
updated: 2026-08-03
status: candidate
scope: repository
category: frontend-architecture
confidence: high
last_verified: 2026-08-03
promotion_target: none
projects:
- usercenter-react
tags:
- react
- directory-structure
- feature-module
- module-boundary
- admin-scaffold
- tanstack-query
- zustand
- server-state
- client-state
---
# React 管理后台按应用组装、路由、业务切片与共享能力分目录
## Trigger
在 `usercenter-react` 中新增、生成或移动 React 页面、业务模块、路由、请求、表单、表格或跨业务组件,需要判断文件应放在 `src/app`、`src/routes`、`src/features`、`src/pages`、`src/shared` 还是 `src/styles`;或者发现业务代码开始散落在顶层 `pages`、通用 `components` 和请求工具中。
## Context
这个项目的目录结构不是按 React 文件类型全局分组,而是先区分应用组装、路由、业务切片和横向共享能力,再在每个业务切片内按 API、组件、页面、Schema 和资源规格分层。这样既让单个业务模块的改动保持局部化,也让路由、权限、表格、表单、认证和国际化等公共契约有明确归属,并允许资源校验器检查生成式 CRUD 模块的完整性。
当前约定的核心结构是:
```txt
src/
├── app/ # 应用组装:Providers、QueryClient、Router 实例、Route Catalog
├── routes/ # 手写路由树、路由元数据类型与路由副作用
├── features/<resource>/ # 业务垂直切片
│ ├── api/
│ ├── components/
│ ├── pages/
│ ├── schemas/
│ └── specs/
├── pages/ # 非资源型系统页、设置页和可运行示例页
├── shared/ # 跨业务复用的技术能力与业务无关 UI 组合
├── styles/ # 全局 reset、tokens 和全局样式入口
└── main.tsx # 浏览器入口,只负责全局样式/运行时初始化和挂载 App
```
状态与请求管理遵循“服务端状态和客户端 UI 状态分离”的技术栈:
- `@tanstack/react-query ^5.90.12` 管理服务端状态,包括查询缓存、loading/error 状态、mutation、缓存更新与失效。全局 `QueryClient` 位于 `src/app/query-client.ts`,具体 query 和 mutation hooks 位于各 feature 的 `api/` 目录。
- `zustand ^5.0.9` 管理不来自服务端的全局 UI 状态。当前 `src/shared/config/ui-store.ts` 负责主题、语言、侧边栏折叠和命令面板开关,并按需要同步到 `localStorage` 或 i18next。
- 请求链保持 `page/component -> TanStack Query hook -> typed feature API adapter -> shared request helper -> native fetch`。JSON、二进制下载和 multipart 上传分别通过 `requestJson`、`requestBlob`、`requestMultipart` 进入统一传输边界,页面和组件不直接发送 HTTP 请求。
## Evidence
- 2026-08-03:检查 `usercenter-react` 当前项目文档、`src` 目录、导入关系、资源校验器和 CodeGraph 索引;中央知识库搜索“React 目录 / feature module / src/features / module boundary”未发现同根因条目。
- `usercenter-react/AGENTS.md:18-26`:明确应用代码只放在 `src`,路由使用手写 TanStack Router 路由树和 Route Catalog,服务端状态使用 TanStack Query 与 feature API adapter,共享 CRUD 模式放在共享层,运行时 AI 与根目录 `specs/`、`skills/` 分离。
- `usercenter-react/specs/feature-module.spec.md:3-15`:明确业务模块位于 `src/features/<resource>`,标准子目录为 `api/`、`components/`、`pages/`、`schemas/`、`specs/`,表格、表单、权限、认证、i18n 和布局等共享能力位于 feature 外部。
- `usercenter-react/specs/api.spec.md:3-14`:定义 `page/component -> query or mutation hook -> typed feature API -> mock or real adapter` 数据访问链,并禁止组件直接访问 `fetch`、`axios` 或 mock store。
- `usercenter-react/specs/route.spec.md:3-11`、`src/app/router.tsx`、`src/routes/route-tree.tsx`:路由实例和路由树分离;路由树负责懒加载 `src/pages` 的系统页与 `src/features/*/pages` 的业务页,元数据不放进页面组件。
- `usercenter-react/src/main.tsx:1-20`:入口仅加载 Semi React 19 适配、全局样式、i18n,并挂载 `src/app/app.tsx`,没有承载业务逻辑。
- `usercenter-react/src/features/products`:完整 CRUD 样例按 API、组件、页面、Schema 和资源规格垂直聚合;文件名使用 `.api.ts`、`.query.ts`、`.mutation.ts`、`.types.ts`、`.schema.ts`、`-page.tsx`、`-table.tsx` 等职责后缀。
- `usercenter-react/src/features/market-management`:在标准目录之外按业务需要增加 `policies/` 和 `routes/`,说明标准结构是稳定基线,不是禁止扩展的封闭清单。
- `usercenter-react/src/shared`:按 `api`、`auth`、`config`、`form`、`i18n`、`layout`、`list-templates`、`menu`、`permission`、`table`、`utils` 等横向能力组织,不按具体资源命名。
- `usercenter-react/package.json:22,35`:服务端状态依赖为 `@tanstack/react-query ^5.90.12`,客户端状态依赖为 `zustand ^5.0.9`。
- `usercenter-react/src/app/query-client.ts` 与 `src/app/providers.tsx`:应用集中创建并注入 QueryClient;默认 query 配置包含 `staleTime: 30_000`、`retry: 1` 和 `refetchOnWindowFocus: false`。
- `usercenter-react/src/features/products/api/products.query.ts`、`products.mutation.ts`:feature 使用 `useQuery`、`useMutation` 和 `useQueryClient` 管理读取、提交、详情缓存更新与列表缓存失效。
- `usercenter-react/src/shared/config/ui-store.ts`:使用 Zustand 集中管理主题、语言、侧边栏折叠和命令面板状态;这些状态不进入 TanStack Query 缓存。
- `usercenter-react/src/shared/api/json-request.ts`、`binary-request.ts`:共享请求层基于原生 `fetch` 封装 JSON、Blob 和 multipart 请求;feature API adapter 使用这些请求 helper,而不是让页面或组件直接访问传输层。
- `usercenter-react/tsconfig.json:18-21` 与 `vite.config.ts`:`@/*` 统一映射到 `src/*`,源码使用稳定的根路径导入,避免跨目录相对路径漂移。
- `usercenter-react/packages/registry/src/resource-module-verifier.mjs:37-65`:生成式资源模块校验器会定位 `src/features/<module>`,并检查 API adapter、类型、mock、query/mutation hooks、Schema、表格、表单、详情组件、CRUD 页面和资源规格的文件职责与命名。
- 2026-08-03 运行 `pnpm validate:resources`:`products.resource.json` Schema 校验和 `src/features/products` 模块结构验证均通过。
- 2026-08-03 运行 `codegraph status`:索引覆盖 131 个文件、1491 个节点和 3631 条边,但存在 1 个 pending modified file;因此目录结论以当前源码和规范直接检查为准,没有把未同步图谱当成唯一证据。
## Root cause
已验证:项目同时服务人工开发和 AI 生成,需要让业务模块的输入规格、类型、数据访问、UI、路由入口和验证规则可发现、可组合、可检查。若按全局 `components/`、`hooks/`、`services/` 平铺,单一资源会跨多个顶层目录分散,资源校验器也难以围绕 `src/features/<resource>` 做完整性验证。
已验证:应用组装和路由元数据具有全局生命周期,资源业务代码具有按功能演进的生命周期,共享表格、表单、权限、认证、i18n 和布局具有跨功能演进的生命周期。按变化原因划分目录,比仅按文件技术类型划分更符合当前代码和规范。
已验证:远程数据与本地 UI 状态拥有不同生命周期。服务端状态需要请求去重、缓存、失效和 mutation 协调,项目由 TanStack Query 负责;主题、语言和界面开关只在浏览器内变化,项目由 Zustand 负责。请求传输细节则下沉到共享 fetch helper 和 feature API adapter,不进入组件。
推断:把“业务垂直切片 + 横向共享能力 + 集中应用/路由组装”作为新增代码的默认落点,可以减少跨目录修改、重复抽象和 AI 生成时的放置歧义;但是否需要新增 feature 子目录仍应由真实职责决定。
## Preferred action
1. 运行时代码统一放在 `src`;根目录 `specs/`、`skills/`、`packages/registry` 等属于 AI Harness、规范或工具链,不要导入浏览器运行时。
2. 保持 `src/main.tsx` 薄:只做全局样式和运行时初始化、DOM 根节点检查及 `<App />` 挂载。Providers、认证启动边界、QueryClient、Router 实例和 Route Catalog 放在 `src/app`。
3. 手写路由树、路由元数据类型和路由副作用放在 `src/routes`;路由组件可以懒加载 feature page 或顶层 system/example page,但不要把路由元数据散落到页面组件。
4. 资源或业务能力默认建立 `src/features/<resource>` 垂直切片。完整单资源 CRUD 使用 `api/`、`components/`、`pages/`、`schemas/`、`specs/`;只有出现明确职责时再增加 `policies/`、`routes/` 等子目录,不提前创建空的通用层。
5. feature 内按职责命名文件:数据边界用 `*.api.ts`、`*.types.ts`、`*.query.ts`、`*.mutation.ts`、必要时 `*.mock.ts` 和 `*.serializers.ts`;校验与 URL search shape 用 `*.schema.ts`;页面用 `*-page.tsx`;复杂表格拆成 `*-table.tsx`、`*-table-columns.tsx`、`*-table-toolbar.tsx` 和 `*-row-actions.tsx`。
6. 服务端状态使用 TanStack Query:query/mutation hooks 放在 feature 的 `api/` 目录,query keys 使用共享工厂集中管理,mutation 成功后显式更新或失效相关缓存。不要把远程数据复制进 Zustand。
7. 全局客户端 UI 状态使用 Zustand:只存放主题、语言、界面开关等浏览器状态。局部组件状态继续使用 React 本地 state,不因使用 Zustand 而集中所有交互状态。
8. feature API adapter 负责端点、参数、响应和业务类型;共享 `requestJson`、`requestBlob`、`requestMultipart` 负责 URL、请求选项和统一传输行为。页面和组件只调用 query/mutation hooks,不直接调用 `fetch`。
9. 非资源型的 dashboard、403/404、外观设置和模板演示页放在 `src/pages`。一旦页面属于具体业务资源,应放回对应 feature 的 `pages/`,不要把业务页长期堆在顶层 `pages`。
10. 跨业务复用且不拥有具体资源语义的能力放在 `src/shared`,按能力域继续分目录。共享表格、表单和列表模板只拥有布局与交互壳;筛选状态、导航、API 调用和 mutation 保留在 feature。
11. 全局 reset、token 和应用级样式入口放在 `src/styles`;只服务单一组件的 CSS 可与组件同目录。UI 原语直接使用 Semi Design,只有出现重复的项目工作流时才建立项目自有共享抽象。
12. 源码跨目录导入使用 `@/` 别名。判断归属时先问“谁拥有这项业务语义、它与谁一起变化”,再决定目录,不以“它是组件/Hook/工具”作为唯一依据。
13. 新增或生成完整资源模块后运行 `pnpm validate:resources`;涉及共享 API、类型或跨目录影响时,再按项目规则使用 CodeGraph、源码搜索、类型检查和针对性测试核对影响范围。
## Boundaries
- 这是 `usercenter-react` 的仓库级规范,不是所有 React 项目的通用目录标准;其他仓库需要先读取其路由、状态管理、生成器和模块边界约定。
- `api/components/pages/schemas/specs` 是完整单资源 CRUD 的标准结构,不要求轻量 feature 为了形式完整而创建所有目录。`current-user` 只拥有 API 与 Schema,`market-management` 还拥有 `policies` 和 feature-local `routes`,都是当前结构允许的变体。
- 顶层 `src/pages` 适合无独立资源归属的系统页和示例页,不代表所有路由页面都应放在那里。
- `src/shared` 的目标是跨业务复用和资源语义中立,但当前 `shared/auth/auth-context.tsx` 会组合 `features/current-user`。这说明仓库尚未建立可由 lint 强制的绝对无环分层;不要把本记录扩大成“shared 永远不能引用 feature”的新规则,除非另行设计并验证迁移方案。
- 空目录或历史遗留目录不是规范证据;优先以 `AGENTS.md`、`specs/`、当前有效导入关系和可执行校验器为准。
- TanStack Query 只负责远程/服务端状态,不用来保存纯界面开关;Zustand 只负责客户端状态,不作为请求缓存或远程数据副本。
- 局部、短生命周期且只由一个组件树消费的交互状态不必放入 Zustand;优先保留为 React 本地 state。
- `pnpm validate:resources` 只验证带 Resource Spec 的生成式资源模块,不能替代整个应用的类型检查、lint、构建或业务测试。
## Failed approaches
- 按全局 `components/`、`hooks/`、`services/` 平铺所有业务代码:同一资源的修改会散落到多个顶层目录,资源规格与生成结果也难以成组验证。
- 把可复用性未验证的业务组件提前放入 `shared`:会把具体资源语义伪装成公共抽象,增加依赖和后续拆分成本。
- 看到 `feature-module.spec.md` 的目录清单就机械创建空目录:轻量 feature 和复杂 feature 的真实职责不同,空层级不会提高可发现性。
- 把请求结果同时存进 TanStack Query 和 Zustand:会产生两个数据源、重复失效逻辑和状态不同步问题。
- 页面组件直接调用 `fetch`:会绕过 feature API adapter、统一错误处理和 TanStack Query 缓存生命周期。
- 仅凭 CodeGraph 或目录名推断边界:索引可能未同步,目录也可能存在历史空壳;必须回到当前源码、项目规范和校验器核对。
## Promotion record
- Not promoted. 当前规范已经由 `usercenter-react/AGENTS.md`、`specs/` 和资源校验器共同承载;本记录先作为仓库级 candidate,待在另一个 React 管理后台独立复用并验证后,再考虑提炼为跨项目 pattern 或目录审计脚本。