# 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 | | 鉴权 | 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) └── / 只准依赖 @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` 的用例,不设独立模块 - 鉴权定调(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/*` 不得反向 import `apps/*`;`packages/utils` 是叶子 - `activities/*` 只准 import `@sdk`、`@contracts` 与白名单纯库;`apps/**` 与 `activities/**` 互不静态 import(活动经安装装载,ADR-004) ### 5.2 应用内 ``` app/ ─→ modules/(只经 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 } 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-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`),一起提交。只改配置不改文档 = 规则失效