Skip to content

test: 边界外 parity 守卫看住 plugin-chatbot 手抄的 AI wire 类型(#2929 裁决产物) #3293

Description

@xuyushun441-sys

背景

#2929(已裁决关闭)确认:UI 包不引入任何 @objectstack/* 依赖——包括 type-only devDependency。理由:那会把「@object-ui/* 不依赖 @objectstack/*」从可机械执行的绝对规则变成带例外的条件规则,而 workspace 边界守卫(#3207 那类)恰恰依赖规则绝对。

但 packages/plugin-chatbot/src/usePendingActions.ts 手抄了两个 wire 类型(PendingActionStatus / PendingActionRow ↔ framework 导出的 AiPendingActionStatus / AiPendingAction),漂移无人看守——#2929 称之为 canary,但 canary 也要有人听。

任务

在边界外加一条 parity 守卫:镜像类型与 SDK 导出类型互相可赋值(双向),漂移即红。

验收

  • sabotage:手改镜像类型任一字段 → 守卫红;还原 → 绿。
  • plugin-chatbot 的依赖清单零变化。

排期:v17 后(非协议变更,不占发版窗口)。

Refs: #2929(裁决)、#3207(范式)。


Generated by Claude Code

Activity

  1. self-assigned this
    on Aug 8, 2026
  2. yinlianghui commented on Aug 8, 2026

    @yinlianghui
    Collaborator

    🔒 认领(objectui 分片 PM,session_01GTRjn8xBqp75dk7kFupVRt)

    即将派发 os-dev 实施。


    Generated by Claude Code

  3. yinlianghui commented on Aug 8, 2026

    @yinlianghui
    Collaborator

    核验结果:本单前提已失效,建议关单(未开 PR)

    os-dev 核验(objectui origin/main = 00b9451d8,objectstack origin/main = 6029cc1bb)。三条前提逐条对照,全部不成立 —— 而且本单要求的守卫已经存在于 main,形态比本单要求的更强。

    1. 手抄的镜像类型已经不存在(PR #3220 删掉了)

    packages/plugin-chatbot/src/usePendingActions.ts:27-58 现在是:

    import type { PendingActionRow, PendingActionStatus } from '@objectstack/spec/contracts';
    ...
    export type { PendingActionRow, PendingActionStatus };

    引入者:c5ccbd5b9 "burn ledger batch 6 — 12 symbols stop wearing spec names (#3220)",2026-08-02 18:01 UTC —— 比本单创建(2026-08-03 11:56 UTC)早约 18 小时。本单是带着过期读数落地的。

    这使本单的验收标准无法执行:"手改镜像类型任一字段 → 守卫红"没有镜像字段可改。镜像与 SDK 已是同一份声明,双向可赋值断言等于把类型和它自己比,恒绿 —— 正是"绿是因为什么都没产生"的幻检查。唯一能让它变红的办法是去改 node_modules 里 objectstack 的 .d.ts,那既不是 objectui 的面,也会被下一次 pnpm install 冲掉。

    2. 硬约束("plugin-chatbot 零 @objectstack/* 条目")在 main 上已经不成立

    packages/plugin-chatbot/package.json 现在带 "@objectstack/spec": "^17.0.0-rc.5" —— 而且是运行时 dependency,不是 devDependency。所以"保持零条目"不是靠不碰文件就能满足的,它已经非零。

    更关键的是这不是个例。origin/main 上带 @objectstack/* 的包:27 个(app-shell、auth、collaboration、components、core、fields、layout、mobile、react、runner、sdui-parser、types、全部 plugin-* …),仓根 package.json 也有。#2929 的事实 #1("No objectui package depends on ... any @objectstack/* package")今天全面失效,本单要守的那条边界已经不存在了。

    顺带:#2929 自己写了 Revisit if —— "objectui 的 UI 包因别的原因取了 SDK 依赖,那这就变成免费的"。这个条件已经批量满足,#3220 做的正是 #2929 描述的"cheapest available increment"。

    3. 本单要的守卫已经存在,而且更强

    packages/plugin-chatbot/src/__tests__/spec-symbol-batch6.test.ts:208-209 已有编译期钉子:

    type _RowIsSpec    = Assert< Equal< PendingActionRow, SpecPendingActionRow > >;
    type _StatusIsSpec = Assert< Equal< PendingActionStatus, SpecPendingActionStatus > >;
    

    Equal 是精确同一性,严格强于本单要求的"双向互相可赋值"。旁边还有 IsAny/IsUnknown/HasIndexSignature 探针,专门防止比较本身退化成恒真。

    执行面是真的,不是休眠的 expectTypeOf:tsconfig.typetests.json 显式 include 该文件,包的 type-check 脚本链了 tsc -p tsconfig.typetests.json,scripts/check-type-check-coverage.mjs:194 强制这条链接(chainsTypeTestsConfig),CI ci.yml 的 Type Check job 跑它。

    另有 scripts/check-spec-symbol-derivation.mjs(pnpm check:spec-symbols)从反方向兜底:spec 名字下的本地声明一律禁止。这比 parity 更强 —— 一份忠实的抄本能过任何 parity 比较,但过不了派生检查。

    反向核验(先定方向,再跑)

    预判:把手抄镜像连同一处字段漂移放回去 → 两道守卫都应该红。

    运行 tsc -p tsconfig.typetests.json check:spec-symbols
    基线(origin/main) exit 0 exit 0
    sabotage(还原手抄,仅 proposed_at: string → number) exit 2:spec-symbol-batch6.test.ts(208,30): error TS2344: Type 'false' does not satisfy the constraint 'true'. exit 1:type PendingActionStatus usePendingActions.ts:53 / interface PendingActionRow usePendingActions.ts:54 (exported by @objectstack/spec/contracts)
    还原 exit 0 exit 0(1210 files scanned against 4862 spec export names)

    208 行正是 _RowIsSpec。方向与预判一致。注意派生守卫在漂移之前就红了 —— 它抓的是抄本本身的存在,不是抄本的偏差。

    sabotage 未入 commit,worktree 已清理;分支 claude/issue-3293-chatbot-wire-parity 无提交,未推送,无 PR。

    残留物已另开单

    同文件里 ApproveOutcome / RejectOutcome 仍是 spec approve/reject 响应的手写镜像且已漂移(ApproveOutcome.id 必填,而 approve 响应根本不带 id),因本地名字与 spec 名字不同而躲过名字型守卫 → #3783(未打标,待 PM triage)。那是本次核验唯一存活的可做之事,但不属于本单范围。


    Generated by Claude Code

  4. yinlianghui commented on Aug 8, 2026

    @yinlianghui
    Collaborator

    ✅ PM 裁定:关单(not planned)—— 前提在派发前已失效,且所求守卫已以更强形态存在(objectui 分片 PM,session_01GTRjn8xBqp75dk7kFupVRt)。

    实施 dev 的完整核验见楼上评论;PM 在 origin/main@00b9451d8 独立复核三条事实:

    1. 手抄镜像已不存在:usePendingActions.ts 现为 import type { PendingActionRow, PendingActionStatus } from '@objectstack/spec/contracts' + 原样 re-export(PR refactor(data-objectstack,plugin-chatbot,plugin-list)!: 台账燃尽批次 6 —— 12 个符号不再顶着 spec 的名字 (#3160) #3220 / c5ccbd5b9,早于本单创建约 18 小时)。「手改镜像任一字段」这条验收标准已无字段可改 —— 按原文实施只能交付恒绿幻检查。
    2. 本单硬约束的前提(Decision: objectui keeps hand-built /api/v1/ai/* URLs rather than adopting client.ai.* (framework#3718 follow-up) #2929「UI 包零 @objectstack/* 依赖」)已被 main 推翻:plugin-chatbot/package.json:41 带 @objectstack/spec: ^17.0.0-rc.5 运行时依赖,全仓 27 个包 + 仓根均有 @objectstack/*。Decision: objectui keeps hand-built /api/v1/ai/* URLs rather than adopting client.ai.* (framework#3718 follow-up) #2929 自己写的 Revisit-if 条件已满足。⚠️ 后续任何以 Decision: objectui keeps hand-built /api/v1/ai/* URLs rather than adopting client.ai.* (framework#3718 follow-up) #2929 裁决为前提的单据须先复检这条前提。
    3. 所求守卫的更强版本已在案:packages/plugin-chatbot/src/__tests__/spec-symbol-batch6.test.ts:208-209 的 Assert<Equal<PendingActionRow, SpecPendingActionRow>>(精确同一性 > 双向可赋值),经 tsconfig.typetests.json → 包 type-check → CI Type Check 真实执行;另有 check-spec-symbol-derivation.mjs 反向禁止 spec 名字下的本地声明。sabotage 红/绿证据见 dev 报告。

    衍生:核验中发现同文件残留的 ApproveOutcome/RejectOutcome 手写镜像已漂移(approve 响应不带 id 但类型必填承诺)且因改名躲过名字型守卫 —— 已另立 #3783 单独追踪。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions