@bpos/activity-contract (13.3.0)
Installation
@bpos:registry=https://gitea.akhr-ai.com/api/packages/ChaiHao/npm/npm install @bpos/activity-contract@13.3.0"@bpos/activity-contract": "13.3.0"About this package
@bpos/activity-contract —— 脚手架契约
系统给活动的只有一样东西:这份契约。 契约以内是系统;契约以外、活动包以内的一切归活动。 那就是黑盒线的准确位置。
形状全部是 zod,不是 TS 接口:每次跨界调用双向 safeParse —— 系统不信任活动交来的,
活动侧的 SDK 也校验系统答的,装不装 SDK 都拦得住。
规范在《操作系统系统设计》:§6.1(清单)· §3(操作表单)· §6.2(活动进程)· §6.4(能力面的名词表)· §6.5(事件表)· §4.2(工单那一组)· §2.8(业务数据)· §7.3(交出的两条路)· §12(令牌只对本任务节点有效)。
三样东西
| 在哪 | 是什么 | |
|---|---|---|
| 线材 | src/index.ts |
递入信封 · 事件表 · 名词表 · 交出与错误信封 |
| 清单与操作表单 | src/manifest.ts |
bpos-activity.json 的形状(manifestSchema,含 declares.tables[] 的表声明)与工单定义那三段,加机检用的几把尺子(manifestGaps / outputIssues / orderIssues / tableIssues / artifactIssues / commitIssues / chartIssues / archiveIssues),以及件上 ref 那一格的值形状(refValueSchema / refIsEmpty / refKeyOf)与主键串的编解码(encodeRefKey / decodeRefKey) |
| 不在这里的 | — | 实践的图(graphSchema 那一组)——图归实践、在系统侧编排,活动不知道有边 |
清单在这里而不在工作台契约里,是因为它是活动交付物的形状:包一搬出这个仓,
手写 bpos-activity.json 的人得拿得到那把尺子。构建时 manifestSchema.parse(自己的清单),
写坏了停在人改得动的地方,不必等装包那一刻由服务端说。
引用方向只许一边:本包不引 web/api/contracts;工作台契约单向引本包的清单形状,
再原样导出(接缝只有 web/api/contracts/plugin/manifest.ts 一处)。两向各有一条用例钉着:
本包的 tests/boundary.test.ts 钉禁止的那一向,server 的 tests/boundaries.test.ts R8 钉许可的那一向。
两个方向,两条线材
| 方向 | 线材 | 形状 |
|---|---|---|
| 系统 → 活动 | POST http://127.0.0.1:<BPOS_EVENT_PORT>/events |
eventEnvelopeSchema;进程答 eventAckSchema 即确认 |
| 活动 → 系统 | POST <BPOS_CAPABILITY_BASE>/call |
capabilityCallSchema → capabilityResultSchema |
| 活动 → 系统 | POST <BPOS_CAPABILITY_BASE>/handoff |
handoffSchema → handoffOutcomeSchema |
信封(环境变量)见 ACTIVITY_ENV:taskId · nodeRunId · nodeId · token · base · eventPort。
一个任务节点一个进程:进程知道自己是哪一单的哪一步,不知道图长什么样、别的步是谁。
事件
enter · work-order-reply · work-order-saved · human-say · timer ·
control · handoff-refused(现算一份在 SYSTEM_EVENTS)。
每条带 id、按本任务节点内的先后顺序投、至少送到一次——活动用自己的实例状态去重,
系统不重放(§6.2)。每种事件的来处见 SYSTEM_EVENT_SOURCE:发出去的每一个口对着一条回来的路。
名词
工单 4(create · read · write · void)· 对话 2(say · input)· 模型 1 ·
业务数据 1(table.read,只读)· 工作持久 4(state · memory 各两个)· 文件 4(get · text ·
put · info)· 定时 1 · 本步读数 1 · 留痕 1(现算一份在 CAPABILITIES)。
写业务库不在里面(7.0):落业务库只有工单提交一个入口——活动把要落的东西摆进工单的
数据对象、在数据表单的 commit 里声明落到哪,人提交、系统在那一个事务里落定并盖章。
归档同理随落定走(件上标了 archive 的文件格),所以没有单独的归档动作。
发卡也不在里面:对话区没有卡——要人拿个主意、补几格信息,派一张一两格的执行工单; 对象的新建同样只从工单落定来,系统不替活动建对象。 审批不在里面:活动不声明审批、不知道审批、收不到裁决。 邮箱也不在里面:信箱只服务系统的事件规则,与员工自己那只接通之后的全部同步——活动既不发信、也不收信。
什么改动要升大版本
往 CAPABILITIES / SYSTEM_EVENTS / CAPABILITY_ERRORS 这几个封闭集里减一个值,
对已经打好的活动包是破坏性的——那些包把旧的枚举内联进了 dist/activity.mjs。加一个值
破不破,看这个值从哪一头来:系统答出去的(一种事件、一个拒绝码)加了就破——老包拒收并报
「系统答的形状不对」,而毛病在系统;活动发出来的(一个能力名词)加了不破——老包不认识它,
也永远不会收到它。破坏性的走大版本,其余走小版本。
往事件载荷里加一个字段同样不破(zod 对象默认非严格,多的字段被忽略),走小版本。
分界线不是「改没改契约」,是「老包会不会收到一个它解不出来的东西」——看的是活动包内联的那一份。
「系统答出去的」不止事件种类与拒绝码:事件载荷与能力答复里的每一个枚举同样是——ORDER_RESULTS(办结的结果)·
COMMIT_ACTIONS(落账摘要里的动作)· 工单状态(order.read)· NODE_RUN_STATES · SIX_STATES(node.read)·
DOCUMENT_KINDS(doc.list)· control 的三个动作。这几样加减一个值都走大版本。工单种类 ORDER_KINDS 不在其内:
它是活动写的那一侧(清单声明、order.create 派;order.read 只答活动自己派的单,老包永远收不到一种它不认识的)——
加一种走小版本,减一种走大版本(定义者 2026-09-24 定)。系统→活动的对象一律不严格
(12.1.0 前 order.read 的办结段与答复里的文件引用是严格对象——系统往里多答一格,老包整条解不出来;已放开)。
这两条由 tests/wire-freeze.test.ts 机器钉着:每个大版本冻一份系统答出去的封闭集(同一个大版本里动一个值就红)与活动写的那一侧——名词表与工单种类(少一个就红)。
清单那一侧收窄不按这条走:清单不在活动包内联校验的那条线上(manifestSchema 是系统在装包那一刻用的尺子),
所以判据换成一条:收窄之后从前跑得起来的包会不会跑不起来。会 → 大版本;
只是把一个本来就用不成的值提前拦下来(拦点从下游挪到机检,并说得更清楚)→ 小版本。
icon 收进图标调色板走的就是后者:调色板外的值从前装得进、机检还全绿,可它建不出活动类型来(建类型 422),
从来没有一个包靠它跑起来过。
两道机器闸:改契约先过它们(12.1.0 起)
小版本的承诺——一个包在某个大版本的一个小版本上打出来、装得上,在之后同一个大版本的小版本上都照旧装得上、跑得起来—— 不靠记性守,由下面两道闸钉着。契约此刻还没有锁成正式版(定义者 2026-09-24:先把已有的几个活动做完再定), 所以「大版本攒着出」只是建议,撞到非破不可的照旧按上面的判据升:
- 哪些是契约(活动包依赖、同一个大版本里只放宽不收窄):清单的形状(
manifestSchema与它引到的每一段)· 事件信封与各事件载荷 · 能力名词的请求与答复 · 交出与它的答复 · 拒绝码 · 上面那几个封闭集与上限常量 · 打包那一刻用的五只手 (manifestSchema·manifestGaps·outputIssues·tableIssues·orderIssues,与折兄弟清单的registryOfManifests)的签名与语义 · SDK 的defineActivity与能力面 client、测试台fakeCapabilityFace。别的导出是系统两侧(server · web)共用的实现,不在这份承诺里。 - 小版本只放宽:加名词、加可选格、加封闭集里活动写的那一侧的值、放宽机检、修缺陷;不许动系统答出去的封闭集、 不许收窄清单(从前全绿的包变红)、不许把系统→活动的对象改严格。
- 大版本尽量攒着出:撞到非破不可的,能攒成一批就攒、写清老包怎么跟再出;升大版本时在
wire-freeze.test.ts的FROZEN里为它另写一份。 - 两道机器闸:
tests/wire-freeze.test.ts(系统答出去的封闭集按大版本冻结、名词表与工单种类同一个大版本只许加、系统→活动的对象不许严格)与tests/compat-corpus.test.ts(发出去的每一个包的清单原样放在tests/corpus/,每一次改契约都拿此刻的尺子重量一遍, 四把尺 fail 与 warn 都要为零)。每个正式发出去的包发一版就把那一版的清单抄一份进语料,旧版留着。
判出大小版本之后还有四样要在同一次提交里做完(漏了哪一样都不当场报错,症状都出在别处):
改代码常量与同名的 package.json · server 与 web 同一次改到 · 外层 AI-Native/CLAUDE.md 那张
「契约版本」表递一格 · 并进 main 后 contract/publish.sh 发一次私有 npm。四样各自漏了会怎样,
写在 contract/README.md「契约升版那一次提交要做的四样」。
5.5.0:enter 载荷加 extraContext(恒有,没填是空串)——往开放的对象里加字段,按上面的判据走小版本;活动清单的 ^5.x 范围都满足。
5.6.0:能力名词加 ledger.reverse(冲销一行台账:打废弃标记、记理由与本步,数字一个不动)、table.read 多一格
retired(不给就是在用行);清单的表声明与列声明多几样注解(表的 text / time / refs,列的 text / measure)。
名词是活动发出来的那一头、注解是清单那一侧只放宽不收窄,两样都不破老包,走小版本。
5.7.0:工单封闭集加第四种 collaborate(协同工单:人带着自己的 AI 做出一份交付物,
执行工单偏人独立完成;同一副表单、同一枚「提交」,分界在谁来干);工单定义加一格可选的
deliverable(只协同有、且必有,指数据结构里一个 file 字段——放行时那一格非空是系统那一道
的固定规则,机检第七查拦漏写 / 错写)。清单那一侧是放宽:从前跑得起来的三种单一个字都不用改;
work-order-reply 的结果集一个值都没加(协同与执行同一枚 submitted),所以老包也收不到
任何它解不出来的东西。两条判据都指向小版本。
5.8.0:表单字段的封闭集加第九种 chart(件上一格只读的展示),字段多一格 chart
({ kind, unit?, from? }:画法封闭五种 bar / hbar / line / pie / donut)。两条数据驱动的路:
显式值由活动写(order.create 的初值、order.write 的补丁),值是图表数据
{ labels, series: [{ name?, values }] }(每条系列与 labels 等长);派生图写 chart.from,
值从本件那张表格此刻的行现算、数据对象里不存这一格。图表恒为只读(accessOf 对它恒答 read),
不进 rows、不作 decision / deliverable,机检第七查逐条拦(chartIssues 一处判两侧用)。
表的列类型 COLUMN_TYPES 与产出字段 CONTRACT_FIELD_TYPES 一个字都没加——图表不是列、也不是产出。
清单那一侧是放宽:从前跑得起来的八种字段一个字都不用改;活动发出来的那一头没减东西,
老包也收不到任何它解不出来的东西。两条判据都指向小版本。
6.0.0:制品从工单来 + 六格补齐。六条改动,头两条各自就够得上大版本——
- 撤制品卡(产品设计 §1.5、附录 B 31)。
chat.card的卡族去artifact(只剩decision·elicitation),card-reply.kind跟着去一个值,artifactArchivePayload去kind与cardId(只归资料;那一份收严成strictObject,老包照旧递kind: 'artifact'时当场被指名, 而不是把那一格默默丢掉、照type归出一份资料来)。两个封闭集各减一个值:卡族是活动 发出来的那一头(减一个破),card-reply.kind是系统答出去的那一头(加减都破)。 要人认一版草稿的改派验收工单,要人做出一份文件的改派协同工单。 ref值的形状进契约(收窄)。refValueSchema/refIsEmpty/refKeyOf从工作台契约 搬进这里(那一侧经plugin/manifest.ts那一处接缝再导出),checkValue多一支ref: 主键串或{ key, label? },老写法{ 列: 值 }当场拒并指名。从前它落在default分支上, 活动经order.create/order.write写老写法一路无声——从前跑得起来的包会跑不起来。needs.tables分读写(收窄)。一条可以写成{ address, write?: true };字符串写法恒为 只读。机检第五查核「write只许标在master.*上」(标在自己声明的表或别的域上都 fail), 能力面对没标write的外域表拒写并指名。从前靠约定,一次手滑就能让方案侧的包改掉全域 共用的那一行——清单侧的判据同样指向大版本。- 文件格的
archive(加格)。工单定义里file字段多一格archive,指本清单declares.documents[]里一个文档类型的代号:放行成立的那个事务里按那个类型归档, 种类随类型。第七查三条(archiveIssues一处判两侧用):代号要在本清单声明里、 协同单的deliverable那一格必须标了archive且那一类是制品、非文件格不许标。CAPABILITY_ERRORS加suspended(挂起中调能力面收它,与「这一步已收场」的node-closed分开)——系统答出去的封闭集加一个值,本身就是大版本。 measure.unitFrom(加格):度量四选一,单位可按行取自另一列(数量列的单位随料走)。- 列
default(加格):declares.tables[].columns[].default,与经营对象那一层的列规格同一格, 落到库上是DEFAULT、落到屏上是新建弹窗的预填;形不合这一列的由机检第五查当场指名。
活动包要跟的:清单 contract 范围改 ^6.0.0;制品卡那一段改成验收单 / 协同单加一格标了
archive 的文件格;写 master.* 的登记类活动把那张表标上 write: true;ref 那几格改成
主键串或 { key, label }。
6.1.0:协同工单 = 制品卡的替代。6.0 落的是「制品从工单来」的前一半(文件格标 archive、
放行即归档),这一版补另一半——复杂制品不再由活动自己做完请人认,而是派一张协同单、
让执行人带着自己的 MCP host 做出来(产品设计 §1.4 / §1.5 / §3.5、系统设计 §3.1 / §3.4、附录 B 43 / 44)。
活动要把这一步交代清楚,所以多两格、多两条机检;加格与加机检,一个封闭集都没减——小版本:
text.prompt(加格):给 AI 的提示词——执行人的 host 做这一步时喂给它的模型的那段话。 协同单该有(缺了机检警示,不拦激活);别的类型写了也警示(它不上屏、不进取单答复)。text.references[](加格):制品参照与模版{ field, role: 'example' | 'template' }[]。field必须是数据结构里access: read的file字段(机检拦:不是文件格的话 host 拿不到可读的字节,人能改的话活动给的那一版会被当成待填的格改掉);值由活动在order.create时给(文件库里的一份引用)。- 交付物那一格不许写
multiple(加机检):只有一个槽,再交即覆盖(件上换一份、 host 里desk.deliver_artifact再交一次都是覆盖,旧的那份留一行工作日志、字节不删)。 多份的话「这一张单的产出是哪一份」在屏上、在 host 里、在放行那一道都答不出来。 - 交付物归到的那个文档类型,
formats要落在DELIVERABLE_FORMATS之内(加机检):html/markdown/pdf——工作区那一列里渲得开的那三种(产品设计 §3.5「交付物的规格」、 系统设计 §13)。不写formats(不限)也拦:不限意味着 Word / Excel 也收得下。 这个常量住契约包,机检、服务端(件上传与desk.deliver_artifact同一把尺)、前端三处同源。 - 活动可在
order.create时预填交付物槽(自己起的一版草稿当起点):checkValue对file值照旧判形状,没有新的口子。
活动包要跟的:清单 contract 范围改 ^6.1.0(^6.0.0 照样满足,不改也跑得起来);
协同单补 text.prompt,要给参照 / 模版的补 text.references[] 并把那几格标成 access: read;
交付物那一格去掉 multiple、它归到的那个文档类型的 formats 收进三种之内。
6.2.0:引用进输入与表格列 · datetime · 主键串的编解码。第六阶段第一个活动
端到端跑通之后撞出来的几格;全是放宽,一个封闭集都没减——小版本:
- 输入 / 产出字段收
ref(CONTRACT_FIELD_TYPES加一个值):一步的输入常常就是 「哪一条对象」。写成text的时候起单弹窗摆的是一个空输入框,人得自己把主键串背出来, 抄错一个字照样收得下——活动拿它读不到行,屏上是「这一步空跑了一趟」而没有一处说得出为什么。 值的形状与件上那一格同一份(refValueSchema);字段上写ref指定对象表地址, 那个地址要在needs.tables里且是对象表(机检第五查拦)。 - 表格的内联列收
ref(新封闭集FORM_COLUMN_TYPES= 列类型 +ref):一张报价行里的 「哪一颗料」就是这一格。从前只能摊成两列文本自己拼回去,拼不回去一个字也不报错。 人保存与放行按refValueSchema判(tableRowGaps一把尺);列上那个地址归机检第七查。 - 列类型加
datetime(COLUMN_TYPES加一个值):落库timestamptz,契约里的值是 ISO 8601 带时区的串(checkValue拒光一个YYYY-MM-DD、也拒不带时区的时刻)。date只写得出哪一天;只有它的时候,一件「几点发生的」只剩两条路——截成一天, 或塞进文本列(从此排不了序、比不了大小、数仓认不出它是时间),两条路都不报错。 台账的业务时间time指得到它(一刻本来就含着一天);屏上按业务时区渲。 encodeRefKey/decodeRefKey(加两个函数):主键串col=val;col=val的编解码搬进契约包。 从前它只在工作台契约里、活动够不着,于是每个要写引用格的活动自己拼一遍——值里带了;=%的那一条拼出来拆回去就不是原来那一条,而两头都不报错。工作台契约的encodeRecordKey/decodeRecordKey改成从这里单向再导出(接缝只此一处,用例钉)。
活动包要跟的:清单 contract 范围改 ^6.2.0(^6.0.0 / ^6.1.0 照样满足,不改也跑得起来);
把“哪一条对象”那几格输入从 text 改成 ref 并补上地址;摸成几列文本的引用列合回一格 ref;
要记「哪一刻」的列从 text 改成 datetime;自己拼主键串的改调 encodeRefKey。
6.3.0:输入契约的文本字段可标 multiline。一步要收的常常是一段话——一封邮件的正文、
一段客户口述、一份要人贴进来的原文。件上那一格(declares.orders[].data.fields)早就有这一格,
输入契约(declares.input.fields[])只有单行那一档:触发弹窗摆的是一个 <input>,人往里贴一段
带换行的正文,框里只看得见最后一行,而换行还在不在值里屏上答不出来。换一个要人贴一段正文起单的
活动都会撞到,所以它是系统的一格。
inputFieldSchema 多一格可选的 multiline(只 text 有,写在别的类型上机检当场指名,
措辞与件上那一格逐字一样——refineMultiline 一把尺两处用);两处触发弹窗(单活动节点的、实践的)
对标了这一格的文本字段渲多行框,值仍是字符串。
放宽,走小版本:清单那一侧多一格可选属性,从前跑得起来的包一个字都不用改;活动发出来的
那几张封闭集(CAPABILITIES / SYSTEM_EVENTS / CAPABILITY_ERRORS)一个值都没动,老包收不到
任何它解不出来的东西。活动包要跟的:清单 contract 范围改 ^6.3.0(^6.x 的老范围照样满足,
不改也跑得起来);要人贴一段正文的那几格输入补上 "multiline": true。
6.4.0:声明的表可以标人读名列 nameColumn。屏上一律印人读名、不印键(产品设计 §7.3):
引到别的表的列、台账的挂靠列,印的是目标那一行的名字(「华芯智能」「人民币」),键降成小字。
哪一列是名只有表的定义者答得出,所以它是声明里的一格:declares.tables[].nameColumn 指本表
columns 里的一列,且是文本或枚举列(一个数字、一个日期当不了「这一行叫什么」)。
台账不必标——一笔账的身份是「哪一天、哪几个键」,没有「它叫什么」。
经营对象那一层的人读名列由系统清单给(master.party 是 name、master.part 是 mpn……),
活动引用它们时什么都不用做。
放宽,走小版本:清单那一侧多一格可选属性,从前跑得起来的包一个字都不用改——对象表没标只提示
不拦(机检 warn),只是引到它的列在屏上退回印键。活动包要跟的:清单 contract 范围改 ^6.4.0
(^6.x 的老范围照样满足,不改也跑得起来);每张对象表标上 "nameColumn": "<哪一列是名>"。
7.0.0:工单落定——落业务库只有工单提交一个入口(产品设计 §1.4 / §1.5、系统设计 §2.8 /
§3 / §4.5、附录 B 6 / 45 / 47 / 49)。一句话:业务库的写从能力面挪进了工单定义的 commit。
几条改动里任何一条单独就够得上大版本——
- 业务数据名词只剩读(封闭集减四个值):撤
ledger.append·ledger.reverse·object.upsert·artifact.archive。活动对业务库只读;要落的东西摆进工单的数据对象、在数据 表单的commit里声明落到哪,人提交、系统在同一个事务里落账、办结、冻结、归档并盖章 (任务、节点、工单、提交人、时刻)。归档同理随落定走(文件格的archive),没有单独的归档动作。 - 对话区没有卡(封闭集再减):撤
chat.card与两张卡的形状、撤事件card-reply。 要人拿个主意、补几格信息,派一张一两格的执行工单——它有表单、有复核、有冻结、记得到人。 对象的新建同理只从工单落定来,所以NODE_RUN_STATES也去掉了awaiting-object。 - 事件加两种(系统答出去的封闭集加值,同样破):
work-order-saved(表单声明了recheckOnSave的单被人保存了一次,请活动复核一遍)、work-order-mail(沟通工单收到一封回信)。 work-order-reply多两格:landing(落账摘要,恒有;不写业务库的单与「重做」是{ items: [] })与by(提交人{ id, name })。活动交出时要带回去的需求号、客户代号从 摘要里取——回头去库里捞一遍的话,捞到的可能是别人同时落下的那一行。- 工单定义 = 数据表单四段 + 一块类型拓展(清单侧收窄):
data·interaction·commit·text四段讲的都是数据对象,四种单共用;这一种单特有的东西写在以类型命名的 那个键下——执行guide· 验收acceptance· 协同artifact(必有)· 沟通communication(必有)。顶层的deliverable挪进artifact.slot,text里的guide/standard/criteria/matters/prompt/references与interaction.decision各归各位,text只剩brief·sections·fields。写了不属于本类型的那一块、一份定义写了两块,机检 fail。 interaction加两格:requiredWhen(带条件的必填——「结论选了退回则原因必填」)与recheckOnSave。- 两侧共用的纯函数进契约:
evalCondition·accessAt/missingOf·planCommit/summarizePlan·formatSerial·mailDraftOf/mailDraftIssues。件上、宿主、服务端三处要 同一个答案,各写一遍必然有一天不一样——而那一天,人是照着预告点的提交。 - 顺带:
table.read的orderBy收数组(至多四列);chat.say可带一张本步派过的工单 (屏上多一枚「打开工单」);order.read的done收成有形状的办结段(带landing),沟通单 另带mails;导出FORMAT_EXTENSIONS/formatOfFileName(档次与扩展名一把尺)。
活动包要跟的:清单 contract 范围改 ^7.0.0;每一处 cap.append / cap.upsert /
cap.reverse / cap.archive 都改成一条 commit 落账项(值先摆进件上一格只读的格,人看得见);
发卡的那几处改派轻工单;text 里那六格与 deliverable 挪进各自的类型拓展;读 work-order-reply
的地方改从 landing 里取键。SDK 同版(@bpos/activity-sdk 7.0.0),信封多一格业务时区
(Envelope.timezone,缺席答 'UTC',配一个 dayIn(zone, at))。
7.1.0:落账项落得进「同域的表」 —— 机检那一句放宽到设计的口径(系统设计 §8.2、§3.8;
附录 B 27)。从前这一道要求落账项的 table 在本清单 declares.tables 里,而落定那一道
(server/src/modules/desk/landing.ts::writableAt)判的是域相同:一个活动拆成两包时,
第二包写的正是第一包建的那张表——两包在同一个域里、本来就是同一件事的两段。两句话不一样的代价是
装得上、机检全绿的包在人按下提交的那一刻被拒,而那时候人已经把一张单填完了。
写得了的从此是:同域的表(本清单声明的,或同域别的活动建的)与在 needs.tables 里标了
write: true 的经营对象表;别的域的表永远只读,一个字都没放松。域从本清单声明的表推
(清单里没有子系统代号),一张表都不声明的活动因此只剩经营对象那一档。
放宽,走小版本:机检少拦一种清单,从前跑得起来的包一个字都不用改,一条新 warn 也没有;
封闭集、载荷、答复形状一律没动,老包收不到任何它解不出来的东西。活动包要跟的:没有——要用这一档
(落账到同域别的活动建的表)的包,清单 contract 范围写 ^7.1.0。
8.0.0:业务库四层——对象 · 单据 · 台账 · 文档(产品设计 §1.4;系统设计 §3.8 / §6.1 / §8.2;
附录 B 53 · 54 · 56)。从前叫 ledger 的「只增不改的事实行」整体改叫 voucher(单据),
ledger 换成新语义 「按主键一行的状态」。八条改动,头两条各自就够得上大版本:
TABLE_KINDS三个值(object·voucher·ledger):一个封闭集改了一个值的含义—— 照 7.x 写的包声明kind: 'ledger'的表在 8.0 里是另一种东西,数仓按kind = 'ledger'建事实表 会建错。这一条单独就是大版本。COMMIT_ACTIONS五个值:加post(过账:按主键找到那一行,没有就建;set置值、add/sub增减,一句里原子地做)。动作配得上种类三分:对象create/update、 单据append/reverse、台账post——7.x 的「append/reverse落到ledger上」 在 8.0 里是一条 fail。- 落账项加
add/sub(只在post项里;同一列在set/add/sub里至多一次; 目标是数值列、来源是数值),并加一条 无单据不过账:有post的表单至少有一项append或reverse。 set改成可缺:落账只写例外,没写的列按同名直落解(§3.8)——7.x 那句「至少写一格」 逼着作者把一个本来解得出来的列再抄一遍,而抄错一个字就是一条静静落错的列。- 声明多三格:单据的
source: { kind, no }(源单那两列,都是本表的文本列)、台账数值列上的nonNegative,以及挂靠attachedTo的三种情形——单体挂单头(必填)· 单头挂一张对象表(可不挂)· 台账挂一张对象表或单据表(必挂);谁都不许挂到台账上。time与source只属于单据,nonNegative只属于台账的数值列,nameColumn对象表该写 · 单据表可写可不写 · 台账不许写 (6.4.0 那句「台账不必标」在 8.0 里成了一条 fail)。 - 引用的目标放宽到单据表:件上的
ref、输入 / 产出的ref、表格内联的ref列都指得到 对象表与单据表(挑一条对象,或挑一张源单);台账仍不作引用与挂靠的目标——一行状态不是身份。 表上的refs注解没放宽:它的目标只有对象表,单据之间靠source连。 ref来源的column改成可缺:不带就是整把主键串(记源单号那一列用的就是它); 它是一串字,所以只落得进文本列。- 顺带:
parseAddresses认全角的分号与逗号(中文输入法下打出来的就是它们;不认的话整串 当成一个地址,屏上那一句「这几个不成形」指着人打对了的那一串)。
破坏性,走大版本:一个封闭集换了一个值的含义、另一个封闭集加了值且动作与种类的对应收紧、
两条新 fail(台账标 nameColumn、台账写 time)——7.x 里声明了表的包装进 8.0 的系统会被机检点名。
活动包要跟的:清单 contract 范围改 ^8.0.0;把当事实行用的 kind: 'ledger' 改成 'voucher';
台账上的 nameColumn 与 time 撤掉;要过账的表单补 post 项与 add / sub,并确保同一张单里
记了单据。SDK 同版(@bpos/activity-sdk 8.0.0)。
8.1.0:表单结构加「怎么摆」四样(系统设计 §3.2 / §3.3 / §3.5 / §3.7)。节多两格 role
(context 参考 / input 填写 / verdict 结论)与 columns(1 / 2 / 3);格多一格 span
(1 / 2 / full);列多两格 width(narrow / normal / wide)与 primary(主列:表比栏宽、降成
一行一张卡时它做标题);图多一格 top(只画前几项)。件跑在一条窄栏里,结构里不说怎么摆,
视图只能按内容猜——猜出来的就是一列长说明把旁边四列挤到一个字宽、二十四根柱糊成一团。
只加不减,走小版本:全是可选格,老包一个字不改照旧装得上;清单侧只放宽不收窄。
活动包要跟的:没有——要用这几格的包把 contract 范围写 ^8.1.0。
8.2.0:制单体验四样 + 台账不作目标(产品设计 §1.5 · §3.2;系统设计 §3.2 / §3.3 /
§3.6 / §3.7 / §4.4 / §6.1;dev/archive/2026-09-21-活动地基审核-ERP模型.md(已归档)§七 裁定 1 · 4 · 7)。
拿一家供应链贸易公司的常规 ERP 逐环对表撞出来的五条,都是只加不减:
- 算格
calc(CALC_KINDS封闭两种product/sum,可带scale,缺省 2 上限 6)。 落账模式是声明不是脚本,件上的算格是唯一的例外:行金额 = 数量 × 单价、单据合计 = Σ 行金额这一类制单时当场要看见的算术。恒为只读——人改不了;活动经order.write替它定一个别的数即拒并指名,而原样回显系统算出来的那个数不算写(活动为了留住人加的行 必须整张表读回再写回,逼它逐格剥掉等于给每个活动都留一份税)。一只纯函数evalCalc(form, data)凡写进库的那一刻都算(派单、活动写、人保存、落定;件与预告各算各的,同一只), 所以人看到的就是将要落的。乘一层、不嵌套;合计的操作数可以是算格; 任一操作数空着算格为空(不当 0)。 - 值域
range({ min?, max? },数值格与数值列)。件上那一道从前只判必填、类型、取值集、 行数:数量填成负数、税率填成 3 一路无声地落进库,而活动只能在recheckOnSave里写一句 不是闸的意见。越界与缺口同一把尺(rangeGap一处算),pointer指得到表格里那一格。 - 引用的筛选
ref.where(REF_WHERE_OPS封闭三个eq/ne/in,条与条之间是与)。ref: master.party从前列的是全部往来单位,挑供应商时客户、货代、报关行都在里面。 件上的选择器与落定那一道都按它走(bpos:lookup的载荷多一格where)。 default真的生效:从前谁都不判它——写一个取值集之外的枚举,清单装得进、机检七行全绿, 错要等到派单那一刻由valueGaps说。8.2 起机检提前拦,而那把尺就是派单那一把 (见下面「机检那把尺怎么定」):表格字段的缺省是一组行、图表那一格是一份图表数据,照旧收得下。 表格的列也收default(「加一行」按它预填)。- 台账不作目标:
declares.input.target指到台账机检第五查拦(refs与attachedTo两处早就有这道闸)。一行状态不是身份,而起单那一刻系统只按主键核那一行在不在。
只加不减,走小版本:全是可选格与新拦的路,从前跑得起来的包一个字都不用改
——目标指到台账的包从前也起不了单(起单那一刻核的是对象 / 单据表)。封闭集只加不减,
活动发出来的那一头没动,老包收不到任何它解不出来的东西。
活动包要跟的:没有;要用这几格的包把 contract 范围写 ^8.2.0。
机检那把尺怎么定:拦的是「运行时必拒的」,提前到装包。default 这一格就是靠它守住小版本的
——另写一套判据(比如「只收标量」)会比运行时严:表格字段的缺省是一组行、图表那一格是
一份图表数据,两样在 order.create 里都收得下,8.1 里真跑得起来;拦掉它们按上面那把尺
(「从前跑得起来的包会不会跑不起来」)就是大版本。所以 defaultIssues 不另写一套,
它问的是派单那一刻同一句(checkValue / tableRowGaps):派单时收得下的机检就不拦,
派单时必拒的机检提前说。这条原则对以后每一次「把运行时的判据提前到机检」都成立。
8.3.0:地基四条(系统设计 §3.2 · §3.8;说明书 §11;activities/procurement-chain/README.md
末尾那五条撞出来的)。照一家供应链贸易公司的采购六环(询价 → 报价 → 订单 → 入库 → 发票 → 付款)
写一条真的实践链时撞出来的,都是只加不减:
- 经营对象那一层的形状随契约包走(
MASTER_TABLES·masterRegistry(),一处现算)。master.*十三张表的列、主键、挂靠、引用、人读名列从前只住在 server 里,而活动包是在仓外 打的——build.ts拿机检四把尺自检时连不上任何一台实例,于是attachedTo: master.*带进来的 挂靠列一条都解不出:omit它被判「那张表没有这一列」,不omit又会在激活那一刻凭空多出一格 没摆进任何一节的字段,两句话指的都不是真原因。机检四把尺没收到登记时按契约包这一份解master.*;收到了就按收到的那一份(服务端递的那一份里本来就有这十三张,还带着管理者后来 加的列)。server 那一侧(建表、登记、开机自检)读的就是这一份,不另抄一遍。 - 补格
augment(顶层data.augment[]与表格字段的augment[],同一组格:range·calc·where·default)。取形解出来的格,名、类型、取值集、单位、人读名与说明 都从列上来——那是表声明答得出的全部;件上还有四样只属于这一张单的,从前只写得进data.fields里自己写的那一格,于是要给取形来的某一列加一道值域,只能把它omit出来再写 一格同名的,同一列的形状因此在清单里存了两份。表格那一侧更重:columns一写就整个盖掉shape,一张要算行金额的单体表格只能全写内联列。补格只加不改:改不了类型、改不了名字、 不与omit同时指同一列,补的那一格解不出来机检指名。人读名与访问档不收 ——text.fields与interaction.access/interaction.rows本来就够得着取形解出的格 (定形之后那几格就在data.fields里,按名字对),再收一份的话「哪一处赢」只有读代码才答得出来。columns写了就整个盖掉shape这一条语义一个字都没变。 serial的日期段认取形解出来的那一格。commitIssues里field/ref两种来源在 「取形还没解」时早就走 warn(判不了),只有serial.date.field是硬 fail——于是 「单号按归期分段」这条最常见的 ERP 编号规则,要么把归期那一列omit出来自己写一格, 要么放弃日期段。补成同一副。- 台账上「一格引用盖两列」解得出来(第 1 条的推论,不是另一处代码)。
refs → master.part盖着mpn+maker,件上合成一格;落账到以mpn+maker为键的台账时maker本该由同名直落第 2 条「引用」解出来,而那一条要先认得出这张表上的线——线要目标表的形状。 经营对象进了契约包,仓外打包那一刻也解得出来,post项因此不必再逐格写{ ref, column }。
只加不减,走小版本:全是可选格与新解得出来的东西,从前跑得起来的包一个字都不用改
——经营对象那一层的形状从前在激活那一刻本来就解得出(服务端有登记),这一版只是把同一个答案
提前到仓外;新报的那几条(补格补错了、ref 取的不是目标表的主键列)拦的都是激活那一刻本来
就会被拒的包。封闭集只加不减,活动发出来的那一头没动。
活动包要跟的:没有;要用补格的包把 contract 范围写 ^8.3.0。
8.4.0:两条,都是范例链拆绕法时剩下的(系统设计 §3.2)。都是只加不减:
- 表格字段的
shape也收omit(与顶层同形:"<表地址>"或{ table, omit? }, 同一只resolveOne、同一套点名)。顶层从 8.0 起就有omit——系统发的单号、落账给成常量的 那几列不上件;表格那一侧只收一个表地址,于是单体表上那几列系统或活动给、人不填的 (行号由落账的rowIndex给、源单种类由落账给成常量)照样解成列,只能排在rows.edit之外 当只读列摆着——件上横着几格人改不动、也看不出所以然的列,宽度还都占着。 「摆着」与「不上件」是两件事,而 8.4 之前只有前一件写得出来。 表格那一格比顶层多一道:行的身份keys不许指到不上件的列(在omit里、被一条 引用盖着当非锚列、或取形根本没解出来)——件上每一行都会被判「主键列空着」 (modules/desk/orders.ts),而那一列根本不在件上:人对着一张单,红带指着一格他看不见的列, 保存不了、提交不了,也没有一处说得出为什么。 registryOfManifests(manifests):几份清单的declares.tables折成一份TableRegistry(与masterRegistry()并成一份)。活动包是在仓外打的,8.3 之后手上有形状的是本清单与master.*;挂靠到同一子系统里别的包声明的那张表仍解不出它的主键,挂靠列因此一条都带不进来 ——采购链的procurement.payable挂在pc-invoice声明的发票单据表上,invoice_no就撞在这里, 当时只剩一条规矩:把那一列照原样写进columns,同一张表的形状在两个包里各存一份。 可一条单据流本来就是拆开的几个包(报价 · 订单 · 收货 · 发票 · 付款),而它们本来就摆在同一个 父目录下。build.ts的范本因此加一步:按BPOS_SIBLING_MANIFESTS(几份清单的路径,逗号分)或 自动扫同一父目录下兄弟包的bpos-activity.json,折成登记递给四把尺;一个兄弟都没有时不递 ——那一档与 8.3 逐字一样。同一个地址两份清单都声明时要逐字相同(键序也算),不同即当场抛 并指名是哪个地址、哪两份清单:折出一份登记时只能挑一个,静静挑一个的话两个包各自机检全绿, 装到实例上才有一个按另一份形状对不上。(这一条落地的那一刻就抓住了pc-invoice与pc-pay两份procurement.payable声明的分叉。)
只加不减,走小版本:表格的 shape 从一个地址放宽成「地址或 { table, omit? }」,
老写法一个字都不用改;registryOfManifests 是新导出的一只函数,谁都不必调它。
封闭集一个值都没动,活动发出来的那一头没动,老包收不到任何它解不出来的东西。
新报的那两条——keys 指到不上件的列、两份清单同一地址声明得不一样——按「从前跑得起来的包会不会
跑不起来」这把尺都是不破:前者的包装得上、激活得了,可件上每一行都判不过,从来没有一个包靠它
跑起来过;后者只在一起 build 时才判得到,而一起 build 这件事本身是这一版才有的。
活动包要跟的:没有;要用这两样的包把 contract 范围写 ^8.4.0,build.ts 照范本
(activities/procurement-chain/*/build.ts)加那一步。
8.4.1:纯结构重构,契约面一个字没动(系统设计 §3.9 末段的裁定「种类是基类上的拓展,只加不改」)。
四种工单各自那一块的形状与机检搬进了 src/kinds/{execute,accept,collaborate,communicate}.ts,
src/kinds/index.ts 是模块登记表;ORDER_KINDS · orderKindSchema · ORDER_KIND_LABEL ·
ORDER_KIND_NAME · ORDER_EXTENSION_OF_KIND · ORDER_EXTENSION_KEYS 从登记表现算,
导出名与值一字不变。几片共用的小零件(列名、字段级三档与「这一格是不是只读」、判据 / 事项 / 参照 / 信)
下沉到 src/atoms.ts,manifest.ts 原样再导出——从 @bpos/activity-contract 引任何名字的地方
一个字都不必改。撤掉的只有三只内部 refine 包装(refineOrderArtifact / refineOrderReferences /
refineOrderCommunication),合成登记表那一道 refineOrderKinds;仓内仓外都没有人调它们。
新导出的那几只是模块自己的手(kindModule · kindModuleOf · ORDER_KIND_MODULES ·
artifactOf / acceptanceOf / guideOf / communicationOf · decisionFieldOf · slotFieldOf ·
mailFieldsOf · viewFormExtrasOf(件上那几枚小签:槽与参照 / 模版;9.0.0 起改名 viewCellTagsOf,见下;
8.4.1 里它叫 viewFormExtrasOf、答的是「init 的 form 多出来的那一格」,视图协议换成渲染模型之后
它答的是「哪一格标什么」——形状随之改,没有活动包调它) · MAIL_ORDER_KINDS · COLLAB_DESK_KINDS)——
系统那一侧按种类分叉的地方从此只经它们,两道扫真实源码的用例(server/tests/kind-modules.test.ts ·
web/src/features/desk-task/internal/kinds/literals.test.ts)钉着。
走补丁版:封闭集一个值都没动、形状一格都没动、拒绝的话一个字都没改,老包收不到任何新东西;
活动包要跟的:没有。
协同模块的 viewFormExtrasOf 改名 viewCellTagsOf(答「哪一格标什么」,取值集 VIEW_CELL_TAGS;投影从此不认识哪一种单多哪一格)——导出面撤一个名字、随本版发出;活动包不调它,不影响清单。
9.0.0:台账的每一个数都出自本次落定写下(或冲销)的单据行(产品设计 §1.7「台账只随单据变」;
系统设计 §3.8「机检」)。从前「无单据不过账」只要求同一张单里至少记了一张单据,管不了过账那个数
打哪儿来:add / sub 取得了件上任意一格,于是一本账随件走而不是随单据走
——账上那一笔对不回任何一行单据,而一本对不回去的账比一本错账更难查。两条改动:
- 机检第七查多一道(定义是静态的,机检够了,落定那一道不必再判)。对每一条
post项,add/sub的每个来源、以及set里除台账主键列之外的每个来源(写明的与同名直落解出的 过同一把尺——只判写明的话,把那一格删掉让同名直落去解就绕过去了,而绕过去的那一版落进库的 是同一个数):column要有一项append以同一张表格为行来源且那一列落进了它;field要落进某一项顶层一行的append;from指的那一项要是append或reverse。value(常量) ·serial·submittedAt/submittedBy·ref(+column,引用格的主键段) 不受此限——它们不是「账上的一笔数」,是号、时刻、人与一个写死的步长;台账主键列的set也不受此限:它是这一行的身份。另加一条:rowIndex不许进add/sub(行号答的是件上 那张表格的第几行,把它加进一本账等于让账随「这一行摆在第几位」变)——这一条只看定义就判得了, 清单那把 zod 也拦。 from指得到reverse项:落定引擎里冲销那一项找到并锁住了那一行,把它的列 暴露给后面的项,与append写下的那一行同一副读法。红冲那张单因此这么写: 顶层一行reverse冲掉单头,再post从{ from: <冲销项>, column: … }取数反着过账 ——取的是库里那一行的真值,而不是活动上一次读库读到、摆在件上的那个数 (两者差多少,从前两边都不会报错)。
清单侧收窄,走大版本:按「从前跑得起来的包会不会跑不起来」这把尺——会。
一份把 add 取自件上某一格的旧包,从前装得上、跑得起来,9.0 的机检当场拦下并点名。
仓里的包都跟了:采购链的 payment-void 从「按件上那张表格每行一笔」改成「整单级、数从冲销项取」,
走查夹具多一张单头单据 walk.probe_run(顶层一行的过账要有一张顶层一行的单据)。
活动包要跟的:逐条核一遍 post 项,改法优先「让那个数落在单据行上」(把它写进记单据那一项的
某一列),而不是删掉过账;红冲那几张改成 from 指冲销项。contract 范围写 ^9.0.0。
9.2.0:作业指导书的步与验收判据的条钉在件上(产品设计 §3.2「件上的注」,系统设计 §3.7 / §3.9)。
guide.steps[] 每一步收一句话,或 { text, at? };acceptance.criteria[] 多一格 at?。
at 是一节的 id 或一个顶层字段名(表格的列不作锚点:一行高 40px,一条注钉上去要么把行撑开、
要么盖住相邻的格;要指一列就指那张表格本身)。件把步渲在那一节 / 那一格之上、把条渲在之下,
条上另摆活动的判定章;没写 at 的在件顶,standard 在件尾。签核的勾不进件——那是担责的那一下,
由宿主渲在件右侧的签核栏、按件报的位置与那一条对齐(定义者 2026-09-21 裁定)。
从前这几样摆在件上面那一张「处理区」卡里:人一边照着第 3 步做,一边要在下面二十格里找它说的是 哪一格;十几条判据与它们各自判的那一格隔着一整块。
机检核锚点并点名:at 指不到、或指到一张表格里的列名、或一个名字同时是节 id 与字段名,都当场
fail 并把这张单上挂得住注的地方全列出来。不拦的话那一条会悄悄跑到件顶、照样印得出来、照样签得了,
只是不在它该在的那一格旁,而屏上一处报错都没有。
只加一格可选的,走小版本:按「从前跑得起来的包会不会跑不起来」这把尺——不会
(一步写成一句话的旧包一个字都不必改,没写 at 的注钉在件顶)。活动包要跟的:没有;要把步与条
钉到格旁的,给它们补一个 at 就是了。contract 范围照旧写 ^9.0.0。
视图协议另走一版(它自己计版):件上多了「注」这一格、桥多了 bpos:anchors / bpos:goto 两条,
默认通用视图的自述递到 3;按 2 写的视图文件装不进来——条文只在件上印得出来,而它读不到那一格。
9.1.0:表格分大表与小表(产品设计 §3.2「件」,系统设计 §3.2 big 那一行与 §3.7)。
表格字段多一格可选的 big: { rows }(2 – 50):卡内固定展示 rows 行、超过的在卡内上下滚、
列头钉在滚动区顶上,卡头另出一个筛选框(按这张表每一列屏上印的样子字面匹配、大小写不敏感)
与列头排序(点一次升、再点降、再点还原;数按数比、日期按日期比、其余按屏上印的串 localeCompare('zh-CN')),
卡头常驻「共 N 行 · 筛出 M 行」。不写是小表——与 9.0 逐字一样:行全摆开、不筛不排。
从前只有小表那一档,于是一张芯片 BOM 的报价单在件那条窄栏里独占几千像素,人要找一颗料只能一路滚。
只说怎么摆(与 span · width · primary · top 同一档):筛与排都是看法——
不改数据对象里行的次序、不改保存与落账的内容,筛掉的行照样在、照样落;编辑态里筛着也改得了行
(件上输入框绑的仍是原行下标字段[原下标].列,所以 bpos:point 指得到、保存送的仍是整份原次序),
新加的行落在最后并立刻可见,加删改仍按主键比。它因此一格都不碰 missingOf / evalCalc / tableRowGaps。
只加一格可选的,走小版本:按「从前跑得起来的包会不会跑不起来」这把尺——不会
(不写这一格的包一个字都不必改,屏上也逐字一样)。同一次撤掉的只有视图那一侧的一样东西:
默认通用视图表格卡上那个「表格 / 图表」开关(它是视图层的看法,从来不在契约里、不在数据里)——
要看图就声明派生图(chart.from)。活动包要跟的:没有;明细行上百的那几张表标一格
big: { rows: … } 就是了。contract 范围照旧写 ^9.0.0(9.1 只加了一格可选的)。
9.3.0:大表的起用行数(产品设计 §3.2「件」,系统设计 §3.2 big 那一行 · §3.7 · §5.6)。
big 多一格可选的 from:行数到了它才按大表摆,不到就按小表摆开
("big": { "rows": 8, "from": 21 } —— 一张 BOM 二十个料号以内摆开看,二十一个起才收成大表)。
不写 from 仍是恒按大表,9.1 那一档一个字不变。机检 from > rows:不大于 rows 的起用行数
写不通——到线那一刻卡里正好摆得下全部行,滚动区是空的,这一格等于没写。
从前 big 是一句恒成立的话,于是同一格表格要么永远收成大表(三五行的单据只摆了半张卡、
上面还挂着一个筛不出什么的筛选框),要么永远摆开(几十行的 BOM 独占几千像素)。
一张单的行数本来就随这一次的业务变,摆法该跟着它变。
这一判在投影里做,不在契约里、也不在件里(§5.6):宿主那只纯函数按投影那一刻这张表的行数
解出是大表还是小表,解完只把 { rows } 递给件——from 不进渲染模型。所以编辑中加过了线不当场切,
下一次打开再算;冻结件重渲按冻下的那一份数据算,与办结时看到的一致。件自己数的话,同一份数据
在两份视图里摆得不一样,而屏上一处报错都没有。
只加一格可选的,走小版本:按「从前跑得起来的包会不会跑不起来」这把尺——不会
(不写 from 的包一个字都不必改,屏上也逐字一样)。活动包要跟的:没有;行数随这一次的业务
大起大落的那几张表(BOM、明细)标上 from 就是了。contract 范围照旧写 ^9.0.0。
机检加一条 warn 也算改了清单侧的尺子:包的 build.ts 要求 0 warn(一份自己都不干净的清单不该打成包),
所以一条新 warn 会让一个字都没改的包当场红——6.4.0 的 nameColumn 就这样撞红过 A1 1.1.0。
升版的那条车道因此要把仓里已有的包各 build 一遍(活动仓 BPOS-Activity/ 的与本仓 activities/ 的),
撞红的那几个包一并改到:这是升版自己的尾,收不掉就别升。
13.3.0:件上多一种格「成员」,产出与输入各多一种「清单」,补格多收一样「多行」,file.text 认邮件、多一格可选入参 recognize(活动写的那一侧只放宽,系统答的那一侧只加一格可选的,小版本)。前两样是为图上的「动态并行」备的:一步按上一步交出的一份清单起出几份、各有各的人——一行一份,或同一个人的几行并成一份(系统设计 §7.7)。
- 成员格
member(FORM_FIELD_TYPES与FORM_COLUMN_TYPES各加一个值):件上的顶层格与表格的列都可以。值是成员 id 的一串字(不是带名字的对象——名字由读面补);checkValue只判形,这一位挑不挑得了(在用、进得了工作台、左轨有「任务」)要查库,由系统在工单提交那一道判。它只在件上:不是表的列类型(COLUMN_TYPES·TABLE_COLUMN_TYPES不动)、不取形、不落账——同名直落把它当作「件上没有这一格」,落账来源写明取它的被机检点名拒;也不是输入 / 产出的字段类型。不带default(清单随包走,成员是这一台上的人);成员列另不带unit·multiline。必填的空着,缺口那一句是「还没挑人」。 - 清单产出
list(新封闭集OUTPUT_FIELD_TYPES=CONTRACT_FIELD_TYPES+list,declares.output.fields[].type改收这一集):声明每一项的列columns(listColumnSchema:名、人读名、类型,类型集就是FORM_COLUMN_TYPES——活动把件上那张表格的行照抄进来时一一对应;枚举列要取值集、引用列要写ref)。至少一列、列名不重;别的类型不带columns。值是一组对象,checkValue逐行逐列判(键都在列里、每一列合它的类型),空数组合法。清单产出只喂动态并行的那一步,不接进别的步的输入。一次交出里清单有多少行、多少字节的上限是系统的部署保险,不在契约里。 - 清单输入
list(新封闭集INPUT_FIELD_TYPES=CONTRACT_FIELD_TYPES+list,declares.input.fields[].type改收这一集):一份收到的「这一份的那几行」——一行一份时是只有一行的清单,同一个人的几行并成一份时是他的那几行。与清单产出同一副声明(columns,至少一列、列名不重),列不收成员(那一列只定这一份由谁办,一份的人已经在任务节点上);值与清单产出同一把尺,必填的一行都没有算空着。活动只拿到它声明的那几列。它只由动态并行的那一步喂(图上的来源$items),不是起单时填得了的一格:共用的那一集CONTRACT_FIELD_TYPES里仍没有list,实践自己的输入契约与单活动任务的图按taskInputFieldSchema(起单要填的一格)收、写清单被拒;带必填清单输入的活动起不了单活动任务——系统在起单那一道拒并说清,这样的活动只能编在实践里动态并行的那一步上。 - 补格收
multiline(augmentSchema多一格可选的;顶层data.augment[]与表格字段的augment[]同一组格:range·calc·where·default·multiline):取形解出的文本格与文本列从前标不了多行——表格写了内联columns就整个盖掉shape,要一列多行只能把整张表改写成内联列。只补在文本上:解出来不是文本的那一格补了它,机检点名(与range/calc/where落错格同一道);只补这一样也算补了,不报「一样都没补」。并进解形后的那一格 / 那一列,件、预告、保存、落定读的是同一份。 file.text认邮件(.eml),答的页多一格可选的from(系统答出去的对象加一格,只加):一封存下来的邮件答成几页——第 1 页首行# 邮件,其后固定五行主题:发件人:收件人:抄送:时间:(缺项照留;时间是 ISO 8601,照信头自己的时区偏移),再是## 附件(N 份)的清单与## 正文;抽得动的附件各作后续几页,首行# 附件 n:文件名(第 i/N 页),页上带from: { name, format, page }(filePageSchema)——出自哪份附件、它的格式、是它的第几页;信本身那一页与别的格式的页不带这一格。名词、入参、拒绝码都不动。老包照旧(内联的旧尺把多的这一格忽略掉);要按from分流的包把 SDK 升到 13.3.0 重打。file.text多一格可选入参recognize(true或{ model },model是needs.models里声明过的名字;true用默认模型)。带了它,没有文字层的 PDF 页与图片文件(PNG · JPG · WebP · GIF,一份算一页)由系统经模型转写成字;一封邮件里的图片附件与附件里没有文字层的页同样。一次至多 8 页:单独一份 PDF 超了答too-large,邮件里超过的不拒、照旧为空并在附件清单上写明。这条绑定的型号不收图片答unsupported-format。转写来的页ocr仍是true而text不空——那是模型照图转写的,不是文件里存的字,照它抽出来的东西要在件上标出来、请人对着原件核。token 记在这一步的会话与预算里,所以挂起中、额度已满时带recognize的调用被拒。不带这一格的调用一个字不变;答复的形状与拒绝码不动。- 落账的
from引得到「每行一笔」的项——只要这一项与它以同一张表格为行来源(清单侧放宽):取的是件上同一行写下的那一行。一张表逐行各记一张单据、各发各的号(缺口料一张表逐行决策就是这样),过账那一项逐行按本行刚发的号去置一本以单号为主键的账——从前from只引得到顶层一行的项,这样的过账写不出来。行来源不同的、顶层一行的项引它,照旧拒(说不出指的是哪一行);被引的那一项这一行没落下的(它的rowWhen把这一行跳过了),落定那一刻拒并指明是件上第几行。 - 系统答出去的封闭集一个没碰(
wire-freeze不动);节点态、系统事件、拒绝码都没加值;enter的载荷没加格——活动不知道自己是第几份。老包一个字不必改、照旧装得上;要用成员格、清单产出、清单输入、补格多行或recognize的包写contract: "^13.3.0"。SDK 跟到 13.3.0(它整份再导出契约,新符号自动带出;cap.fileText多一格可选的第三参递recognize——SDK 递出去之前按自己那一份契约过一遍、不认识的格被剥掉,所以要识别的包得用这一版 SDK 重打)。视图协议递到 5(控件的取值集多了member,另记)。
13.2.0:派单带幂等键(order.create 的载荷多一格可缺的 idempotencyKey,只加,小版本)。
- 事件至少送到一次:活动派完单、还没把状态存回去就崩掉的那一拍,重投回来会再派一张同样的单——两张待处理的单,人办了一张、另一张一直挂着(调模型的活动一拍常过软时限,正是最容易撞上重投的那一种)。现在同一个任务节点这一趟里同一把键只派得出一张:再派答原来那一张的
orderId,不另建、不另算一次人工调用;只认键与定义、不比内容(重投回来的那一拍调模型再抽一遍,字不同是常态,内容以第一张为准);同一把键换一种定义是invalid(两件事撞了名,不是重投)。键归「这一趟」:回头的边再进这一步是新的一趟,照旧派新单。 - SDK 13.2.0 的
cap.order()缺省带键:按「事件 id · 定义 · 第几次」拼成<eventId>:<definition>:<n>——同一条事件重跑一遍,第 n 次派的还是第 n 把键;活动要按自己的事实(料号、供应商)认的,自己给idempotencyKey。不在事件里派的(活动自己起的别的路)不带缺省键,照旧。 - 老包一个字不必改、照旧装得上(它们不带键,照旧两张)。要这一道的包把 SDK 升到 13.2.0 重打、
contract写^13.2.0。系统侧:bpos_orders.orders多一列idempotency_key与部分唯一索引orders_idempotency_once(迁移 0077;开机自检撞一次)。
13.1.0:表格行上的条件必填可以落在只读列上——条件读的每一列人都改得动时(清单侧放宽,小版本)。
- 活动预填、人改不动的一列(技术解析单上客户要的「型号」),从前一律不许标
rows.requiredWhen:理由是「值由活动给,标成必填等于一条人走不出去的路」。可条件若读的是人改得动的列(「判定」),这一格红了,人改判就消——「判了照供,型号就不能空」正是这种拦法,从前写不出来。现在requiredWhenIssues对表格行放行这一种:目标是只读列、条件里每一片叶子读的列都在rows.edit里(不是算格)。条件里有一列人也改不动的照旧拒,并点名是哪一列;算格、只读表格照旧拒;顶层requiredWhen不动(投影在只读的顶层格上不带条件,放宽它件上就对不上)。缺口照旧由missingOf按行算、指到那一格,件上的红带、宿主提交区与系统提交那一道同一把尺。 - 从前过得了机检的清单一格不变;老包不改。要在只读列上写行条件必填的包写
contract: "^13.1.0"(13.0.0 的系统装它时机检仍会拒)。SDK 不跟(它不用这一道尺),仍是13.0.0;视图协议不变。
13.0.0:「主数据」改叫「经营对象」,这一层只有对象(清单侧收窄,大版本)。
MASTER_TABLES撤掉两种单据:master.fx_rate(汇率)与master.part_notice(停产 / 变更通告)。发生的事与此刻的状态都有属主子系统——这两样由某个子系统的登记活动记成单据、推它的台账(当前汇率 · 物料生命周期),别的活动要读就照属主原样声明依赖副本。MasterTableSpec.kind收成'object',attachedTo·time两格随之撤掉(经营对象不挂靠别人)。- 对象上不放会变的处境:
master.part撤掉lifecycle(由通告推的那本台账接);master.party_brand.status撤掉「过期」这个取值(过没过期按「有效期至」与今天现算,不另存一格)。 - 机检第五查多一句:
needs.tables里的master.*只认这一层真有的几张,写了撤掉的地址当场 fail 并说该去抄哪一份副本(从前要到装包那一刻才撞上「没登记」)。 - 线材上一个值都没动(
wire-freeze的FROZEN另写一份 13,与 12 逐字相同);兼容语料从这一版起按大版本分目录(tests/corpus/<大版本>/),12 那一批留档不再量——它们本来就装不进 13 的系统。 - 老包要跟的:读过
master.fx_rate·master.part_notice· 物料lifecycle的改读属主子系统的表(依赖副本);读品牌授权「过期」的改比有效期至;其余的包只把contract范围改成^13.0.0、SDK 升到13.0.0重打。「主数据」这个叫法从屏上、文档与机检的话里撤了,地址前缀照旧是master.。
12.3.0:经营对象 master.project 的「状态」缺省「立项」(经营对象形状只加不改,小版本)。
- 经营对象那一层的形状随契约包走(
MASTER_TABLES):master.project的status多一格default: '立项'——此后新建的项目(落定建对象、补建卡「建」、数据模块建一行)这一格不再是空的;既有行不动(空着的是实话「没人说过到哪一步」)。库这一侧由服务端迁移落缺省、刷登记行,数据契约跟着升一个小版本。 - 老包一个字不必改:活动不许声明
master.*,只引用;引项目的包照旧装得上、跑得起来。SDK 不跟,仍是12.1.0;视图协议不变。
12.2.0:件上的注钉得到取形解出的格(清单侧放宽,小版本)。
- 作业指导书的步(
guide.steps[].at)与验收判据的条(acceptance.criteria[].at)从前只认节 id 与data.fields里写明的格——那道核挂在工单定义的 zod 上,清单解析那一刻手上没有表的形状,单头取形(data.shape)解出来的格(件上的主体)写at当场被拒,活动只能钉到那一格所在的节。现在清单解析那一道对「取了形的单、名字又不在写明的格与节里」的锚点先放过,机检第七查(orderIssues)拿定形之后的那一份照全部的格判,指不到照旧点名、把挂得住注的地方(含取形解出的那几格)列出来。模块钩子issues(o, shaped?)多一个可选参数(orderKindIssues(o, shaped))。 - 老包一个字不必改;要把注钉到取形解出那一格的包写
contract: "^12.2.0"(12.1.x 的系统解析清单时会拒)。SDK 不跟,仍是12.1.0;视图协议不变(投影本来就按渲染模型里的格解锚点)。
12.1.3:发号的日期段是号上印的那一段(发号的缺陷修正,清单一个字不必改)。
- 按月编的号(
date.format: 'YYYYMM')从前在系统里按「哪一天」分线数:同一个月的第二天起又从 1 数,撞上已经发出去的号(开发实例上DMD-202609-0001被第二次发出,落定按撞键被拒)。现在取号按号上印的那一段分线——按月编的一个月一条线、按日编的一天一条线。拼号与数号用的是同一段,由契约新导出的serialDateSegment一处算(formatSerial也经它);服务端建一条新线时接着同一段里按天记的旧计数数,已经发出去的号不重发。 - 老包不改;
contract范围不必跟(这一处是系统的行为,清单的形状与机检都没动)。SDK 不跟,仍是12.1.0;视图协议不变。
12.1.2:表格一行的身份:带条件的行唯一,条件要只看身份列(行身份的缺陷修正,只放宽)。
- 勾选表(比价单「勾上的行里同料同档只许一行」,
rows.unique带when: selected):条件落在身份之外的「选中」上,人一勾一不勾,同一行的身份就在「按列」与「按行序」之间跳,不许加删行的表格存件那一道当成删了一行又加了一行(话里的列名还是空的「」)。现在rowIdentityColumnsOf只拿「条件只看这一族行唯一里的身份列」的那几条认身份(R01 那两条——有客户料号按客户料号 + 数量、没有的按型号 + 数量——条件看的是客户料号,行为不变);别的只作提交时的唯一约束。 - 从前存得下的件照旧存得下;老包不改。用勾选表的包写
contract: "^12.1.2"(12.1.1 的系统上存件被拒)。SDK 不跟,仍是12.1.0;视图协议不变。
12.1.1:rows 项里的引用格先看行内、再看顶层——机检照这一条判(机检的缺陷修正,只放宽)。
- 单头一格币种、每一行也一格币种(报价单头的币种、行上可另写)是单据的常态。落定取值(
planCommit)与同名直落(resolveCommit)一直按「行内先、顶层后」取,机检第七查却在两处同名时判「说不清指的是哪一个」拒掉——同名直落自己解出来的那一格被自己拒,这样的单据一张都声明不出来。现在机检与另两处同一条规矩:rows项里{ ref }先找行内的列,找不到再找顶层字段。 - 从前过得了机检的清单一格不变;老包不改。单头与单体各有一格同名引用的新包写
contract: "^12.1.1"(12.1.0 的系统装它时机检仍会拒)。SDK 不跟(它不用这一道尺),仍是12.1.0;视图协议不变。
12.1.0:一次复审——改了一处、加了两道闸(见上面「两道机器闸」;定义者随后定:先不锁正式版)。
- 系统答给活动的对象一律不严格:
order.read的办结段(orderDoneSchema)与答复里的文件引用(file.put的答复、doc.list的ref, 改用同几格的fileRefOutSchema)从前是严格对象——系统哪天往里多答一格,内联了它们的包整条答复解不出来,与「往载荷里加一格不破」 这条判据自相矛盾。活动递进来的文件引用照旧严格(多一格多半是写错了)。 - 两道机器闸:
tests/wire-freeze.test.ts与tests/compat-corpus.test.ts(发出去的十个包的清单进了语料)。 - 只放宽,老包不改;要这一条的包把 SDK 升到 12.1.0 重打(放开的是包里内联的那一份)。SDK 同版
12.1.0;视图协议不变。
12.0.0:文档类型也有依赖副本——要读的每一类都在清单里声明(清单侧收窄,大版本)。
- 11.0.0 给表开了依赖副本,文档类型还照「已登记或本清单声明」判:一个读别的活动归档的那一类(
needs.documents)的活动,新库上属主没装过,那一类没登记,第五查照样拦——与 11.0 要解决的是同一个问题。 - 现在
declares.documents[]多一格copy: true:同一个子系统里别的活动归档的那一类,照属主那一份原样抄一份;needs.documents里的每一类都要在declares.documents[]里声明(tableIssues说,与表同一条规矩)。激活那一刻副本没登记就按它登记(登记在本活动所在的子系统下——文档挂子系统,读别的子系统归的文档此刻没有路),登记了只对种类,不改名、格式与说明,也不把这一类改记到自己名下;种类对不上,机检第五查先说、激活整趟拒。副本也可以当归档的目标(同一个子系统里的类本来就归得进)。 - 老包要改:
needs.documents里列着、没在declares.documents[]声明的那几类,照属主原样声明一份副本;contract写^12.0.0(此刻仓里与活动仓的包都没用needs.documents,只是范围要跟)。SDK 同版12.0.0;视图协议不变;MCP 契约、数据契约不动。
11.0.0:依赖副本——一个活动碰到的每一张表都在自己的清单里声明(清单侧收窄,大版本)。
- 从前只读别人的表只在
needs.tables里列地址,机检认「已登记或本清单声明」——于是一个读别人表的活动单独装不进一台新库:那张表的属主没装过,表就不在,第五查拦下(后来为互读的一串活动放宽成「这一台上别的活动装着的版本声明了只警示」,新库上照样拦)。设计的本意是「无论持有的还是依赖的,都声明、装的时候对比创建」。 - 现在
needs.tables里经营对象之外的每一张都要在declares.tables里声明:自己持有的照旧;别的活动持有的照属主那一份原样抄一份、标copy: true——依赖副本。挂靠的目标同理(挂靠列取自它的主键,新库上得先建得出来);副本refs指到的表照旧要列进needs.tables。激活那一刻副本走同一条路:没有就按它建(建在属主那个子系统下,那个子系统这一台上要已建,没有就拒并点名),有就对形状、只加缺的列。 - 副本不带来写权:写不写得了照旧按域判——同子系统的表写得,别的子系统的只有属主在
postableBy里许了的台账能过账(needs.tables标write)。机检算写权与跨域过账用这个活动所在的子系统(writableTablesOf·tableIssues·orderIssues多一个可选的subsystem参数,服务端递;仓外打包推不出域、自己一张表都不持有时,落到副本上的那几条判不了、不拦)。 - 副本不改人读名与注解(那归属主那一份),也不替属主挨注解的警示(对象没标人读名列、单据没写业务时间:抄的人改不了,改了就不是原样)。
- 一张表只有一个属主:
registryOfManifests折兄弟包时两份都不标copy即抛;副本与属主那一份除copy外逐字相同,差一个字即抛并指名。 - 撤掉的:第五查对只读的表「这一台上别的活动装着、还没激活的版本声明了——只警示」那一档(副本让互读的一串活动各自就装得进,不必排先后)。
- 老包要改:只在
needs.tables里列着、没声明的别人的表(与挂靠的目标),照属主原样声明一份副本;contract写^11.0.0。SDK 同版11.0.0;视图协议不变;MCP 契约、数据契约不动。
10.5.1:缺口那几句念那一格的人读名(缺陷修正,只改话、不改形状)。
- 取形解出来的顶层格(单头的「要价期限」这一类)清单里多半没写
text,fieldTextsOf从前直接退回列名,于是缺口、拒绝那几句念给人听的是「「deadline」是必填的」——一个他不认得的词;表格里的列走的是另一条路,一直念对。现在没写text的格先用它自己身上的人读名(表列的label),再退才是列名。 - 老包不改、清单一格不动。SDK 不跟(它不用这一只函数),仍是
10.5.0;视图协议不变。
10.5.0:能力名词加一个 event.fail——软确认之后才抛的那一拍不再静默丢掉(只加一个名词,小版本)。
- 一条事件处理超过软时限(SDK 缺省 20 秒),SDK 先答确认、活儿接着做;之后抛了,系统那一侧记着「送到了」、不再投——任务停在「执行中」、没有一张单、屏上一个字都没有,只有工程日志里一行。调模型的活动一拍常过 20 秒(读两页来件、问一次模型就是二三十秒),换一个活动一样会撞。
- 现在 SDK 在那一支里自动报
event.fail(那一条的 id 与抛出来的那句话):系统把它放回队里,按「没送到」那一套退避重投、次数接着数,三次不成保护性挂起,理由里写着活动报的那句话;工作日志落一行。与答 500 那条路(软时限之内抛的)同一套语义,「一拍抛了不静默丢掉」对长拍也成立。 - 活动不必自己调它;要做的是它本来就该做的:一条事件重投回来照状态接着做(至少送到一次)。老包不改(它们内联的是老 SDK,照旧只进工程日志);想要这一条的包把 SDK 升到
10.5.0重打,contract范围照旧即可(名词是系统答的,清单不声明它)。SDK 同版10.5.0;视图协议不变。
10.4.1:客户与联系人两条引用共用一列时,后一条的锚列退到它自己的下一列(取形的缺陷修正,只放宽)。
- 往来记录、询价单、报价单常一起引「哪家客户」(
master.party)与「那家的哪一位」(master.contact,主键party_code + contact_code)。联系人的人读名列不在键里,它的自然锚列是party_code——而客户那一条早占着它,从前后一条整条不成格(机检七 fail),件上少一格、落账也取不到。 - 现在锚列按线的次序分(
linkAnchorsOf):自然锚列被前面的线盖着时,退到这条线自己的下一列(按目标主键的次序),那一列得是它头一个盖着的——联系人那一格以contact_code命名。退无可退的照旧整条不成格、点名。取形、同名直落与数据模块印人读名三处同一条规矩(数据模块那一侧从前会把联系人的名印在客户那一格上)。 - 只有自然锚列被占着时才退,所以从前解得出来的表一格都不变;老包不改。两条引用都要的新包写
contract: "^10.4.1",指向客户的那一条写在前面。SDK 同版10.4.1;视图协议不变。
10.4.0:照片——表多一种列、件上多一种格,只加不改(有些单据要关联几张照片:收货的外箱、来料的丝印、破损的现场)。
- 表的列类型多一种
image(TABLE_COLUMN_TYPES= 六种标量 +image,库里一格jsonb):一格放一组照片的引用(photoRefSchema:原图的sha256·name·format·size,另带缩略图thumb: { sha256, size },活动放进来的那一张可缺),至多 20 张,格式只收 JPG · PNG · WebP、单张 10 MB。照片列只落在对象与单头上:台账、单体不许,也不作主键(机检拦)。COLUMN_TYPES不变——输入与产出字段、件上表格的列都不收照片。 - 件上格多一种
image(FORM_FIELD_TYPES):只在顶层;必填的一张都没有是缺口(missingOf「一张照片都没有」);值按checkValue那一把尺判;不带缺省(照片是这一回传的,要预填由活动在派单时放进数据)。取形时单头 / 对象上的照片列解成件上一格照片,表格取形时照片列不上件(必填的照片列报 warn);同名直落照旧。 - 读的那三只手(
file.get·file.text·file.info)收照片引用:table.read读到的那一张原样递过去就读得到原图。 - 清单只放宽,老包不改;要用照片的新包写
contract: "^10.4.0"。SDK 同版10.4.0。视图协议 3 → 4:渲染模型多一种控件(按 3 写的件认不出照片格,那一格空着而屏上一个字都不说),桥多两条消息(宿主把缩略图递进件、件请宿主开大图)。
10.3.0:两格相等与号段,只加可选声明,不改旧定义(总账那一路要的两样;也用得上发票、付款核销、多仓入库)。
interaction.equal: [{ fields: [甲, 乙] }]:两个顶层数值格(常是两格合计算格)都算得出来而不等,就是一条提交前的缺口、指到第一格——件上的红带、宿主提交区与系统提交那一道同一把尺(missingOf)。它是校验,不是新的算格(算格仍只有乘积与合计)。机检拦两格不在件上、不是数值格、是同一格。serial.segment: { field }:号按件上一个文本 · 枚举 · 引用的顶层格分开数,拼在前缀之后(JZ-HK-202609-0001);引用取它指的那一行的主键。取号按「前缀 + 号段」当一条号线(库里_serials的prefix那一列存的就是这一截,库不改);那一格空着落定时指名拒绝。拼号与号线由契约一处算(formatSerial·serialLineOf)。- 清单只多可选格,老包不改;要用的新包写
contract: "^10.3.0"。SDK 同版10.3.0。视图协议仍为 3:渲染模型多一格可选的equal,按 3 写的件读不到它照旧渲得出,服务端提交照旧拦。
10.2.0:工单件上的两类通用规则,只加可选声明,不改旧定义。
interaction.rows[表格].requiredWhen让一列在本行条件成立时必填;rows.unique让完整列组合在命中本行条件后不得重复。两者都用已有条件算子;unique与keys分开,重复可保存草稿、提交时指到第二行第一列。件、宿主与服务端共用缺口语义。- 清单只多可选格,老包不改;需要这些能力的新包写
contract: "^10.2.0"。SDK 同版10.2.0。视图协议仍为 3:旧视图可继续渲旧定义;新定义应使用已支持行规则的默认视图或更新自定义视图。旧自定义视图若忽略规则,服务端提交仍会复核并定位拒绝。
10.1.0:两条新路,都是只加不减(定义者 2026-09-22 定;产品设计 §1.4,系统设计 §3.8 · §6.1 · §6.4 · §8.2)。
- 跨域过账走台账自己的白名单:台账的声明多一格
postableBy——一条一张外域单据表 ({ voucher, columns }),列明这张单据过得了本账的哪几列。属主域由此掌握「谁能推我的状态」; 跨域只许post(记单据、冲单据、建改对象仍只在本域),无单据不过账不变——那一笔的数仍出自 本次落定写下的单据行,而那张单据是推动方本域的。needs.tables标write的外域表从 「只有master.*」放宽成「master.*或别的域的台账」。三道核:装包机检(目标台账的形状此刻 解不出来的只报警示)· 激活那一刻的形状比对(白名单按只加不改)· 落定那一刻按登记里此刻 那一份再核一次。 - 活动找得到这一单目标名下归过的文档:能力名词加一个
doc.list(加一个不破,见上面那条 判据),清单needs加一格documents(要读哪几类,机检第五查与表地址同一把尺)。文档仍不挂对象: 找得到是因为文档行上记着归档它的那条任务、任务上钉着这一单的目标。
老包要改什么:一个字都不必改。两样都是可选的新格与一个新名词,^10.0.0 的范围照旧装得上;
要用哪一样再声明哪一样。SDK 同版(@bpos/activity-sdk 10.1.0,多一只 cap.docs())。
10.0.0:工单种类收成三种(定义者 2026-09-22 定;产品设计 §1.5 / §3.5,系统设计 §3.9)。两件都破:
- 协同改名代理:封闭集里那一个取值
collaborate改成agent,屏上的章「协同」改「代理」, 「协同工单 / 协同制品 / 协同列表 / 协同办单」一律改成「代理…」。「协同」这个词从此空出来, 留给以后新增的槽位。契约面上另有两处改名:COLLAB_DESK_KINDS→AGENT_DESK_KINDS、 登记表那一格listedOnCollabDesk→listedOnAgentDesk(活动包碰不着这两样)。 - 沟通工单整一种撤了:封闭集减一个值
communicate,连带撤communication那一块拓展契约 (matters与mail)、系统事件work-order-mail与它的载荷、orderMailSchema/OrderMail/MAIL_DIRECTIONS/MAIL_TEXT_MAX/MAIL_PARTS/mailDeclSchema/matterSchema与./mail(mailDraftOf/mailDraftIssues/parseAddresses/MAIL_RECIPIENTS_MAX)、order.read答复里的mails,以及ORDER_RESULTS里只有它用的completed。 邮箱不再与工单有任何关系:系统 › 邮箱页、企业邮箱开通与自检、事件规则以邮件为源起单、 邮件记录与水位都还在,它们本来就不是活动的能力。
破在哪:封闭集减一个值、又改一个值的名——旧包把枚举内联进了 bundle,
一份 kind: 'collaborate' 或 kind: 'communicate' 的清单在 10.0 的实例上装不上
(机检第七查当场拒并指名),已经装上的那几张单由迁移 0069 就地改名 / 删行。
老包要改什么:清单 contract 范围改 ^10.0.0;kind: "collaborate" 一律改成 "agent"
(那一块拓展的键 artifact 一个字不动);kind: "communicate" 的那几条定义没有替代——
要与外界往来一次的,改派一张执行工单把往来记下来,信由人在自己的邮箱客户端里发;
读 work-order-mail 与 order.read().mails 的代码删掉。SDK 同版(@bpos/activity-sdk 10.0.0)。
拒绝
业务上的拒绝用 HTTP 200 承载(ok: false + error + message + hint)——它不是传输错误,
是系统对这次调用的回答,活动要读得到才知道下一步做什么。真正的 4xx 只有两种:
令牌不对 401、这一步已交出或这一单已挂起 409。码是封闭集,见 CAPABILITY_ERRORS。
Dependencies
Dependencies
| ID | Version |
|---|---|
| zod | 4.4.3 |
Development Dependencies
| ID | Version |
|---|---|
| @types/node | ^24.0.0 |
| tsx | ^4.19.0 |
| typescript | ^5.6.0 |