这份文档是 web 的架构真值与边界设计说明,不再承担日常执行级 import/目录/验证清单的唯一入口。
前端执行级治理真值现在收口到 web/AGENTS.md:
web/AGENTS.md负责日常实现约束、模块边界、跨模块 import 规则、route 注册规则、i18n key 规范、shared 上提与 frontend validation- 本文档负责解释为什么采用
app + modules + shared、为什么保持模块拥有权清晰,以及为什么这些边界服务于可复用多 worktree 并行开发
如果实现级规则与本文档的表述需要分层,执行时以 web/AGENTS.md 为准;若该执行级规则改变了前端长期架构或 owned scope,再同步更新本文档。
authority-first overlay 补充:
web的 owned scope 表示前端长期实现归属,不表示前端拥有 shared contract 的最终 authority- bounded scope forbids unrelated expansion, not required authority repair
- 当页面、bootstrap、route/menu 组装或 generated consumer 发现上游 authority drift 时,应优先修上游 authority,再同步前端消费层
前端的职责不是做一个复杂的营销站,而是提供一个稳定的后台平台壳,让新模块能快速接入。
它要优先满足:
- 登录和权限控制
- 动态菜单
- 标准后台页面布局
- 标准列表 / 表单 / 详情 / 弹窗模式
- 模块页面快速接入
最终技术路线:
Vue 3TypeScriptViteTDesign Vue NextPiniaVue RouterAxios@tanstack/vue-query
前端本地化采用壳层收口策略:
- locale 状态和消息查找集中在
web应用层,而不是散落到页面内部 web/src/locales/**拥有 shell/root catalog、locale 状态、消息聚合入口、回退语言与持久化策略web/src/modules/<name>/locales/**拥有模块 catalog;模块消息 key 不得复制到 root catalog 形成第二套真值- 应用壳层负责在中心运行时入口聚合 root catalog 与 module catalog;页面、壳层和模块都不应绕过该入口自行拼装第二套 messages
- 同一 locale 内不允许重复 exact key 定义;unused key 必须通过治理脚本清理,避免历史文案继续占据 catalog
- 重复 value 允许存在,前提是 key 归属和业务语义不同;治理目标是消除重复真值,不是强行把语义不同的相同展示文案合并
- 英文 locale 的可见 UI 文案必须首字母大写;连接词、统计单位等语法片段可按语境保留小写
- 当前 locale 范围与
server设计面对齐,只收敛到zh-CN/en-US,默认语言与回退语言都为zh-CN - 请求层统一透传当前 locale,避免每个 API 调用手写语言头
- 用户可见时间必须显式绑定当前
vue-i18nlocale;禁止用宿主环境默认值渲染可见时间,例如new Intl.DateTimeFormat(undefined, ...)或无参数toLocaleString()/toLocaleDateString()/toLocaleTimeString() - 可见时间格式化默认收口到
web/src/shared/observability的 locale-aware formatter;API payload、URL query 与持久化状态仍保持 canonical UTC 或页面本地输入语义,不把 wire contract 改成本地化字符串 - 当前阶段先建立消息 key、回退和持久化口子,不要求一次性把全部页面铺成多语言资源
选择 TDesign 的原因:
- 更贴近中后台场景
- 组件体系规整,适合批量生成标准页面
- 设计风格比传统后台库更现代一些
- 对 AI / MCP 辅助生成代码较友好
TDesign Vue Next 运行时引入方式:
- 默认通过
vite.config.ts的 resolver 将模板中的t-*组件按需解析到tdesign-vue-next/es/<component>子入口 - 应用入口不得使用
app.use(TDesign)全量注册,也不得引入tdesign-vue-next/es/style/index.css全量样式 - 程序式插件和渲染函数组件只在使用点显式 import;如 resolver 未覆盖样式,只补对应组件级入口或 style import
前端视觉真值额外收口到仓库根目录 DESIGN.md,它负责定义 Graft 的统一视觉语气、页面骨架和 agent 生成口令;更细的页面模板与约束收口到 ai-plan/design/governance/frontend/前端视觉设计规范.md 和 ai-plan/design/graft-design-system/。
前端页面实现流程额外固定为“先声明页面类型,再进入实现”:
- 首阶段内置 4 类基础页面母版:
shellauthoverview-dashboardlist-form-detail
- 它们不是页面类型全集;若新需求无法自然归类,必须先登记扩展页面类型并补齐其结构、状态、主题响应与 i18n 规则
- 新增页面、重构页面和复杂布局页默认先输出结构方案,再进入代码实现;简单文案/样式/小交互修复可直接实现,但仍服从同一套类型、主题和文案治理规则
AI 辅助生成或评审前端代码时,应优先按
TDesign MCP 辅助开发规范 获取组件列表、组件文档、DOM 结构和变更日志。
MCP 只作为开发时知识源,不作为 web 运行时依赖。
当任务涉及 TDesign Vue Next 组件用法时,前端 Agent 默认先做 TDesign MCP preflight,以 vue-next 为查询框架,
完成组件资料查询后再进入编码;只有 MCP 不可用时,才允许退回官方文档并在 closeout 中说明原因。
这项要求属于前端治理和 closeout 审计要求:需要在任务结束时留下 MCP evidence,但不要求把 MCP 接入 CI、hooks 或仓库脚本硬门禁。
时间输入与共享查询语义补充规则:
- 页面级
DatePicker/DateRangePicker状态必须保存为用户本地展示时间语义 - 禁止长期持有
Date、Date[]、或混合Date/string作为筛选状态 - 需要稳定跨时区复现的 URL query 与 API request,必须在共享边界统一转换为 canonical UTC
- 禁止
Date -> toISOString() -> 直接绑定 DatePicker / DateRangePicker - 恢复 deep link 时,必须先把 canonical UTC 转回本地展示时间,再回填日期控件
web/src/
├─ app/
├─ layouts/
├─ modules/
├─ shared/
├─ contracts/
├─ config/
├─ locales/
├─ router/
├─ store/
├─ api/
├─ style/
└─ assets/
职责约束:
app/放壳层页面入口、应用装配入口和其他不归单个业务模块长期拥有的页面;认证页只允许作为壳层装配点或薄包装,长期实现真值收口到modules/authmodules/放 backend modules 对应的前端模块,是业务能力的唯一长期真值shared/只放跨模块可复用且无业务语义的组件、composables、helpers 与样式片段contracts/放平台级前端稳定契约,不放模块私有契约config/放壳层级平台配置真值;如果该目录仍存在,它继续属于shell-owned,不作为模块业务真值落点locales/处理应用级 locale 状态、消息目录和格式化入口router/处理静态路由和动态路由装配store/只放跨页面共享状态api/只放平台级 request/auth/session adapter,不放模块业务 API 真值;auth运行时实现迁入modules/auth后,这里只保留平台 adapter 与桥接消费面
最终运行面约束:
app/**是壳层页面真值,不承担业务模块实现web/src/modules/<name>/**是业务模块页面、API、局部类型、模块私有契约、模块消息源和模块 bootstrap 声明的长期真值web/src/shared/**是跨模块共享真值;只有在资产被多个模块或壳层复用且不携带业务语义时才允许进入- 新模块默认目录为
pages、components、api、contract、types、locales - 模块存在稳定子菜单或平级子页面时,页面真值默认收口到
pages/<subpage>/index.vue,并让index.test.ts、index.less与该页面同目录共置 - 仅供模块内部多个页面/组件复用的 helper、snapshot、样式片段与 composable,默认放在
modules/<name>/shared/**,不要与根级web/src/shared/**混用 modules/<name>/bootstrap-routes.ts是模块拥有的动态路由声明文件;壳层通过模块注册入口消费它,而不是把它当作任意跨模块 import 面- 永远不要重新引入根级
web/src/pages/**作为长期运行面 - 根级
web/src/api/**只允许保留平台级 adapter;模块业务 API 不得落回根级 - 根级
web/src/contracts/**只允许保留平台级稳定契约;模块稳定契约与跨模块稳定 DTO 必须收口到web/src/modules/<name>/contract/** web/src/modules/<name>/types/**默认是模块私有实现面,不作为跨模块依赖入口- 根级
components/、hooks/、utils/不是最终所有权层;可复用且无业务语义的资产应进入shared/**,平台级基础设施应回到壳层边界 - 当前代码中如果暂时还不存在
web/src/shared/**,表示尚未抽出符合条件的共享资产,而不是允许继续把业务真值散落到根级目录
多 worktree 准备阶段的 web 目录所有权补充约束:
layouts/、router/、locales/、config/、全局store、平台级contracts/、平台级api/和app/属于shell-owned目录web/src/modules/<name>/属于module-owned边界,模块内部应承载该功能的页面、API、局部类型、模块私有稳定契约、模块消息源与模块注册面web/src/shared/**属于shared-owned边界,只承载业务无关的复用资产- 临时 agent worktree 的默认
webowned scope 按模块粒度声明,不按api/、app/、store/等技术层切分 shell-owned与shared-owned目录不归任何 agent worktree 长期占用;需要修改时通过开发者集成切片协调
目录边界补充约束:
app/**只允许依赖shared/**、contracts/**、router/**、store/**、平台级api/**,以及模块显式对外暴露的注册面- 当
config/**仍存在时,它继续作为壳层/平台配置依赖面被app/**、layouts/**、router/**等 shell 目录消费,不转化为模块 owned scope app/**不得导入modules/*/api、模块pages、模块内部components、模块内部locales或模块局部 composablesmodules/**不得反向导入app/**shared/**必须保持业务无关,不得承载权限码、路由名、DTO 或user/rbac/dashboard这类业务语义- 根级
api/、contracts/、components/、hooks/、utils/不得重新成为模块实现溢出的默认落点
建议至少提供两个布局:
AuthLayout用于登录等无需完整后台壳的页面BasicLayout用于后台主界面
BasicLayout 应包含:
- 顶部导航
- 侧边菜单
- 面包屑
- 内容区
- 底部 Footer 与统一安全留白
- 用户操作区
所有标准后台页面都挂在 BasicLayout 下。
当前阶段的默认后台壳基座必须继续以真实 web/ 工程为唯一运行面,不得把
web/ai-libs/tdesign-vue-next-starter 或其他 starter/demo 工程当作临时运行基线。
收口原则:
web/ai-libs/tdesign-vue-next-starter只作为本地参考源,用于借鉴布局、页面组织、治理配置和 TDesign 写法ai-plan/design/graft-design-system/作为当前项目的 Graft 风格参考模板目录,只提供页面骨架、设计约束与组合方式,不提供运行时依赖- 如需复用 starter 壳层或页面样板,应把必要结构显式迁入当前
web/工程,并按 Graft 的menu + route + page + api + permission边界收口 - 不允许在
web外保留并行 runtime surface、并行路由真值、并行 mock/demo 入口或第二套前端工作台 - starter 自带的前端权限旁路、
mock业务接口、tabs-router策略和演示业务页面只能作为迁移参考,不得成为 当前运行态依赖 - 当真实运行面已经不再引用某个 demo 页面时,应直接从
web删除该页面与残留引用,而不是在真实工程里继续保留“备用示例”
前端路由分两类:
- 静态路由 例如登录页、404、基础首页
- 动态路由 根据后端返回的菜单与权限生成
建议流程:
- 登录成功后获取用户信息、权限集、菜单树
- 前端根据菜单元信息装配可访问路由
- 使用全局守卫阻止未授权访问
- 首页快捷入口由当前可见菜单派生;新增菜单时不再单独维护第二份 homepage quick action / quick link 注册
前端实时能力统一采用两段式模型:
- 普通 HTTP 请求申请单次 realtime subscription ticket
- 使用统一
/ws?topic=...&ticket=...连接消费对应 topic
约束:
- 页面不得直接长期依赖模块私有 WebSocket 订阅路径来消费普通资源/事件推送
web的实时消费 authority 是 canonical topic,而不是页面私有 socket URL 拼接规则- 容器资源统计这类持续刷新视图,应以 WebSocket 推送为 authority;HTTP 继续承担基础详情/列表结构数据,不再承担实时 stats authority
- 前端收到新的实时快照前,应保留最后一次成功值,避免因为一次刷新失败把 UI 退回“未采集”
多 worktree 收口补充约束:
router壳层不应继续直接维护“页面 path -> component”的功能白名单- 模块应仅通过
modules/<name>/bootstrap-routes.ts向壳层声明可接入的 bootstrap 动态路由 - 首页快捷入口是壳层消费当前 visible menu/bootstrap state 的派生结果,不是模块声明式 dashboard contract
- 首页 widget area 若要展示模块摘要、状态或操作组件,仍需单独设计 dashboard widget contribution;不要把 widget 需求混入菜单注册
- 对 Notification Center 这类不属于左侧业务菜单、但仍属于后台壳的全局页面,模块应通过注册面的
globalRoutes声明菜单外全局路由;后端不应返回 hidden menu 来撑路由 - 菜单隐藏和页面/Tab 隐藏必须分离:
hiddenMenu只影响菜单渲染,不能阻断 Tab、breadcrumb 或 keep-alive 上下文同步 - 共享壳层只负责装配,不负责替模块持有长期 feature 真值
路由元信息至少要包含:
namepathcomponenttitletitle_keyiconpermissionmodule
菜单本地化约束:
title_key是web内部菜单与路由装配的唯一长期真值- 如果上游输入仍同时提供
title,只能在 request adapter 边界把它当作外部输入回退,不得把title写回模块或壳层作为第二套内部真值 - 菜单、标题与错误显示在存在稳定 key 时必须优先走
key + fallback,不得把后端最终文案当作前端主真相 - 模块与壳层内部都不应再存放“
title_key+ 独立 title 文案”的长期并行定义
契约治理补充约束:
route name/path、storage key、request header、auth scheme、API error code、feature flag key与前端按值分支的稳定状态枚举,统一受 契约治理与魔法值治理规范 约束- 平台级前端契约优先收口到
web/src/contracts/*,模块私有稳定契约优先收口到web/src/modules/<module>/contract router、request、storage、模块 contract 边界优先消费显式 typed contract,而不是继续扩散裸字符串- 不得用 alias、重复 path 常量或根级 re-export 维持第二套长期 contract 真值;旧 contract 完成调用方迁移后必须删除
新增一个后台模块时,前端至少新增:
- 模块 API
- 模块路由定义
- 页面组件
- 权限码映射
- 菜单元信息
当仓库处于多 worktree 准备阶段时,新增模块还必须新增:
web/src/modules/<module>/下的模块注册入口- 模块自己的
bootstrap-routes.ts路由声明 - 默认目录
pages、components、api、contract、types、locales - 模块自己的消息源、局部 UI 片段与 typed contract
模块接入壳层只能通过 modules/ 下的注册入口完成;壳层文件只消费注册结果,不承担模块实现。
新增模块时禁止出现以下落点:
- 根级
web/src/api/**中的模块业务 API - 根级
web/src/contracts/**中的模块私有契约 - 根级
components/、hooks/、utils/中的模块专有实现
如果某个能力确实被多个模块复用,先判断它是否业务无关;只有业务无关时才允许抽到 shared/**,否则继续留在模块边界内。
当模块文案未来可能需要切换语言时,应优先存放稳定消息 key,而不是把可变文案直接写死在路由或菜单元信息里;菜单场景优先对齐 title_key + title fallback。
对于后端返回但直接面向用户展示的稳定业务文案,例如 permission code -> 名称/说明 这类可预测元数据,前端应优先在所属模块内
建立 stable contract -> locale key 的映射,再由 locales/** 提供多语言文案。接口原文只作为未知项、兼容期或迁移期回退,
不得长期作为唯一显示真值,否则页面会出现“菜单已本地化但表格说明仍是后端英文种子”的分裂状态。
如果是标准 CRUD 模块,建议统一模式:
- 列表页
- 创建 / 编辑弹窗
- 详情抽屉
- 搜索区域
- 批量操作区
这样 AI 辅助生成时代码更容易保持一致。
为降低 AI 在新页面生成时的自由发挥,web 可以保留一组 starter 风格的页面样板作为设计参考或显式迁移模板,
但这些样板必须收口在当前 web 工程的真实模块和页面边界内,不得再把 starter 整体工程视为运行时基线:
- dashboard
- list
- form
- detail
- result
这些页面样板如确有保留必要,必须满足两个条件:
- 仍被当前真实运行面直接引用
- 已明确归属到壳层异常页或具体模块 owned scope
不满足上述条件的旧 starter/demo 页面应删除,示例来源统一回到 web/ai-libs/tdesign-vue-next-starter,而不是继续在
app/** 或 modules/<name>/** 之外重新维持根级 web/src/pages/** 平行运行面。
Pinia store 保持克制,只保留真正跨页面状态:
authtoken、当前用户、登录状态i18n当前语言、回退语言、持久化偏好permission权限码集合、路由可访问性navigation菜单树、当前激活菜单module模块列表、模块启用状态
不要把页面局部表单状态全部塞进 store。
Pinia 只拥有跨页面客户端状态;后端资源快照、列表、详情和 mutation 状态由 @tanstack/vue-query 管理。
- 唯一 QueryClient 位于
web/src/shared/query/**,应用启动时注册;模块业务 query key、query function 与 mutation 仍属于modules/<name>/** - query function 必须继续调用模块
api/**,由web/src/utils/request.ts保持唯一 Axios、认证、locale 与错误边界 - URL 查询参数、表单草稿、表格列可见性和本地选择状态不是 Query cache 真值;query key 只使用已归一化的 API 输入
- mutation 完成后精确失效或更新受影响的 module query;realtime 消息同样通过失效或
setQueryData更新,不维护第二份 page-local server snapshot - 当前不持久化 Query cache;认证会话清理必须清空 QueryClient,避免不同用户读取到旧资源
AI 在页面设计、重构或 review 阶段,遇到以下信号时必须先评估模块级 @tanstack/vue-query,再新增
onMounted + watch + ref 的手工请求状态:同接口被多个组件读取、页面重新进入后重复读取、手工 loading/error/data、
手工 refresh / 去重 / 轮询,或 realtime 重连后需要刷新 HTTP 快照。
- 优先迁移 Query 的是 server-owned 列表、详情、目录、摘要和非流式轮询快照;先复用模块
api/**,再定义模块私有 key TanStack Table、TanStack Virtual、TanStack Router和TanStack Form不是默认依赖;只有现有 TDesign、日志虚拟化、 Vue Router 或 TDesign Form 已被性能/维护性证据证明不足时,才建立有验收指标的独立设计- Monaco、xterm、WebSocket 流、编辑器草稿、表格列偏好和 URL filter 不进入 Query cache;不要为了采用 TanStack 建第二套状态真值
Pinia 是 web 唯一正式共享客户端状态层;它不替代 Query cache。
约束如下:
- 不设计
Pinia的全局替换方案,也不把signals作为默认状态管理标准 - 只有在
setting/theme runtime被证明存在真实的computed / watch / store action维护问题时,才允许评估局部试点 - 评估阶段必须先完成文档级 go/no-go 判断;在准入结论明确前,不新增依赖、不改业务代码
- 如需进入局部试点,候选方案优先选择纯运行时的
alien-signals,不考虑需要预编译耦合的方案 - 如果未来引入
useThemeRuntime(),它只能作为setting/theme内部 adapter,不得升级为业务模块通用标准
明确禁用范围:
authpermissionroutertabs-routerAPI cache- 表单提交状态
- 后端业务实体状态
当前针对 signals 的评估、准入条件、最小 POC 候选边界、成功标准、退出标准与测试范围,记录在
ai-plan/public/archive/mvp-extension-path/subtopics/web/design/signals-theme-runtime-evaluation.md。
统一使用 TDesign 组件为主,避免混搭多套 UI 库。
页面设计原则:
- 统一表格与表单间距
- 统一页头、卡片、筛选区结构
- 统一弹窗与抽屉交互模式
- 统一状态标签与操作按钮风格
UnoCSS 只做辅助布局和少量原子样式,不要用来重写整套视觉体系。
涉及 TDesign 组件 API、事件、插槽、DOM 结构、组件升级影响时,AI 必须先查询 TDesign MCP 或官方文档, 再生成或修改代码。没有查询依据时,不应凭经验猜测组件属性名、事件名或内部 DOM 选择器。若本轮涉及组件改动,closeout 必须记录查询组件、查询能力、采用状态与原因。
前端日志基础设施用于统一开发期诊断、运行期故障记录和后续可扩展传输,不等同于页面消息提示,也不作为业务逻辑分支控制器。
基础模型约定为:
LoggerCoreLogEventTransport
默认能力约束:
- 业务代码只通过
createLogger获取 logger,不直接拼装底层 transport - 默认 transport 使用
consola - 需要静默时使用
NoopTransport,而不是让业务代码到处判断是否输出 - logger 必须支持
child()与withContext(),用于派生子模块上下文和附加稳定上下文
调用治理约束:
- 每个 logger 都必须带稳定的
moduleName,并以模块边界而不是页面控件命名 moduleName应与web/src/modules、app、layouts、router、stores等实际责任边界一致,避免临时字符串漂移- 传入
meta与context的数据必须可序列化,避免函数、类实例、DOM 节点、响应式代理和循环引用对象进入日志管道 - 日志里不得记录 token、密码、cookie、完整响应体中的敏感字段、用户输入的高风险原文或其他未经脱敏的敏感数据
- logger 记录错误不代表已经完成用户提示;
logger与 UI message 是两条独立责任链,是否提示用户要由页面或交互层显式判断 catch中调用logger.error后,不得以“已记录日志”为理由静默吞掉异常;必须继续按调用边界选择抛出、转换、返回显式失败态或补充 UI 反馈
调试期治理约束:
- 临时 debug 日志必须有明确生命周期,只允许服务于当前排障、联调或迁移窗口
- 临时 debug 输出在问题关闭、契约稳定或切片提交前应删除、降级或收口到明确开关下
- AI 生成或修改前端代码时,不应默认加入噪音型 debug 日志;确需加入时,应优先放在最小边界并说明后续清理点
- AI 不应把
logger.debug、console.log、注释掉的调试代码或一次性追踪字段长期留在页面、store、router 守卫或 API 封装层 - 长期保留的 debug 开关必须通过 shell-owned Debug Runtime 统一管理,flag 按模块功能使用 namespaced 命名,例如
tabs.layout、tabs.store、project.monaco .env*中的调试键必须与 namespaced flag 对齐,优先使用VITE_DEBUG_<MODULE>_<SUBMODULE>形式;不要继续平铺无边界 debug keywindow.__GRAFT_DEBUG__只作为开发者控制台 API,真实运行时状态由 debug store 持有;控制台入口不应成为第二套状态真值- Debug Runtime 可在所有环境常驻但默认关闭;未来若提供危险调试能力,必须额外受
import.meta.env.DEV约束 - 调试日志默认输出为扁平单行 key-value 摘要,保证排障信息可 grep、可复制和可稳定比对,不把关键事实藏在深层嵌套对象中
- 对交互类、路由类、菜单类、图表重排类问题,默认优先通过结构化控制台日志、守卫链路日志、事件处理日志或最小复现测试获取运行时证据,而不是只凭静态代码猜测真实交互路径
- 当用户可以提供浏览器控制台输出、录屏、网络面板或稳定复现步骤时,应优先利用这些运行时证据收敛问题,再修改实现;日志只是诊断手段,不应作为长期业务输出保留
扩展边界约束:
Transport设计应允许后续接入远端收集、审计桥接或测试替身,但当前阶段不提前引入复杂采集平台- 业务模块只依赖稳定 logger 接口,不依赖
consola的具体 API,避免后续 transport 替换时扩散改动
当前 web 的治理基线应按一条完整质量链来设计,而不是把类型、格式、Lint、测试拆成彼此独立的可选项。
治理基线至少包含:
TypeScript strictformat:checkESLintStylelintVitestHusky + lint-stagedcommitlint
当前仓库已经把统一验收入口收敛到显式脚本 bun run check,并要求完成态校验顺序固定为:
format:checktypecheckopenapi:frontend-governance:checklint:i18nlintstylelinthygiene:checktest:runbuild
仓库根 Justfile 可以提供 optional developer entrypoint / convenience layer,例如把 just check、just lint、just generate 作为常见工作流捷径;但这些 recipe 只允许包装 bun run check、相关 web/package.json scripts、现有 Go CLI 与仓库脚本,不得升级为新的 startup authority、frontend completion truth、runtime contract 或 CI contract。
执行环境约束:
- 当前仓库的 web 命令统一通过仓库当前环境中的 bun 执行,包括 bun install、bun run dev、 bun run typecheck、bun run test:run、bun run build、bun run check
- 不应混用多套 Bun 或其他包管理器刷新 web/node_modules,避免生成不一致的依赖目录
- .ai/environment/tools.ai.yaml 应记录当前前端工具链真值,AI 和自动化不得自行发明第二套本地命令矩阵
约束说明:
pre-commit只处理暂存文件,优先通过lint-staged执行格式化与局部修复,不把完整check链路塞进提交前钩子;本地 contract governance changed-scan 也应在这里阻断pre-push不再重复执行本地 changed-scan;远端 push 阶段的 contract governance 阻断统一由 CI job 负责,避免本地提交链与 Windows Git / WSL 执行环境分叉- i18n governance 通过
bun run lint:i18n收口到bun run check,且lint:i18n默认以STRICT_I18N_KEY_FIRST=true执行 strict key-first governance;CI 继续执行bun run check即可覆盖 i18n,不得新增第二个 CI i18n 真值;本地 hooks 只在web/src/**、web/scripts/check-i18n-governance.ts或web/package.json变化时增量触发lint:i18n - strict key-first governance 下,fallback-only server key-first registry findings 是阻断项;key + fallback pair 只允许作为兼容回退,不得重新变成展示主真相
- 对
web而言,功能完成、任务完成、准备合并三个节点都必须跑完整check;开发中间态才允许退回最小验证 - 完整
check是当前前端手动验收、合并前检查和 CI 的正式入口,而不是可选建议 Vitest是当前前端治理的正式单元测试基线,不再把“前端没有测试”当作默认前提Stylelint用于约束Vue样式块与样式覆盖边界,避免随意改写 TDesign 内部 DOMhygiene:check负责死代码、重复代码和非白名单固定字号治理;必要固定字号必须带 selector 级白名单原因commitlint与仓库commit-msghook 负责把仓库提交规范收口到可自动校验的入口,但提交治理真值统一以根AGENTS.md为准;提交标题默认语言、ownership 判定后的自动提交条件、scope强制要求、普通提交正文 bullet 要求,以及 escaped control text 禁令都不得在web文档里另立第二套规则- 当前最小自动提交信息约束至少包含:标题符合 Conventional Commits、必须显式带
scope、普通非 merge/revert 提交必须包含至少一条-正文 bullet,并禁止把字面量\n、\t、\r一类 escaped control text 直接写进提交信息;默认标题语言与自动提交边界均跟随根AGENTS.md - JetBrains / WebStorm Inspection、TS language service suggestion、IDE 拼写检查默认只作为本地辅助信息,不直接
视为仓库完成态阻塞项;只有当同类规则被
typecheck、lint、stylelint、test:run、build之一覆盖,或仓库文档 明确提升为项目规则时,才进入正式验收口径 - 默认完成态要求
typecheck、lint、stylelint、test:run、build无 warning,不把 Vite/Rollup 构建 warning、 chunk size warning 或第三方依赖 warning 当作天然可忽略噪音 - 只有确认 warning 来自第三方依赖,或当前切片无法在不引入更大风险的前提下安全消除时,才允许走受控例外
- 受控例外必须记录在当前 active topic 或 subtopic 的 tracking 文档中,并写清楚 warning 来源、影响、暂不处理原因、
下一步治理动作;不得继续使用
non-blocking warning这类弱化完成态要求的表述 - 对高价值、低争议的问题,优先把规则前移到 CLI:
例如未使用变量、未使用导入、import 排序、
debugger、调试期console.log不把 JSDoc 完整性、suggestion 级 TS 提示、IDE 专属重构建议默认塞进 CI
类型治理策略:
- 默认保持
strict开启 - 不允许通过大面积
as any规避类型问题 any仅用于第三方库边界、动态 JSON、临时迁移兼容层等少数边界场景,并尽量收口到adapter、client、schema一类位置
参考源边界:
web/ai-libs/tdesign-vue-next-starter默认仍是本地参考源,用于借鉴治理配置、TDesign 页面组织和样式写法- 允许从 starter 提取壳层、页面样板、治理配置或样式模式,但必须显式改造后并入当前
web工程,不能把 starter 全量工程当作临时运行基线 web/ai-libs/不是web的运行时依赖,也不是第二个前端真值或第二个 Git root- 不应把 starter 内的
mock、前端权限旁路、tabs-router、演示页结构不加约束地长期保留在 Graft 当前web
首批页面:
- 登录页
- 仪表盘
- 用户管理
- 角色管理
- 权限管理
- 模块管理
- 审计日志基础页
后续页面再按模块逐步接入。
这套前端设计的重点不是炫技,而是让页面新增路径固定、风格统一、权限接入自然。
如果一个新模块能在较少决策下完成“菜单 + 路由 + 页面 + API + 权限”接入,就说明前端架构是成功的。