Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
43 KiB
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. 一句话改动原则
任何一次改动都必须能被一句话精确描述:"只改 <模块路径> 的 <层>,公开契约不变。"
这句话成立的充要条件:
- 每个模块有唯一物理边界(一个目录)
- 每个模块有唯一对外出口(
index.ts) - 依赖方向单向无环
- 以上三条由工具强制(§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 |
| 鉴权 | Keycloak(统一鉴权中心)+ jose 本地验签 |
资源服务器:RS256 / iss / aud 本地验,不回源;权限只到端(ADR-012) |
| 声明数据校验 | 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 侧任何文件不得静态 importactivities/**
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 |
| L1 | mail |
邮箱账号与收发:两条接入路(IMAP/SMTP · 企业微信邮件 API)的适配、轮询拉取与入站留痕、订阅分发、出站留痕;凭证密封、跨进程租约 | ADR-013 |
| 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的用例,不设独立模块 - 鉴权定调(ADR-012):统一鉴权中心(Keycloak)资源服务器——后端只验 Bearer JWT;鉴权中间件在 apps/api 的 app 层实现,身份判定读
org出口;权限只到端(设计中心 / 运营中心 / 工作台),角色→端由运营中心的表定,路由归属写在契约的access上;不做多租户
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/*不得反向 importapps/*;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落地时要发落地失败卡 vshuman-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、@utilsusecase/:编排 domain,依赖port.ts声明的能力(EventPublisher、ModelGateway、Clock…),不 import 任何 adapteradapter/:实现 port。Drizzle / pg / pg-boss / @mastra/* / AI SDK 只准出现在 adapter 与 app(§11 机器强制)- 优先导出工厂函数而非 class
5.4 模块依赖图(权威在 §4 层级表,此图为速览)
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)
@mastra/*与 AI SDK 只准出现在internal/adapter/**与app/**- port 与 contract 一律用自定义类型,不得出现 Mastra 类型(
Agent、Workflow、Memory等不出 adapter) - Mastra 存储表(
mastraschema)由 Mastra 单写;业务代码不得直读直写,投影一律经memory模块出口
6.3 骨架示例
流程包编译(process adapter,示意):
// 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):
// 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.tsschema 驱动渲染与校验,与后端回传校验同 schema - SSE 事件到达 → 按信封目标(任务/工作流/要素)失效对应 TanStack Query 缓存,由订阅层统一做;组件禁止自行轮询或手写缓存
- 路由状态即应用状态:当前区/页签/下钻栈可从 URL 完整还原(TanStack Router 类型化 search params);内存路由栈只是原型期形态,正式系统必须可深链
10. 模块骨架与命名
10.1 固定骨架
新建模块严格照抄,不许发明变体(以 ledger 为例):
// modules/ledger/contract.ts —— 对外类型,只有类型和常量,没有实现
export type LedgerAddress = { domain: string; object: string; field?: string };
export class DraftConflictError extends Error {}
// modules/ledger/port.ts —— 本模块要外界提供的能力
export interface EventPublisher { publish(e: EnvelopeDraft): Promise<void> }
export interface Clock { now(): Date }
// 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不得 importinternal/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(根)
{
"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 直接解析失败):
{ "name": "@order", "private": true, "exports": { ".": "./index.ts" } }
11.3 ESLint(要点)
'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 依名实现(见本节末注),之上增补:
{ 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 门禁
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 与破坏性契约归用户)
- 修改任何
index.ts/contract.ts/port.ts/@contracts - 修改申请范围之外的任何文件;顺手重构、顺手改格式、顺手升级依赖
- 新增 npm 依赖(§1 表外)
any、as unknown as、@ts-ignore、@ts-expect-error- 在
domain/写 IO(fetch、fs、db、Date.now、Math.random) - 全局可变状态、单例缓存、模块级
let - 删除或跳过已有测试
- 修改
.dependency-cruiser.js/eslint.config.js/tsconfig.json/tools/module-tiers.json让报错消失 - 绕过写三分:任何台账写入不经
ledger出口的三个方法之一 - 绕过两缝:任何模型调用不经
ModelGateway、任何工具执行不经ToolInvoker(回放审会失明)
12.6 提交前自检 + CONTRACT.md
提交前自检清单,逐条打勾:diff 不超单 / tsc / eslint / depcruise / api-report / 有测试 / 无 any / 无单外重构 / 未动约束配置。每模块 CONTRACT.md 模板:
# 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-012 统一鉴权中心(Keycloak):资源服务器本地验签(
jose),Staff.id=sub;权限只到端,sys.role_access可改;名册 Admin API 同步 + 首登即建;60 秒票据给 SSE 与下载;口令 / 会话 / 岗位授权退役(取代 ADR-010)
留白(实现期再定,各建 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),一起提交。只改配置不改文档 = 规则失效