This repository has been archived on 2026-09-13. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
BPOS-MVP/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

43 KiB
Raw Blame History

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 层级表,此图为速览)

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,示意):

// 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.ts schema 驱动渲染与校验,与后端回传校验同 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 不得 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(根)

{
  "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 与破坏性契约归用户)

  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 模板:

# 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),一起提交。只改配置不改文档 = 规则失效