feat(integration): 连接器动作可声明上游效果,connector_action 因此可计数 (#4395) - #6160
Conversation
`ConnectorActionDescriptor` 只描述动作的形状,对它究竟读还是写只字未提,所以 `crm.push_opportunity` 和 `crm.lookup_account` 在运行时无法区分。#4354 的断扫告警 (`selected > 0 AND acted = 0 AND unmeasured = 0`) 因此对所有连接器驱动的流程都是盲区: 执行器只能报 `unmeasuredEffect`,既无法证明干过活,也无法在停止干活时被标记。 `ConnectorActionSchema` 新增可选 `effect: 'read' | 'write'`,`connector_action` 执行器据此三分支计数:声明 write 且派发成功 → acted:1;声明 read → acted:0(真实测得 的零);不声明 → 维持 unmeasuredEffect。派发失败时声明 write 回落为不可计数(上游可能 已写成,与 http 节点对被拒绝的写请求判断一致),声明 read 仍报 acted:0。 `effect` 落在可作者化的 ConnectorActionSchema 而不只是描述符接口上,因为那是唯一可能 的产地:registerConnector 存的是 ConnectorSchema.parse(def),描述符是从这份 def 投影 出来的;只加在描述符上则永远无法被填充(非 strict 的 z.object 会静默丢弃作者写的 effect,已实测)。同一声明经 getConnectorDescriptors 送达设计器。 count 型效果(bulk)不做,待真实需求。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011M7UwH25Unfi73UHim7ajY
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
…nector-action-effect
本仓的 regen 合并驱动在 merge 时把生成产物留给合并后重生成,因此这一次 `gen:docs` 吸收的是 main 侧 #5552(`FieldMapping.transform` 退役)的既有后果,不是本 PR 的改动; 本 PR 自己的 `ConnectorAction:effect` 行原样保留。check:generated 十门恢复全绿。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011M7UwH25Unfi73UHim7ajY
📓 Docs Drift CheckThis PR changes 2 package(s): 112 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
|
队列管家:新签名 ⇒ ⛔ 不重投,留完整签名与判读(台账无此条,且这是真实门禁失败,不是 flaky) 踢出事实(两读数):
|
|
队列管家:读数提醒 —— 本 PR 于 实读(PR head
这正是 06:20Z 拦截评论里门禁点名的那一行(裸名 为什么值得提这一句:合并队列是全仓共享的串行资源,一次注定失败的构建会让排在后面的条目跟着重建一轮。若车道认为该门禁在本例是误报,那是另一回事 —— 请在本单说明,本座位不会代为判断 ADR 的适用性。 Generated by Claude Code |
…nector-action-effect
#6072 的防回流门 check:spec-parsed-alias 已落 main,本分支建于其前,新增导出需补齐时序。 `ConnectorActionEffectSchema` 是一个裸 z.enum —— 无 default、无 transform,z.input 与 z.infer 同构 —— 按 D5 判据不配 `XParsed`(那会给作者一个只能选错的同义名),按 D6 改为在 type-alias-convention.pin.test.ts 立编译期断言。与其上方两行的 ConnectorType / ConnectorStatus 是同一类,故并入 integration/connector.zod.ts 分块;编号接文件末尾续号 (IsoNNN 只是唯一名,门读的是 z.input 出现处),避免为补一个空位重排其后 300 余行。 同时把 pin 计数断言 718 → 719 —— 该用例原注释只预期「减一」,这里补记它同样会因新增 同构别名而「加一」。 实测 D6 确实咬得住:临时给该 schema 加 .default('read') 后,check:test-typecheck 报 `src/type-alias-convention.pin.test.ts: 1 type error(s) in a file the ledger does not cover`(注:裸 tsc --noEmit 不覆盖 *.test.ts,证明落在 typecheck 脚本的第二步)。 api-surface/integration.json 一并重生成:regen 合并驱动在 merge 时把该产物留给合并后重生成, 本次补回 #6072 的 10 个 XParsed 条目,本 PR 自己的两个条目原样保留。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011M7UwH25Unfi73UHim7ajY
|
Queue steward — closing out this seat's own 06:54Z read-out. The ADR-0122 gate is now GREEN on this branch; the seat stands down. At 06:54Z this seat posted a read-out predicting the branch would be caught by What changedHead moved That is the correct route for this symbol: Verification (two readings, not one)
Collateral, for the recordThe 06:53Z Seat boundary⛔ This seat has not re-queued, not flipped ready/draft, not touched code, not touched the claim. Remaining checks on Generated by Claude Code |
Fixes #4395
落地 2026-08-06 维护者最终确认的裁决(评论 #5207142016):
ConnectorActionDescriptor增可选
effect,connector_action执行器按声明计数。count 型效果(bulk)不做。问题
ConnectorActionDescriptor只描述动作的形状,对它究竟读还是写只字未提,所以crm.push_opportunity和crm.lookup_account在运行时完全无法区分。#4354 的断扫告警是selected > 0 AND acted = 0 AND unmeasured = 0,而connector_action只能给出三个答案里最诚实的那个:
acted: 0低报一次 Salesforce 创建 —— 每条健康的连接器扫描都触发告警,操作员很快学会忽略它;acted: 1高报一次查询 —— 告警永不触发,那正是 Surface flow run summaries (selected / acted / skipped) — a scheduled flow that does nothing is currently indistinguishable from one with nothing to do #4354 要修的原始 bug 换层重演;unmeasuredEffect诚实,也是盲区:走连接器的流程贡献不出任何信号,既无法证明干过活,也无法在停止干活时被标记。
改动
ConnectorActionSchema新增可选effect: 'read' | 'write',执行器三分支计数:writeacted: 1unmeasuredEffect(上游可能已写成)readacted: 0(实测的零)acted: 0(它无论如何改不动东西)unmeasuredEffect(原样)unmeasuredEffect(原样)失败路径与
http节点对被拒绝的写请求所做的判断逐字一致。声明是可选的:已有连接器一个字不用改,报告内容与之前逐字相同。同一声明经
getConnectorDescriptors送达设计器,GET /api/v1/automation/connectors现在带上effect。effect落在可作者化面上派单边界写的是「裁决只提 descriptor,authorable 面不在本单」。实施中确认描述符单独加字段
不可能成立,因此把
effect加在了可作者化的ConnectorActionSchema上(authorable-surface分片 +1 键),证据:
ConnectorActionDescriptor全仓唯一产地是engine.getConnectorDescriptors()(
packages/services/service-automation/src/engine.ts:1942),而它是从RegisteredConnector.def.actions投影的;registerConnector存的是ConnectorSchema.parse(def)(同文件:1687),插件路径与ADR-0097 声明式 materialization 路径(
plugin.ts:1263)都经过这一次 parse;ConnectorSchema是非 strict 的z.object,改动前实测:作者写下actions: [{ key, label, effect: 'write' }],parse 后得到{"key":"push","label":"Push"}——
effect被静默丢弃。即:只加描述符字段,该字段永远无法被任何东西填充,执行器的 write/read 分支永久不可达 ——
一个 ADR-0049「declared but never enforced」的死键。现在它从第一天起就被运行时读取。
connector不是受治理的 metadata 类型(packages/spec/liveness/无connector.json),因此没有 liveness ledger 义务;
check:liveness已通过。请维护者复核这一格可作者化面。反向验证(方向事先声明)
Revert A —— 删掉执行器的 effect 分支(恢复无条件
unmeasuredEffect)。预测:三个断言「已声明」判决的用例转红,而「未声明」用例与「声明 write 但派发失败」用例应保持绿 ——
它们钉的是未变的兜底。实测完全一致,4 红:
Revert B —— 保留执行器分支,删掉产地键,用于证明可作者化键是承重的。第一次跑出假绿:
service-automation 从 spec 的
dist解析类型,而我没重建 spec(AGENTS.md §9 陈旧产物陷阱)。重建时 authorable-surface 棘轮直接拦下:
1 authorable key(s) disappeared from the contract。spec 自身测试(跑 src)给出方向:
—— 没有产地键,每个声明读回来都是
undefined。这就是上面那处范围判断的实测依据。验证
pnpm --filter @objectstack/spec test326 文件 / 8367 用例全绿;pnpm --filter @objectstack/service-automation test65 文件 / 783 用例全绿;tsc --noEmit通过;check:generated十门全绿(api-surface / authorable-surface /json-schema.manifest / 参考文档由
--fix重生成,未手改);check:liveness、check:engine-double-contract、check:route-envelope、check:error-code-casing、
check:nul-bytes 等 18 门旁路门全过;改动文件 eslint 干净。
Generated by Claude Code