Files
JX-BPOS/docs/编码规范.md
T
ChaiHaoandClaude Opus 5 259192f736 人手改的那一手真正落得下去;界面不撒谎那几处补齐
手改是**兜 AI 的错**的那一手 —— 这一轮把它从「摆在那儿但落不了地」做成真的:

件上就地改
· 保存**按地址收**,不看当下那张卡:载荷从前借道工单的 `blocks[i]` 取列名,
  于是熔断卡(没有 blocks)在屏上时,人补的那一格一个字都送不出去 ——
  而他要补的恰恰是把这一单卡住的那个必填格。授权面改由活动清单的台账声明把关。
· **能增删行**:错不止「这一格填错了」,还有「这一行不该在」与「漏了一行」。
  删挂记号不当场抹掉(看得见、可后悔),且**删要说出来**(`drops`)——
  「没回传就是删了」会把一次不完整的回传变成一次删表。
· **必填没填按不下提交**:与放行门 L1 同一个数同一套算法;闸上摆出差在哪一行哪一格。
· 只写真改了的那几行 —— 改一格却留「修订 4 行」的痕,那句话是假的。

判据与门
· 裁量判据带谓词时**谓词说了算**:「选填缺口已补齐」基座数得出来,
  人填好的目标价它照样判「客户未提供」的毛病由此消掉。
· 验收卡判据**读时现算** —— 人在成果件上补一格,判据跟着变。
· 放行门认「**办完了没有**」不认「有人写过」:从前写个 `false` 就把门推开,
  于是一单没打标就进了放行门。三个数全由基座现数。
· 拒回把「活没干完」说成「数或行对不上」的毛病:判据点名的表一行都没写时直说那一步没办。
  **指向两扇打不开的门,比不指还糟。**

界面不撒谎
· 日志只有三色:负责人绿 · 智能体蓝 · 系统灰(归侧五种是度量的分法,不是给人看的)。
· 验收收口答两件事:落了什么(按**交付物**点名,一份成果的几张表并成一行)、
  往哪儿去(转出哪一口、带过去哪些字段);一个口都没发就直说到此为止。
· 签完到收尾之间那一段有自己的话「字已签下,正在落地」—— 中间态本来就该说出来,只是要说对。
· 消息不再被内部事件刷屏:只有从通道进来的件才谈得上「无人认领」。
· 建客户公司与联系人**一次确认落两行**;联系人编码自增不问人;卡面摆表的人读名。

A1 三案例端到端全 released:全 A 类直通 12 拍 / 客户点名跳一条 10 拍 / 新客户缺必填 14 拍。
五道门禁全绿(971 passed)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JsTDjoHovERXyy6Wmw1ALp
2026-08-02 13:41:44 +08:00

687 lines
43 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# JCAI 编码规范
> **版本 V0.1 · 结构契约 · 2026-07-26 · 读者:Coding Agent**
>
> 本文件是本仓库的**架构约束契约**:任何人或 AI 在本仓库写代码前必须先读完本文件。与本文件冲突的代码是 bug,不是风格问题。
>
> 语义源两卷(《操作系统.html》V2.15 与《活动模型.html》V2.0)**已归档 `docs/archive/`**
> (2026-07-30):它们完成了奠基使命,flow 相关章节已是历史记述。现行语义权威 =
> **代码现状 + `docs/活动开发规范.md`(活动侧)+ 各模块 `CONTRACT.md`(系统侧)**;
> 本文件只规定"代码长什么样、放在哪、谁能依赖谁"。引文里的 OS x.x / AM x.x 编号
> 指向归档卷的章节,作历史出处保留。发现权威打架,停下来报告,不得自行仲裁。
---
## 0. 一句话改动原则
任何一次改动都必须能被一句话精确描述:**"只改 `<模块路径>` 的 `<层>`,公开契约不变。"**
这句话成立的充要条件:
1. 每个模块有**唯一物理边界**(一个目录)
2. 每个模块有**唯一对外出口**(`index.ts`)
3. 依赖方向**单向无环**
4. 以上三条**由工具强制**(§11),不靠自觉
---
## 1. 技术选型(定案)
| 领域 | 选型 | 一句话理由 |
|---|---|---|
| 语言 | TypeScript(全栈,ESM only,`"type": "module"`) | 单语言全栈,契约可共享 |
| 运行时 | Node.js 22 LTS | — |
| 包管理 | pnpm workspaces | monorepo 原生,严格依赖隔离 |
| 前端 | React 19 + Vite + TanStack Router | SPA;类型安全路由(路由状态即应用状态,配零过程状态) |
| 前端样式 | 设计令牌 + 原生 CSS(飞书蓝体系,不引 Tailwind/CSS-in-JS) | 两台 PRD 原型的令牌与组件视觉直接平移,双主题(工作台浅色/治理台深轨浅面)已验证 |
| 前端服务端状态 | TanStack Query | 缓存/失效语义现成;SSE 事件按信封目标失效对应缓存(§9.2) |
| 编排画布 | diagram-js(PRD 画布件 1:1 平移) | 布局引擎与画笔(flow-layout/flow-node-paint)是框架无关纯 TS,治理台原型已验证「连线即映射、画图即校验」与蛇形正交布线;React Flow 的 DOM 节点模型需整套重写布局与视觉,弃 |
| 后端 HTTP | Hono(自建应用,**Mastra 以库形态嵌入**) | 鉴权、岗位权限、SSE 全归我们;不暴露 Mastra 默认端点(ADR-001) |
| Agent/编排框架 | **Mastra**(`@mastra/core` + `@mastra/memory` + `@mastra/pg`) | 图编排(挂起/恢复/快照持久)、Agent 回路、会话记忆、工具系统(§6) |
| 模型接入 | Vercel AI SDK provider 工厂(经 Mastra) | 四元绑定(Provider/URL/API-Key/Model)落 provider 工厂 |
| 数据库 | PostgreSQL 16(单库多 schema) | 事件存储、台账自生长、Mastra 存储、队列同库(§7) |
| 系统自持 ORM | Drizzle ORM(+ drizzle-kit 迁移) | 类型安全、迁移随系统版本 |
| 台账自生长 | node-postgres(`pg`)直写(ledger adapter 内) | 运行时 DDL/DML 由声明生成,ORM 静态 schema 不适用 |
| 队列/定时 | pg-boss | PG 原生,先持久后投递、至少一次天然契合 |
| 代码契约校验 | zod | 仓库内 API/DTO |
| 声明数据校验 | ajv(JSON Schema 2020-12 子集) | 活动声明是运行时数据,不是编译期代码(ADR-003) |
| 测试 | Vitest;集成测试用 Testcontainers(PG) | domain 层测试零 mock |
| 门禁 | tsc / ESLint(flat) / dependency-cruiser / 契约快照 | §11 |
版本号以实现时最新稳定版为准;**新增任何表外依赖 = 契约变更,走 §12 申请**。
---
## 2. 仓库形态(唯一合法形态)
```
repo/
├── docs/ 两卷规范 + 本文件 + debt.md + adr/
├── apps/
│ ├── web/ 前端:工作台 + 治理台
│ │ └── src/
│ │ ├── app/ 路由、Provider、鉴权壳、SSE 连接。唯一组装层
│ │ ├── features/ 前端业务模块(§9 清单)
│ │ │ └── desk-task/
│ │ │ ├── index.ts 唯一出口
│ │ │ ├── contract.ts 对外类型
│ │ │ ├── internal/
│ │ │ └── __tests__/
│ │ └── shared/ 无业务语义:typed-fetch、sse hook、format
│ └── api/ 后端:五层的系统侧全部
│ └── src/
│ ├── app/ 启动、Hono 路由注册、Mastra 实例、DI 装配、workers
│ ├── modules/ 后端业务模块(§4 清单)
│ │ └── ledger/
│ │ ├── index.ts 唯一出口(纯 re-export)
│ │ ├── contract.ts 对外类型
│ │ ├── port.ts 本模块需要外界提供的接口
│ │ ├── CONTRACT.md 给 AI 读的地址簿
│ │ ├── internal/
│ │ │ ├── domain/ 纯逻辑,零 IO,零框架
│ │ │ ├── usecase/ 编排 domain,依赖 port
│ │ │ └── adapter/ 实现 port(Drizzle/pg/pg-boss/Mastra 只准在这)
│ │ └── __tests__/
│ └── shared/
├── packages/
│ ├── contracts/ 前后端唯一共享点:zod schema + 推导类型(§8.1)
│ ├── activity-sdk/ 活动开发面:基座六件接口 + mock 宿主 + 回放夹具(§8.2)
│ ├── sfeel/ S-FEEL 子集解析与求值,纯函数(§8.3)
│ ├── ui-kit/ 无业务语义展示组件(卡片原语/时间线/净化视图容器)
│ └── utils/ 纯函数,零依赖
└── activities/ 第一方活动包(「系统外开发」的仓内形态,§8.2)
└── <activity-name>/ 只准依赖 @sdk 与 @contracts;禁止 import apps/**
```
### 2.1 结构规则
- 一个模块 = 一个目录 = 一个能用一句话说清职责的业务概念,通常 5–20 个文件
- 模块目录名**单数名词、kebab-case**;模块**不可嵌套**,子概念放 `internal/`,大到需要独立就升格为平级模块
- 文件名 kebab-case,类型名 PascalCase,函数名 camelCase
- 禁止按技术类型分目录(`controllers/`、`services/` 各一坨),按业务概念聚拢
- `activities/` 下的活动包**不是模块**:它们是活动库的安装物,编译为单文件 ESM 产物,经 `activity-registry` 安装进系统;api 侧任何文件**不得静态 import** `activities/**`
---
## 3. 架构五层 → 代码落点
| 架构层(《操作系统》01) | 代码落点 |
|---|---|
| 交互层(工作台 + 治理台) | `apps/web`(features)+ `apps/api` 的 `interaction` / `message` / `human-tool` 模块(协议侧) |
| 流程层(触发器 + 流程包/工作流) | `process`、`trigger` 模块 |
> **2026-07-29 范式退役**:用户定「活动不再分 flow 和 loop,内部实现都是 Loop」——
> `flow-engine` 模块整个删除,`paradigm` 字段从清单与 `activity_package`/`task` 两表退场,
> 活动的业务模型只剩案例模型(CMMN + 说明书 + DMN)。本文提到 flow 范式的段落均为**历史记述**,
> 按当日结构写就;今天的实况是:一种活动、一个引擎(`loop-engine`)。后果见 docs/debt.md。
| 引擎层(流程引擎 + 活动基座) | 流程引擎在 `process` 内;活动基座六件 = `ledger` / `memory` / `toolbox` / `llm` / `interaction` + 契约类型(@contracts);范式运行器 = `loop-engine` / `flow-engine` / `skill-engine`;任务运行时 = `task` |
| 调用层(系统调用 · LLM 工具 · 人类工具 · 通道) | `toolbox`(功能工具/纯工具)、`llm`、`human-tool`、`channel` |
| 基座层(数据 · 台账 · 记忆 · 工具箱 · 活动库 · 流程包 · 事件存储) | PostgreSQL 四 schema(§7)+ `event` / `ledger` / `memory` / `toolbox` / `activity-registry` / `process` 各自的 adapter |
治理台九功能**不设**后端"governance 模块":每个功能是对应模块的管理用例 + `apps/api/src/app/routes/governance/*` 的路由组装;前端是 `gov-*` features。工作台五要素同理(`desk-*` features + 各模块用例)。
---
## 4. 后端模块清单
依赖层级 L1 最低;**import 只准指向更低层级模块的 `index.ts`;同层模块互不 import,协作走 port + app 装配**(§5)。
| 层 | 模块 | 职责(一句话) | 语义出处 |
|---|---|---|---|
| L1 | `event` | 统一事件标准:信封存储、归属解析、投递五诺(先持久后投递/同任务按序/至少一次+幂等/死信回执/无主兜底);监听注册表与出站关联记录存放于此——上层经出口写入,归属解析对表匹配,不反向依赖上层 | OS 06 |
| L1 | `org` | 员工与岗位:账号、岗位授权(客体三类×动作三种)、停用前置检查 | OS 02 |
| L1 | `llm` | LLM 实例:自定义模型名 → 四元绑定(Provider 枚举/URL/API-Key/Model),ModelGateway 含录制/回放缝 | OS 04/11 |
| L1 | `ledger` | 数据与台账:三级寻址、通用事务接口、schema 自生长(最大化合并/最宽类型/只增不破坏)、任务级草稿层与任务视图、写三分 | OS 04/09 |
| L2 | `memory` | 任务记忆(会话历史+窗口配置)、活动记忆四层存取、任务日志两级投影、蒸馏钩子执行 | OS 08 |
| L2 | `toolbox` | 功能工具注册与两审、调用契约、效应等级、连接配置声明与凭证绑定、调用留痕、纯工具作用域 | OS 07 |
| L2 | `channel` | 通道实例:入站归一(解析→系统写→数据事件)、出站发送 | OS 07 |
| L2 | `message` | 系统消息:通知、跨任务征答收件箱、死信与回执的人侧呈现 | OS 02/06 |
| L3 | `interaction` | 交互协议:对话流单入口、视图区推送、控制权按态转交、SSE 会话 | OS 02,AM 07 |
| L3 | `human-tool` | 人类工具:工单两模式(执行/验收)+ 对话卡族(决策/补全/评价)的卡协议与回传校验、到达面两路、跨人卡、驳回重答 | OS 05,AM 06 |
| L4 | `release` | 放行与落地:落地预检、放行写合并、幂等落地、落地失败卡、输出口发射 | OS 7.3/09,AM 06 |
| L4 | `activity-registry` | 活动库:包清单六区、安装两审(静态审+回放审)、包版本×资产版本、两维分类、.JS 装载、资产版本进化管线(候选增量状态机 → 回放重演 → 人放行生效 → 回退) | OS 11,AM 11 |
| L4 | `skill-engine` | skill 范式:技能脚本(结晶片段 = 确定性动作链)运行器——装配语义、三层软硬、**载方法不载权力**(脚本引用的工具仍过本活动白名单与调用契约);loop 临场装配 / flow 步引用 / 纯技能活动由 task 直跑 | AM 08 |
| L5 | `loop-engine` | loop 范式:CMMN+DMN 语义引擎(纯 domain)× Agent 回路(Mastra Agent 为 case worker)。**CMMN 子集封顶**:构件与生命周期以 AM 03 构件表 + AM 4.1 两副状态表为全集,不实现 CMMN 1.1 其余构件,扩构件 = 改本行 + ADR;**DMN 落点**:DRD 遍历、决策表求值与命中行留痕在本模块 domain,单元格谓词复用 `@sfeel` | AM 03/04 |
| L5 | `flow-engine` | flow 范式:步序声明运行器(四种步型、重试与兜底、事件即步条件) | AM 05 |
| L6 | `task` | 任务运行时:生命周期与终态五名裁定、Harness 预算执行器/停滞熔断、控制三语义、改派、断点续跑 | OS 04,AM 01/10 |
| L7 | `process` | 流程层:流程包定义与版本、边映射闭合校验、工作流实例(网关/审批点/失败走向/负责人四策略/聚合) | OS 03 |
| L7 | `trigger` | 触发器三类(周期两型/事件/登记),独立配置存放,判定与调用接口调起 | OS 03 |
| L8 | `minute` | 纪要:团队主动记录,可带触发器 | OS 02 |
| L8 | `report` | 汇报:日/周/月/年对任务日志的聚合投影 | OS 02/08 |
约束:
- 技能编排属于活动(AM:活动内容物含技能清单)——`skill-engine` 与 loop/flow 同列范式运行器;置于 L4 因两范式依赖它(临场装配/步引用),import 恒向下、免 port。
- 找不到归属的代码,先怀疑模块拆分错了,再考虑 `shared/`;`shared/` 只收无业务语义的纯工具
- 度量八指标(OS 8.3)是 `memory`(任务日志)与 `task`(终态/纠正信号)之上的**只读投影**,落 `report` 的用例,不设独立模块
- 鉴权定调:单组织 cookie session——登录、会话与岗位权限校验中间件在 apps/api 的 app 层实现,权限判定读 `org` 出口;不引 OAuth/OIDC 与多租户机制
---
## 5. 依赖方向(硬规则)
### 5.1 顶层
```
apps/web ──┐
├─→ packages/contracts ─→ (无依赖)
apps/api ──┘
├─→ packages/sfeel, packages/utils
apps/web ──→ packages/ui-kit
activities/* ──→ packages/activity-sdk ──→ packages/contracts
```
- `apps/web` 与 `apps/api` 互不 import;跨端唯一通道是 `packages/contracts`(路由契约 zod schema + 推导类型)。**禁止 Hono RPC 客户端**——它要求前端 import 后端类型,违反本条
- `packages/*` 不得反向 import `apps/*`;`packages/utils` 是叶子
- `activities/*` 只准 import `@sdk`、`@contracts` 与白名单纯库;`apps/**` 与 `activities/**` 互不静态 import(活动经安装装载,ADR-004)
### 5.2 应用内
```
app/ ─→ modules/<x>(只经 index.ts)─→ 更低层模块 index.ts ─→ shared/, packages/*
```
- `app/` 是唯一组装层:只有这里能同时认识多个模块,new 出 adapter、注入 port、注册路由/worker/Mastra 实例
- 模块 import 模块:**只准指向 §4 表中层级更低者的 `index.ts`**;同层互需(如 `release` 落地时要发落地失败卡 vs `human-tool`)一律由需要方声明 port、app 层把对方的用例注入
- 任何文件不得 import `modules/*/internal/**`(同模块自己除外)
### 5.3 模块内
```
adapter/ ──实现──→ port.ts ←──依赖── usecase/ ──→ domain/
```
- `domain/`:纯函数、值对象、领域错误。**零 IO、零框架、零 Date.now/Math.random**(时钟与随机走 port)。npm 白名单:`zod`、`decimal.js`、`date-fns` + 仓内纯包 `@contracts`、`@sfeel`、`@utils`
- `usecase/`:编排 domain,依赖 `port.ts` 声明的能力(`EventPublisher`、`ModelGateway`、`Clock`…),不 import 任何 adapter
- `adapter/`:实现 port。**Drizzle / pg / pg-boss / @mastra/\* / AI SDK 只准出现在 adapter 与 app**(§11 机器强制)
- 优先导出工厂函数而非 class
### 5.4 模块依赖图(权威在 §4 层级表,此图为速览)
```mermaid
graph BT
subgraph L1
event; org; llm; ledger
end
subgraph L2
memory; toolbox; channel; message
end
subgraph L3
interaction; human-tool
end
subgraph L4
release; activity-registry; skill-engine
end
subgraph L5
loop-engine; flow-engine
end
task --> loop-engine & flow-engine & activity-registry
loop-engine & flow-engine --> skill-engine
process --> task
trigger --> process
L5 --> L4 --> L3 --> L2 --> L1
```
---
## 6. Mastra 能力映射
Mastra 承担四件事:**图编排(含挂起/恢复与快照持久)、Agent 回路、会话记忆、工具定义**。它是实现件,不是架构件——**架构语义以两卷为准,Mastra 的概念不得泄漏进 domain/usecase**。
### 6.1 映射表
| 系统概念 | Mastra 机制 | 落点(adapter) |
|---|---|---|
| 流程包 → 工作流实例(图编排) | 流程包版本(DB 中的 DSL)动态编译为 `createWorkflow`/`createStep` 图;XOR 网关 = `.branch()`,AND 网关 = `.parallel()`,条件求值调 `@sfeel` | `process/internal/adapter/workflow-compiler.ts` |
| 审批点 / 活动节点等终态 | `suspend()` 挂起,快照持久于存储;审批卡回传、任务终态事件到达后按 runId `resume(payload)` | `process` adapter |
| 崩溃后续跑 | Mastra 快照 + 启动时恢复活跃 run(`restartAllActiveWorkflowRuns`) | `app/boot.ts` |
| flow 活动步序 | 每活动版本的步序声明编译为一条 Mastra workflow:步→step(重试参数随声明),人类工具步/监听步 = `suspend()`,人事件/数据事件消费者 `resume` | `flow-engine` adapter |
| loop 活动 Agent 回路 | Mastra `Agent`,`instructions/tools/model` 全部走 `({ requestContext }) =>` 动态形态:按任务注入案例模型装载、白名单工具、活动声明的模型;**回合由 usecase 驱动,不放开自由 maxSteps,预算在环外由 Harness 强制** | `loop-engine/internal/adapter/agent-runner.ts` |
| 任务记忆 = 会话历史 + 窗口配置 | `@mastra/memory` `Memory`,thread = 任务 ID、resource = 活动 ID;**窗口配置 ↔ `options.lastMessages`**(活动唯一记忆接口,AM 09) | `memory` adapter |
| LLM 工具 / 功能工具呈现给 Agent | `createTool`(zod 入参);功能工具由调用契约(JSON Schema)在注册时转换生成,execute 内走 `toolbox` 调用留痕 | `toolbox` adapter |
| 模型四元绑定 | Provider 枚举 → AI SDK provider 工厂(`baseURL`/`apiKey`)→ 模型实例;活动只见自定义模型名,解析在 `llm` | `llm/internal/adapter/model-gateway.ts` |
| 活动记忆四层 / 蒸馏 | **不用** Mastra working memory(语义不合:活动记忆是结构化四层、只经蒸馏写入)——自研 sys 表;装载选摘可选 PgVector 语义召回 | `memory` adapter |
### 6.2 边界三规则(机器强制,§11)
1. `@mastra/*` 与 AI SDK 只准出现在 `internal/adapter/**` 与 `app/**`
2. port 与 contract 一律用自定义类型,不得出现 Mastra 类型(`Agent`、`Workflow`、`Memory` 等不出 adapter)
3. Mastra 存储表(`mastra` schema)由 Mastra 单写;业务代码不得直读直写,投影一律经 `memory` 模块出口
### 6.3 骨架示例
流程包编译(process adapter,示意):
```ts
// process/internal/adapter/workflow-compiler.ts
import { createWorkflow, createStep } from '@mastra/core/workflows';
import { evaluate } from '@sfeel';
import type { ProcessPackage } from '@contracts';
export function compile(pkg: ProcessPackage, deps: CompilerDeps) {
const steps = pkg.nodes.map(node => createStep({
id: node.id,
execute: async ({ inputData, suspend, resumeData }) => {
if (resumeData) return deps.mapOutcome(node, resumeData); // 任务终态/审批回传到达
await deps.startNode(node, deps.assemble(node.inEdges, inputData)); // 起任务/发审批卡(边映射装配)
return suspend({}); // 等终态事件 resume
},
}));
// XOR → .branch([[d => evaluate(edge.cond, d), step]...]);AND → .parallel(...)
return createWorkflow({ id: pkg.id + '@' + pkg.version, /* zod io */ })
.then(/* 按 pkg.edges 组图 */).commit();
}
```
loop 回合驱动(loop-engine usecase,示意——三型行动与断点判定是 domain,Agent 是 port):
```ts
// loop-engine/internal/usecase/run-turn.ts
export function createTurnRunner(deps: { engine: CmmnEngine; agent: AgentPort; harness: HarnessPort }) {
return async function runTurn(taskId: TaskId) {
await deps.harness.assertBudget(taskId); // 预算环外强制
const enabled = await deps.engine.evaluate(taskId); // 语义引擎:Enabled 集与红绿灯(零 LLM)
const action = await deps.agent.decide(taskId, enabled); // Agent 决断(Mastra 在 adapter)
switch (action.kind) { // 三型行动,AM 04
case 'tool': return deps.engine.applyToolResult(taskId, await deps.agent.invoke(action));
case 'human': return deps.engine.suspendOnCard(taskId, action.card); // 调人 = 立监听听人
case 'listen': return deps.engine.registerListener(taskId, action.listener);
}
};
}
```
---
## 7. 持久层
单 PostgreSQL 实例,四个 schema,**各自单写者**——这是「资产与包分离,各自单写者」原则(OS 11.3)在存储上的同构:
| schema | 内容 | 写者 | 迁移方式 |
|---|---|---|---|
| `sys` | 系统自持:员工/岗位、LLM 实例、工具注册表、活动库(包×资产两序列)、流程包、触发器配置、事件存储、消息、任务与工作流台账、活动记忆四层、回放录制 | 各模块 adapter(经 Drizzle) | drizzle-kit,随系统版本 |
| `ledger` | 活动自生长台账:正式版、任务级草稿层、共享域 | 仅 `ledger` adapter(动态 DDL/DML,raw pg) | 声明合并自生长,无人工迁移 |
| `mastra` | Mastra 存储:线程/消息(任务记忆会话轴)、workflow 快照 | 仅 Mastra(`@mastra/pg` PostgresStore) | Mastra 自管 |
| `queue` | pg-boss 作业表 | 仅 pg-boss | pg-boss 自管 |
### 7.1 事件存储与投递五诺(实现要点)
| 承诺(OS 06) | 实现 |
|---|---|
| 先持久后投递 | 信封写 `sys.event` 与 pg-boss 入队在**同一事务** |
| 同任务按序 | 任务级串行消费:按目标 ID 取 advisory lock 的串行执行器 |
| 至少一次 + 幂等 | pg-boss 至少一次;消费侧幂等表(事件 ID × 消费者)唯一约束 |
| 回执与死信走系统通知 | 重试超限 → `message` 模块 port(app 装配) |
| 无主兜底 | 归属解析(`event` domain)无主 → `message` |
### 7.2 台账自生长与草稿层
- 声明合并(最大化合并/最宽兼容类型/只增不破坏)是**纯算法**,落 `ledger/internal/domain/schema-merge.ts`,零 mock 全覆盖测试;DDL 生成与执行在 adapter
- 草稿层:推荐形态为 `ledger.__draft`(task_id × 对象 × 主键 × patch jsonb),读时叠加出**任务视图**;物理形态可换,**功能约束不可变**:读己之写、不产生数据事件、放行合并原子且过数据冲突检查(与 `release` 协作经 port)
- 写三分(系统写/草稿写/放行写)是 `ledger` 出口上的**三个不同方法**,不是一个带 flag 的方法——调用方拿不到"绕门"的形态
### 7.3 录制与回放(回放审的底盘)
两条缝,一个开关:`llm` 的 `ModelGateway` 与 `toolbox` 的 `ToolInvoker` 都支持 `record | replay | live` 模式,录制落 `sys.replay_*`(按任务×步序号)。回放审(OS 11.2)= 用 replay 模式的 adapter 重跑引擎;样例集/契约测试集走同一夹具(@sdk 提供)。**新增任何外部 IO 必须过这两缝之一,否则回放审对它失明——这是新增 port 的验收条件。**
---
## 8. packages 清单
### 8.1 `@contracts`(前后端唯一共享点)
只有 zod schema、常量、品牌类型与推导类型,零实现。内容物按文件:
```
envelope.ts 信封六字段 + 四类事件(数据/时间/人/控制)
terminal.ts 终态五名:'released'|'blocked'|'stalled'|'exhausted'|'terminated' + 非终态注记
card.ts 人类工具 schema:工单两模式(执行 = 空表单/验收 = 可修订报告)+ 对话卡族(决策/补全/评价)+ 落地失败卡(执行工单预置形态)+ 回传协议
contract7.ts 契约七件(输入 data+message / 输出口 / 必交付物 / 验收判据 L1–L5 / 禁区 / 授权边界 / 资源声明)
process-package.ts 流程包 DSL:节点/边与载荷映射/审批点/两种网关/失败走向四选一/S-FEEL 表达式引用
manifest.ts 活动包清单六区(身份/契约/声明/资产初始版/样例集/代码入口)
ledger.ts 三级寻址(域.对象.字段)、事务接口 DTO、任务视图 DTO
api/ 路由契约(按面分文件:workbench.ts / governance.ts / ingest.ts)
id.ts 品牌 ID(TaskId/WorkflowId/ActivityId/EventId/StaffId…)
```
修改本包任何导出 = 契约变更(§12.4)。
### 8.2 `@activity-sdk` 与 `activities/`
- SDK = 活动开发者看到的世界:**基座六件接口**(`ContractSpec` / `InteractionHandle` / `MemoryHandle` / `LedgerHandle` / `ToolInvoker` / `LlmHandle`)+ `defineActivity()` 入口 + 本地开发宿主(mock 工具表、模拟人类工具(工单/卡)、回放夹具)+ 编译预设(tsup → 单文件 ESM)
- 活动包 ABI(ADR-004):默认导出 `defineActivity({...})` 的返回值;包内不得出现 SDK 与白名单纯库之外的 import,不得触任何环境能力(`process.env`/`fs`/`fetch`…)——**静态审扫描产物 import 与全局引用,不过不上架**
- `activities/` 是第一方活动的仓内工位,与 api 之间**只有安装通道**(registry 装载编译产物),没有代码通道
### 8.3 `@sfeel`
S-FEEL 子集(OS 03 定义域)的解析与求值,纯函数。被 `process`(边条件/失败走向)、`flow-engine`(条件谓词)、`trigger`(判定)、前端 `gov-process`(画布即时校验预览)共用——**求值语义一份代码,前后端同源**;后端裁决为权威。
---
## 9. 前端(apps/web)
> **范式 × 交互,双层边界(定调)**:**执行范式是后端实现逻辑的模型,交互模型是前端呈现协议——两层不冲突**(AM 本就规定两范式统一执行契约、对交互层不可区分)。范式 = loop / flow / skill,只存在于引擎层;前端**永远感知不到范式**,只感知统一交互模型——**任务面四区**(任务列表/标题区/工作区/对话区)、**工单两模式**(验收 = AI 拟好的报告可修改;执行 = 指导书 + 空表单)、**对话卡族**(决策/补全/任务评价,补全含「没法填」旁路,决策含自定义收尾项),以 2026-07-26 原型定稿为准。旧文「四卡」是交互层旧措辞,映射:执行卡→执行工单、放行卡→验收(工作区报告 + 对话区验收单)、决策/补全→对话卡;两卷交互章节措辞待同步,同步前**交互结构与视觉以原型为准**,语义打架的落点照前言规矩停下来报告。
### 9.1 features 清单
| 组 | feature | 职责 |
|---|---|---|
| 工作台 | `desk-task` | 任务要素四区:任务列表 / 标题区 / 工作区(工单两模式 + 驾驶舱)/ 对话区(卡族 + composer 锁);控制权按态转交的呈现 |
| | `desk-approval` | 审批点收件与裁决 |
| | `desk-message` | 消息:通知、跨任务征答就地作答、死信回执 |
| | `desk-minute` | 纪要(可带触发器) |
| | `desk-report` | 汇报:日/周/月/年聚合 |
| 治理台 | `gov-task` | 任务管理:监控、控制事件、改派、任务日志查看 |
| | `gov-staff` | 员工管理:账号、岗位授权、停用检查 |
| | `gov-process` | 流程包编排画布(diagram-js 画布件平移):**连线即映射、画图即校验、不闭合不得发布**;导出依赖文件 |
| | `gov-trigger` | 触发器配置(三类) |
| | `gov-activity` | 活动库:安装/两审结果/两维分类/包×资产版本 |
| | `gov-tool` | 工具管理:注册、白名单、凭证绑定 |
| | `gov-data` | 数据管理:域/对象目录、schema 视图、**记录浏览与管理**(检索/分页/记录详情;修订走草稿写、归档走确认带)、清理归档 |
| | `gov-memory` | 记忆管理:活动记忆四层、修剪 |
| | `gov-system` | 系统管理:LLM 实例四元绑定、通道实例、系统用途模型 |
### 9.2 前端硬规则
- 服务端交互只经 `shared/typed-fetch` + `@contracts/api` 的路由契约;组件内禁止裸 `fetch`
- 对话流与视图区推送经单一 SSE 连接(`app/` 建连,feature 订阅);**前端零过程状态**(OS 10):刷新即从服务端投影重建
- 视图区容器渲染活动推送的 HTML:**沙箱 iframe(`srcdoc` + `sandbox` 属性)或净化后注入,二选一由 `ui-kit` 的 `ViewContainer` 统一实现,feature 不得自行 `dangerouslySetInnerHTML`**;无脚本执行权(AM 07)
- 卡片表单由 `@contracts/card.ts` schema 驱动渲染与校验,与后端回传校验同 schema
- SSE 事件到达 → 按信封目标(任务/工作流/要素)**失效对应 TanStack Query 缓存**,由订阅层统一做;组件禁止自行轮询或手写缓存
- 路由状态即应用状态:当前区/页签/下钻栈可从 URL 完整还原(TanStack Router 类型化 search params);内存路由栈只是原型期形态,正式系统必须可深链
---
## 10. 模块骨架与命名
### 10.1 固定骨架
新建模块严格照抄,不许发明变体(以 `ledger` 为例):
```ts
// modules/ledger/contract.ts —— 对外类型,只有类型和常量,没有实现
export type LedgerAddress = { domain: string; object: string; field?: string };
export class DraftConflictError extends Error {}
```
```ts
// modules/ledger/port.ts —— 本模块要外界提供的能力
export interface EventPublisher { publish(e: EnvelopeDraft): Promise<void> }
export interface Clock { now(): Date }
```
```ts
// modules/ledger/index.ts —— 唯一出口。只做 re-export,不写逻辑
export type { LedgerAddress } from './contract';
export { DraftConflictError } from './contract';
export type { EventPublisher, Clock } from './port';
export { createLedgerService } from './internal/usecase/ledger-service';
```
规则:
- `index.ts` 只允许 `export` 语句,零逻辑零副作用
- `contract.ts` 不得 import `internal/`
- `internal/` 内禁止 barrel 文件,直接引用具体文件
- 每个模块根有 `CONTRACT.md`(模板见 §12.6)
### 10.2 术语表(中文概念 → 代码标识符)
命名以本表为准,禁止一概念多名:
| 中文 | 代码 | 中文 | 代码 |
|---|---|---|---|
| 活动 / 任务 | activity / task | 放行 / 落地 / 预检 | release / landing / precheck |
| 流程包 / 工作流 | process package / workflow | 驳回(重做/迭代) | reject (redo / revise) |
| 触发器(周期/事件/登记) | trigger (periodic / event / registration) | 终态五名 | released / blocked / stalled / exhausted / terminated |
| 审批点 / 网关(并行/异或) | approval point / gateway (parallel / exclusive) | 叫停 / 暂停 / 终止 | halt / pause / terminate |
| 边与载荷映射 | edge mapping | 台账 / 草稿层 / 任务视图 | ledger / draft layer / task view |
| 负责人 / 岗位 | owner / position | 信封 / 留痕 / 归侧 | envelope / trace / attribution |
| 工单两模式(执行/验收) | work order (execution / acceptance) | 归属解析 / 无主兜底 / 死信 | ownership resolution / unowned fallback / dead letter |
| 对话卡族:决策/补全/评价 | decision / elicitation / rating card | 技能范式 / 结晶片段 | skill paradigm / crystallized fragment |
| 输出口(必发/选择) | output port (mandatory / selective) | 催办 / 时限 / 监听 | nudge / deadline / listener |
| 契约七件 | seven-part contract | 蒸馏 / 装载 / 技能 | distill / load / skill |
| 验收判据 L1–L5 | acceptance criteria | 白名单 / 禁区 / 授权边界 | allowlist / prohibition / authorization boundary |
| 活动记忆四层:事实/模式/裁决/例外 | fact / pattern / verdict / exception layer | 停滞 / 熔断 / 预算 | stall / circuit-break / budget |
| 工作台 / 治理台 | workbench / governance console | 改派 / 起单 | reassign / initiate |
---
## 11. 机器强制配置
规范靠嘱咐是守不住的。以下配置必须存在且在 CI 执行;**约束报错时,改代码,不改约束**。
### 11.1 tsconfig(根)
```jsonc
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitOverride": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"moduleResolution": "bundler",
"paths": {
"@contracts": ["./packages/contracts/src/index.ts"],
"@sdk": ["./packages/activity-sdk/src/index.ts"],
"@sfeel": ["./packages/sfeel/src/index.ts"],
"@ui": ["./packages/ui-kit/src/index.ts"],
"@utils": ["./packages/utils/src/index.ts"],
"@modules/*": ["./apps/api/src/modules/*"],
"@features/*": ["./apps/web/src/features/*"]
}
}
}
```
### 11.2 更硬的封装(启用)
每个模块加 workspace 包 `package.json`,用 `exports` 做解析层封装(穿透 internal 直接解析失败):
```jsonc
{ "name": "@order", "private": true, "exports": { ".": "./index.ts" } }
```
### 11.3 ESLint(要点)
```js
'no-restricted-imports': ['error', { patterns: [
{ group: ['**/internal/**'], message: '禁止穿透模块出口' },
{ group: ['@modules/*/*', '@features/*/*'], message: '只能 import 到模块根' },
{ group: ['../../../*'], message: '跨模块请用路径别名' },
]}],
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/ban-ts-comment': 'error',
```
### 11.4 dependency-cruiser(在基线规则之上,JCAI 增补)
基线八条(no-circular / no-cross-module-internal / app-not-into-internal / domain-is-pure / usecase-not-to-adapter / web-not-to-api / packages-are-leaves / no-orphans)由 M0 依名实现(见本节末注),之上增补:
```js
{ name: 'framework-only-in-adapter',
comment: 'Mastra/AI SDK/Drizzle/pg/pg-boss 只准在 adapter 与 app',
severity: 'error',
from: { path: 'src/modules/.+/internal/(domain|usecase)/' },
to: { path: 'node_modules/(@mastra|ai|@ai-sdk|drizzle-orm|pg|pg-boss)' } },
{ name: 'tier-order',
comment: '模块只准依赖 §4 表中更低层级;矩阵与本文件同步维护',
severity: 'error',
from: { path: '^apps/api/src/modules/(process|trigger|minute|report)/' },
to: { path: '^apps/api/src/modules/$1/' , pathNot: '…按 §4 层级表展开为逐层规则…' } },
{ name: 'activities-isolated',
severity: 'error',
from: { path: '^activities/' }, to: { path: '^apps/' } },
{ name: 'apps-not-into-activities',
severity: 'error',
from: { path: '^apps/' }, to: { path: '^activities/' } },
```
`tier-order` 的完整展开由 `tools/gen-depcruise-tiers.ts` 从 §4 层级表(以 JSON 形式维护在 `tools/module-tiers.json`)生成——**改层级 = 改 JSON + 改本文件 §4,一起提交**。
八条基线规则不随包携带定义——M0 依规则名实现(语义见其名与 §5 的依赖规则);**本文件自足,门禁配置不依赖任何外部基线文件**。
### 11.5 契约快照与 CI 门禁
```bash
pnpm tsc --noEmit
pnpm eslint .
pnpm depcruise apps packages activities
pnpm api:check # tsc --emitDeclarationOnly → api-report/ 与 git diff 比对
pnpm test
```
改 `internal/` 时 `api-report/` 纹丝不动;碰了公开契约,diff 立刻可见。
---
## 12. AI 协作协议(多 Agent 并行 · 主 Agent 编排制)
### 12.0 角色与授权矩阵
三层角色,授权自上而下收窄:
| 角色 | 职责 | 权限要点 |
|---|---|---|
| 用户(终审) | 只审三类保留项 | ①破坏性契约变更(12.4) ②新增 npm 依赖(§1 表外,12.5#3) ③修改门禁与规则(§11 配置、本文件、module-tiers.json,12.5#8) |
| 主 Agent(唯一集成人) | 拆承包工单派活、仲裁跨模块契约、把关合入主干(12.7)、维护 docs/debt.md 与 ADR | **契约单写者**:`@contracts` 与全部模块契约面(index.ts / contract.ts / port.ts / CONTRACT.md)只经它变更或合入 |
| 子 Agent(模块承包者) | 在承包工单允许范围内自主实现与测试 | 工单范围内 `internal/**` 与 `__tests__/**` 自由改动,不逐次申请;触碰契约面即停,向主 Agent 提变更申请 |
### 12.1 三段式工作流(承包工单级)
三段式以**承包工单**为单位,不以单次改动为单位:①侦察(子 Agent 领单后只读摸底:相关模块 CONTRACT.md 摘要、文件清单、当前公开契约签名、改动落点判断)→ ②计划(变更申请单,交主 Agent 批)→ ③执行(严格按单,不增单外文件)。工单范围内的 internal 改动不再逐次申请;越界即停(12.3)。
### 12.2 变更申请单模板
```
## 变更申请
申请人: <子 Agent · 工单号>
审批人: 主 Agent(涉 12.0 用户保留三类的,由主 Agent 上升)
模块: apps/api/src/modules/ledger
变更层: internal/domain
允许修改: internal/domain/schema-merge.ts;__tests__/schema-merge.test.ts(新增)
禁止修改: index.ts, contract.ts, port.ts, 其他任何模块
契约影响: 无——公开签名不变,api-report/ 预期无 diff
新增依赖: 无
新增测试: 同名异型取最宽类型;互斥类型拦下报错
风险: 安装静态审共用该函数,需回归 activity-registry 测试
```
### 12.3 越界条款
判断必须改申请范围外的文件才能完成任务时:**立即停止编码**,输出「需要越界:原因 / 最小方案 / 替代方案」,等主 Agent 确认(涉用户保留三类的由主 Agent 上升)。不得先改再说明。
### 12.4 契约变更的额外要求
修改 `index.ts` / `contract.ts` / `port.ts` / `@contracts` 任何导出,除三段式外还需:①depcruise 反查列出全部下游受影响模块;②同步更新该模块 CONTRACT.md;③更新 api-report/ 快照并单独说明;④破坏性变更走「先加新的、旧的标 `@deprecated`、下个迭代删除」三步。契约变更的执行与合入只归主 Agent(子 Agent 以提案交单);破坏性变更须用户终审(12.0)。
### 12.5 禁止清单(未经授权一律禁止;授权主体按 12.0 矩阵——契约面归主 Agent,#3 / #8 与破坏性契约归用户)
1. 修改任何 `index.ts` / `contract.ts` / `port.ts` / `@contracts`
2. 修改申请范围之外的任何文件;顺手重构、顺手改格式、顺手升级依赖
3. 新增 npm 依赖(§1 表外)
4. `any`、`as unknown as`、`@ts-ignore`、`@ts-expect-error`
5. 在 `domain/` 写 IO(fetch、fs、db、`Date.now`、`Math.random`)
6. 全局可变状态、单例缓存、模块级 `let`
7. 删除或跳过已有测试
8. 修改 `.dependency-cruiser.js` / `eslint.config.js` / `tsconfig.json` / `tools/module-tiers.json` 让报错消失
9. **绕过写三分**:任何台账写入不经 `ledger` 出口的三个方法之一
10. **绕过两缝**:任何模型调用不经 `ModelGateway`、任何工具执行不经 `ToolInvoker`(回放审会失明)
### 12.6 提交前自检 + CONTRACT.md
提交前自检清单,逐条打勾:diff 不超单 / tsc / eslint / depcruise / api-report / 有测试 / 无 any / 无单外重构 / 未动约束配置。每模块 CONTRACT.md 模板:
```markdown
# module: ledger
职责: 台账:三级寻址、事务接口、schema 自生长、草稿层与任务视图、写三分
不负责: 放行时机与预检(见 release)、数据事件投递(见 event)
公开出口: index.ts —— <逐项列出>
依赖: @contracts;port:EventPublisher(app 注入 event)、Clock
被依赖: channel, release, activity-registry, loop-engine, flow-engine, task, trigger
不变量: 正式版写入必产生数据事件;草稿写不产生事件;同名异型取最宽兼容类型;声明只增不破坏
影响半径: index/contract/port 未变 → 对外零影响
```
### 12.7 git 工作流(并行合入)
- 主干受保护:**只有主 Agent 合入**;合入前五门禁全绿(§11.5),且 api-report/ 的 diff 与申请单「契约影响」声明一致
- **分支即工单**:每张承包工单一条分支 `wo/<模块>-<短述>`;子 Agent 只在自己分支提交,不碰他人分支与主干
- 合入 rebase 后快进(或 squash),保持线性历史;冲突回子 Agent 分支解决,主干上不解决冲突
- 契约面变更单独成分支、单独合入,并**先于**依赖它的实现工单
- 提交信息 `<模块>: <一句话>`,正文引用工单号
---
## 13. 对基线工程规范的调整清单
依据「不合理可调整」授权,差异全部登记于此。基线工程规范不随包——本表仅记录本文件的出身差异,**本文件自足生效,无需回查基线**:
| # | 调整 | 理由 |
|---|---|---|
| 1 | `packages/` 增加 `activity-sdk`、`sfeel` 两个**带业务语义**的包(基线只允许 contracts/ui-kit/utils) | 活动 SDK 必须可独立发布给系统外开发者;S-FEEL 求值前后端同源。二者仍受「叶子不依赖 apps」约束 |
| 2 | 新增顶层 `activities/` 工作区 | 「系统外开发」的仓内形态;以 depcruise 双向隔离代偿物理分仓 |
| 3 | 模块互依从「可以互相依赖(经 index)」收紧为「**只准依赖更低层级;同层走 port + app 装配**」 | 20 个模块的自由互依无法靠"不得成环"事后拦截;分层表可生成 depcruise 规则,机器可证 |
| 4 | domain npm 白名单增补仓内纯包 `@contracts` / `@sfeel` / `@utils` | 纯类型与纯函数,不破坏零 IO |
| 5 | 禁止清单增补 #9 写三分、#10 两缝 | 本系统的安全根基在放行门与回放审,须与结构规则同级强制 |
---
## 14. 决策记录(ADR)
已决(全文为准,细节在 `docs/adr/` 逐条建档):
- **ADR-001** 自建 Hono 应用,Mastra 以库形态嵌入;不用 Mastra server 对外暴露(鉴权/岗位权限/SSE 全归 app 层;开发期可另起 entry 用 mastra playground)
- **ADR-002** 持久分工:Drizzle(sys)/ raw pg(ledger 动态 DDL)/ pg-boss(队列定时)/ @mastra/pg(mastra schema),四写者互不越界
- **ADR-003** schema 方言:**声明层 = JSON Schema 2020-12 子集**(活动声明是数据,可 diff 可版本),**代码层 = zod**;运行时校验用 ajv;声明→zod 仅在工具呈现给 LLM 时转换
- **ADR-004** 活动 .JS 装载 ABI:单文件 ESM,默认导出 `defineActivity()`;能力全部注入(基座六件句柄),零环境能力;静态审扫描产物 import 与全局引用
- **ADR-005** 工具 `validate` 能力位:调用契约 `capabilities.validate?: true`,预检存在即调(OS 7.3 落地预检第四项)
- **ADR-006** S-FEEL 自研于 `@sfeel`,子集文法冻结于 OS 03;不引第三方 FEEL 引擎
留白(实现期再定,各建 ADR 后方可动工):鉴权会话与凭证加密方案;JSON Schema→zod 转换库选型;活动装载的 worker 隔离硬化;活动记忆向量召回启停;e2e 框架。
---
## 15. 实施序(里程碑)
每个里程碑必须先立骨架(index/contract/port/CONTRACT.md + 空测试)再填实现;门禁从 M0 起全绿。
| 里程碑 | 内容 |
|---|---|
| M0 | 工作区骨架 + 全部门禁跑通(tsc/eslint/depcruise/api:check/test 空转绿)+ 环境自举:docker-compose.yml(PG16,四 schema 初始化)、种子脚本(五名员工与岗位)、tools/module-tiers.json(从 §4 表生成)与 gen-depcruise-tiers.ts |
| M1 | 底座:`@contracts` 首版、`event`、`ledger`、`org`、`llm`(含录制回放缝)|
| M2 | 基座能力:`memory`、`toolbox`、`channel`、`message` |
| M3 | 执行面:`interaction`、`human-tool`、`release`、`activity-registry`、`@sfeel` |
| M4 | 范式引擎与任务:`skill-engine`、`loop-engine`、`flow-engine`、`task`(断点续跑三份持久贯通)|
| M5 | 流程层:`process`(编排校验 + 工作流实例)、`trigger`;`@activity-sdk` + 首个样例活动(`activities/`)|
| M6 | 前端:工作台五要素 → 治理台九功能;`minute`、`report` |
并行纪律(主 Agent 编排,12.0):**里程碑内模块并行承包**——同层模块互不依赖,天然可并行;**跨里程碑骨架先行**——下一里程碑各模块的 contract.ts / port.ts 骨架由主 Agent 提前冻结,下游子 Agent 对 port 的 mock 开工,不等上游填完;合入顺序按层级自低向高(§4)。
---
## 16. 新建模块检查清单
```
[ ] 目录名单数名词、kebab-case,且已登记 §4 层级表与 tools/module-tiers.json
[ ] 有 index.ts(纯 re-export)、contract.ts(纯类型)、port.ts(后端)
[ ] usecase 不 import adapter;domain 零 IO;Mastra/DB 只在 adapter
[ ] internal/ 无 barrel 文件
[ ] 有 CONTRACT.md;有 __tests__/,domain 测试零 mock
[ ] depcruise 通过:无入边指向 internal,无同层依赖
[ ] 若声明新 port:确认外部 IO 已过录制回放两缝之一(§7.3)
```
---
## 17. 例外与演进
- 本文件的任何例外必须写进本文件并注明原因与预期清理时间;散落的 `eslint-disable` 不算例外,算违规
- 历史违规集中登记 `docs/debt.md`:旧路径 `warn`、新代码 `error`,清零后升级
- 规则要改就改本文件并同步改配置(含 `tools/module-tiers.json`),一起提交。只改配置不改文档 = 规则失效