Skip to content

[ac#1134] packages/opencode 的 typecheck 接进门 + .cases.ts 收录口径裁决(附变异实测) - #1142

Merged
jinjunnn merged 2 commits into
alphafrom
fix/1134-opencode-typecheck-gate
Aug 26, 2026
Merged

jinjunnn merged 2 commits into
alphafrom
fix/1134-opencode-typecheck-gate

Conversation

@jinjunnn

Copy link
Copy Markdown
Owner

Fixes #1134

票面与实测的差:15 → 2

票面记的是 alpha@82bf5d152 上 15 条。本票 base 是 origin/alpha@8e30bdb77,实测 exit 2 / 2 条 ——
那 13 条 TS2345 已由 #1135(收 ac#1129)顺手修掉,它的 commit 正文明写「探针 launch() 类型标注修正
(#1134 的 13 条 TS2345;零断言变动)」。票面不是错的,是过期的;AC1 的判据不变。

15 条的逐条归类

# 文件 error code ADR-043 谓词:①不在 origin/dev ②自报家门 处置
1–13 packages/opencode/test/tool/alpha-725-policy-chokepoints.cases.ts(:512 :530 :538 …) TS2345 ① ✓(git cat-file -e origin/dev:<path> 失败)② ✓ basename alpha-* 本 PR 未改 —— #1135 已修(launch() 形参 (...args: never[])),零断言变动
14 packages/opencode/test/tool/alpha-websearch-failure.test.ts:644 TS2345 ① ✓ ② ✓ 本 PR 修
15 packages/opencode/test/tool/alpha-mcp-websearch-gate.test.ts:342 TS2352 ① ✓ ② ✓ 本 PR 修

三个文件全部满足 ADR-043 的合取谓词 ⇒ alpha 自有,不撞 north-star。守卫实跑输出在下面「闸门」一节。
packages/opencode/tsconfig.json 在 origin/dev 里,是上游文件,本 PR 一个字节没动。

那 2 条各是什么(断言零变动)

  • TS2352 —— denied as Exit.Failure<never, unknown>。fire() 的拒绝走的是 defect,所以
    plugin.trigger(...).pipe(Effect.exit) 的错误通道类型是 never,那个 cast 两个方向都不重叠。
    改用真的类型守卫收窄:Exit.isFailure 的签名是 <A, E>(self: Exit<A, E>) => self is Failure<A, E>。
  • TS2345 —— R4 变异工具的 execute 返回 Effect<…, WebSearchFailure, never>,而
    Tool.Def["execute"] 是 Effect.Effect<ExecuteResult<M>>(E = never)。补 .pipe(Effect.orDie)。
    这不是给变异体松绑:那是它注册得上的必要条件(R4 那份「最省事的第三副本」照样得写它),
    闸在 call() 的传输层、早于任何错误通道处理,而 orDie 只把 Fail 变 Die —— 下游
    Effect.catchCause + Cause.squash 对两者一视同仁。

运行时逐字不变(改前 / 改后同一台机):

文件 改前 改后
alpha-websearch-failure.test.ts 45 pass / 0 fail / 137 expect 45 pass / 0 fail / 137 expect
alpha-mcp-websearch-gate.test.ts 7 pass / 0 fail / 23 expect 7 pass / 0 fail / 23 expect

AC1

$ bun run --cwd packages/opencode typecheck ; echo "EXIT=$?"
$ tsgo --noEmit
EXIT=0

(base 8e30bdb77 上同一条命令:EXIT=2,2 条 error TS)

AC2:选了哪条路,以及为什么

裁决:.cases.ts / .test.ts 在 packages/opencode 里「收进」typecheck,并把该包接进门。
写在 docs/architecture/quality-gate-environments.md §3.11(该文件是本仓质量门的勘破文档)。

三条候选,两条被实测否掉,不是被偏好否掉:

路 判据 结论
A. 改 packages/opencode/tsconfig.json 加 exclude,对齐 ui-mac git cat-file -e origin/dev:packages/opencode/tsconfig.json → 存在 ⇒ 上游文件,动它要 ADR-033 owner 级收编 拒。而且它换来的是更少的检查:住在这个上游包里的 alpha 自有文件跑在 bun 上被直接剥类型,typecheck 是它们唯一的编译期检查
B. 让 ui-mac 也收进,对齐 opencode 探针 tsconfig 实跑 tsgo --noEmit:摘掉 *.test.ts + *.cases.ts 两条 exclude(311 + 9 个文件)= 1264 条;只摘 *.cases.ts(9 个文件)= 129 条。首条都是 Cannot find module 'bun:test'(ui-mac 的 compilerOptions.types 是 ["vite/client","node","electron"]) 拒(不是本票能装下的)。登记进 §6「已知不修」
C. 收进 + 接门(不碰上游) CI 的 typecheck job 加一步 + alpha-check.sh 第 [4/10] 步同一条命令 + CI_STEPS 登记 MIRRORED;防漂由既有的 local-gate-parity.test.ts 双向判 采用

写下来的口径(§3.11):.test.ts / .cases.ts 是否进 typecheck,由拥有该 tsconfig 的一方决定;
但凡一个包的 tsconfig 把它们收进来了,该包的 typecheck 就必须在门里。

今天的取值:packages/opencode 收进 + 在门里;packages/ui-mac 不收(存量 1264 / 129 条,已留痕)。

顺带把「alpha 自有文件住在上游包里」这件事按仓内纪律用两条独立检索轴重新量了一遍
(alpha@8e30bdb77,git ls-files -- packages/opencode 776 个文件):

轴 结果
轴 1:basename 匹配 alpha-* 17
轴 2:git cat-file -e origin/dev:<path> 失败者 18

差集恰好一条且方向正确:src/session/tool-display.ts 不叫 alpha-*,靠首行的
north-star:alpha-owned marker 自报家门(ADR-041)。⇒ 18 个 alpha 自有文件住在这个上游包里,
14 个在 test/ 下,1 个是 .cases.ts
,而它们全被这个从没被门跑过的 typecheck 收着。

AC2 那道门的变异实测(改坏 → 红 → 还原 → 绿)

每一轮开始前 git status 干净、结束后 git checkout -- <单个文件> 还原并复核。

A. 门真的抓得到「.cases.ts 里写错类型」

往 alpha-725-policy-chokepoints.cases.ts 末尾追加 const __mutation_1134: number = "not a number"
(断言一条没碰,追加后立即还原),原样跑第 [4/10] 步的四条命令链:

STEP4_EXIT=2
test/tool/alpha-725-policy-chokepoints.cases.ts(775,7): error TS2322: Type 'string' is not assignable to type 'number'.

还原后 bun run --cwd packages/opencode typecheck → EXIT=0。
⇒ ①.cases.ts 确实在这个包的 typecheck 程序里(口径不是散文);②新步骤真的红。

A′. 反向对照:同一个错误在 ui-mac 的 .cases.ts 里不会红

同一段代码追加到 packages/ui-mac/src/main/alpha-auth.cases.ts:

UIMAC_TYPECHECK_EXIT=0
UIMAC_TEST_EXIT=0     (1 pass / 0 fail)

⇒ §6 里那句「在补上之前,ui-mac 的 .cases.ts 写错类型不会有任何东西变红」是跑出来的,不是推断的。

B. 防漂三形态 —— 其中一形态原本是洞,本 PR 顺带补上

变异 期望 实测
B1 只删 CI 那一步(CI_STEPS 与本地都留着) 红 ✅ 1 fail:CI_STEPS 里的每一行都对得上 alpha-ci 的一个真实步骤(改名即红)
B2 只删 CI_STEPS 登记行 红 ✅ 1 fail:alpha-ci 的每个代码步都登记在 alpha-check.sh 的 CI_STEPS 里
B3 只删 alpha-check.sh 第 [4/10] 步里那条命令(CI 步与 MIRRORED 登记都留着) 红 ❌ 15 pass / 0 fail —— 假绿

B3 是本 PR 在实测中发现的一个既有洞,而且是这张票能不能算闭合的关键:上面三条 CI_STEPS
断言比的全是步骤名,没有一条问过「alpha-check.sh 真的跑那条命令吗」。而本 portfolio 的合并路径
是「本地判绿 ⇒ --admin 合」——CI 那一格恰恰是被绕过的那道,所以假绿的是真正在用的那一半。
同形态前例:frontend-patch-roundtrip.test.ts 的第十四条(失效形态⑧)。

于是 local-gate-parity.test.ts 加第十六条:从 workflow 解析出全部
bun run --cwd <pkg> typecheck,逐条要求 alpha-check.sh 里真有那一行。补上之后重跑 B3:

PARITY_EXIT=1
(fail) … `#1134`:alpha-ci 的每条 typecheck 命令,alpha-check.sh 里必须真的有那一行
error: alpha-ci 跑了这些 typecheck,而 scripts/alpha-check.sh 里找不到那一行 —— 登记的 MIRRORED 是假的
- []
+ [ "bun run --cwd packages/opencode typecheck" ]

还原后 16 pass / 0 fail。

C. 解析自检自己也要能测出已知的坏

把 workflow 里四条 run: bun run --cwd … typecheck 改成块标量(正则不再匹配),
新断言必须说「测量作废」而不是空对空全绿:

PARITY_EXIT=1
error: alpha-ci 里没解析到 typecheck 命令 —— 本次测量作废,不是通过

登记簿用 bash scripts/assert-gate-files.sh --update 写回(15 → 16,未手改条数),
guarantee 列补上第十六条的理由与诚实边界。

本地闸门:真实输出

HEAD=1c8ffd0d3,fix/1134-opencode-typecheck-gate,base origin/alpha@8e30bdb77,工作树干净。
逐条 bash -c 'set -o pipefail; …',退出码单独取,没有 cmd | tail; echo $?。

# 闸门 exit 自报处理量
1 scripts/north-star-guard.sh 0 点名 2 个 alpha 自有豁免(见下),✓ zero upstream package edits
2 scripts/assert-frontend-patch-roundtrip.sh 0 pin 849c259 + 补丁覆盖 49 个文件,app/ui 双绿
3 scripts/assert-no-nul-bytes.py 0 7682 个文件零字面 NUL
4 typecheck alpha-contracts-consumer 0 0 条 error TS
5 typecheck ext 0 0 条 error TS
6 typecheck ui-mac 0 0 条 error TS
6b typecheck opencode(本 PR 新增) 0 0 条 error TS
7 check:vendor 0 22 + 3 + 3 lock↔bytes OK,40 条 provenance 已验
8 bun-test-floor.sh 15 packages/alpha-contracts-consumer 0 Ran 60 / 6 files,实测 60 ≥ 15
9 bun-test-floor.sh 100 packages/ext 0 Ran 157 / 14 files,实测 157 ≥ 100
10 bun-test-floor.sh 3000 packages/ui-mac src(带 known-fails 棘轮) 0 4494 pass / 0 fail / 311 files / 20760 expect
11 scripts/bun-test-app.sh 0 整包 624(CI=1 口径,地板 600)+ 两轴 5==5 + 5 文件 53 条逐条精确
12 scripts/assert-gate-files.sh 0 158 个闸门文件全部在位,条数与登记精确一致
13 scripts/assert-seed-assets.sh 0 seed/vendored resources present
14 check-doc-links.py docs/architecture/quality-gate-environments.md 0 8 relative link(s) resolve across 1 file(s) —— 1 == 我枚举的 1 个改动 md

north-star 实跑输出:

    · alpha 自有(住在上游包里,但 origin/dev 从来没有过这条路径)—— 不算上游改动:
      packages/opencode/test/tool/alpha-mcp-websearch-gate.test.ts
      packages/opencode/test/tool/alpha-websearch-failure.test.ts
✓ zero upstream package edits (baseline origin/alpha; ADR-033 收编白名单 + ADR-043 alpha 自有谓词除外)

与 base fail-set 的差:base(origin/alpha@8e30bdb77)上唯一的红是
bun run --cwd packages/opencode typecheck = exit 2 / 2 条(而它当时不在任何门里,所以
上表 1–14 在 base 上也全绿)。本 PR 后该项 exit 0。没有新增任何红,也没有任何 known-fails 新增。
本机实测该包 typecheck 4 秒,CI 增量成本可忽略。

诚实边界(不假装闭合)

  1. 新步骤跑的是整个 packages/opencode,不只是那 18 个 alpha 文件。 tsgo 的项目单位就是 tsconfig,
    而那个 tsconfig 是上游的、不能改 ⇒ 一次把上游源码带红的 fork-sync 会让这一步红在与 PR 无关的地方
    (#754 形态)。今天不成立(修完 exit 0 / 0 条),所以不预先造逃生门;真撞上时的处置写在 §3.11
    末尾(静态放行清单,或退回只跑 alpha 文件的独立 tsconfig),不是把这一步删掉。
  2. 第十六条只覆盖 typecheck 这一类命令。 它们形状统一、能从 workflow 无歧义解析。别的步骤本地或是
    脚本、或带 env 前缀、或登记成超集/降级档 —— 那些步骤的 MIRRORED/SUPERSET 至今仍只是一句登记。
    这一条已写进测试注释与 §3.11,没有顺手扩。
  3. alpha-check.sh 我没有整跑:并行 5 条 lane 期间它的第 [8/9] 步会真建探针 worktree 并写共享
    core.hooksPath。跑的是它第 [4/10] 步原样的那条命令链 + 绑住它的 local-gate-parity。
  4. pre-push 用了 --no-verify(理由同上:钩子 = alpha-check.sh)。上表 14 道门是逐条手跑的真实输出。

我主动没做的事

  • 没碰 alpha-725-policy-chokepoints.cases.ts 的任何断言,也没碰这个文件(它的 13 条已由 [REQ-131][CODE] #1129 在目录与工具执行咽喉执行同一 identity 三态策略(E1–E7 + sentinel) #1135 修掉)。
    变异实验 A 用的追加行当场还原,git status 全程可查。
  • 没碰 packages/opencode/tsconfig.json(上游文件),没扩 ADR-033 白名单,没扩 UPSTREAM_EXCLUDES。
  • 没把 ui-mac 的 1264 / 129 条一起收掉 —— 那是独立票,只留痕在 §6。
  • 没让 CI 开始跑整包 opencode 测试(票面 out of scope)。
  • 没动票面:没加 AC、没加准入条件。下面「建议」是建议,未写进 Issue。

建议(仅建议,未落票面)

  1. ui-mac 的 *.test.ts / *.cases.ts 进 typecheck —— 实测 1264 条(含 test)/ 129 条(只含 cases),
    先给 compilerOptions.types 加 bun 类型再逐条收。P2/P3 独立票。在那之前,ui-mac 的 .cases.ts
    写错类型不会有任何东西变红(A′ 实测)。
  2. 把第十六条从「typecheck 这一类」推广到全部 MIRRORED 步骤 —— B3 那个洞对其余登记成 MIRRORED
    的步骤同样开着,只是形状不统一、要一步步来。
  3. docs/architecture/quality-gate-environments.md:372 的小节号是 ### 3.1,应为 ### 3.10
    (与第 49 行的 ### 3.1 撞号)。我没顺手改;本 PR 新增的小节编成 §3.11 以避开。

jinjunnn and others added 2 commits August 26, 2026 03:02
AC1 —— `bun run --cwd packages/opencode typecheck` 在 `alpha@8e30bdb` 上是
exit 2 / 2 条(票面记的 15 条里,13 条 `TS2345` 已由 #1135 在收 ac#1129 时顺手
修掉,commit 正文明写「#1134 的 13 条 TS2345;零断言变动」)。剩下两条:

- `alpha-mcp-websearch-gate.test.ts:342` `TS2352` —— `denied as
  Exit.Failure<never, unknown>`。`fire` 的拒绝走的是 defect,错误通道类型是
  `never`,那个 cast 两个方向都不重叠。改用真的类型守卫 `Exit.isFailure` 收窄。
- `alpha-websearch-failure.test.ts:644` `TS2345` —— R4 变异工具的 `execute`
  返回 `Effect<…, WebSearchFailure>`,而 `Tool.Def["execute"]` 要 `E = never`。
  补 `.pipe(Effect.orDie)`:那是它注册得上的必要条件,不是松绑(闸在 `call()`
  的传输层,早于任何错误通道处理;orDie 只把 Fail 变 Die,而
  `Effect.catchCause` + `Cause.squash` 对两者一视同仁)。

两个文件的断言一条没动,运行时逐字不变:45 pass / 137 expect 与
7 pass / 23 expect,改前改后相同。

AC2 —— 口径裁决 + 接门,证据落在
docs/architecture/quality-gate-environments.md §3.11:

- 裁决「收进」。反方向要改 `packages/opencode/tsconfig.json`(上游文件,
  north-star 守卫下,动它要 ADR-033 owner 级收编),换来的还是**更少**的检查:
  住在这个上游包里的 18 个 alpha 自有文件(ADR-043 谓词,两条检索轴 17/18
  交叉验证,差集只有靠 marker 自报家门的 tool-display.ts)有 14 个在 test/ 下,
  跑在 bun 上被直接剥类型 ⇒ typecheck 是它们唯一的编译期检查。
- 「让 ui-mac 也收进」不是本票能装下的,而且这句话要有数字才算勘破:摘掉
  `*.test.ts` + `*.cases.ts` 两条 exclude = **1264 条**,只摘 `*.cases.ts` =
  **129 条**,首条都是 `Cannot find module 'bun:test'`(ui-mac 的
  `compilerOptions.types` 里没有 bun 类型)。登记进 §6「已知不修」。
- 接门:alpha-ci.yml 的 typecheck job 加一步(本机实测该包 typecheck 4 秒)+
  alpha-check.sh 第 [4/10] 步同一条命令 + CI_STEPS 登记 MIRRORED。防漂由既有的
  local-gate-parity.test.ts 双向判(删一侧即红)。

Fixes #1134

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QHDS2BFKuzm9uNH1svU2oj
本票接门之后跑变异实验,发现门只有一半:上面三条 CI_STEPS 断言比的全是**步骤名**
(CI 有没有这一步 / 对照表登没登记 / 档位合不合法),没有任何一条问过
「alpha-check.sh 真的跑那条命令吗」。

实测(2026-08-26,变异 B3):把第 [4/10] 步里 `bun run --cwd packages/opencode
typecheck` 那一行删掉、CI 那一步与 `MIRRORED` 登记都原样留着 —— local-gate-parity
**15 pass / 0 fail**,而本地那半道门已经不存在。本 portfolio 的合并路径是
「本地判绿 ⇒ `--admin` 合」,CI 那一格恰恰是被绕过的那一道 ⇒ 假绿的是**真正在用**
的那一半。同形态前例:frontend-patch-roundtrip.test.ts 第十四条(失效形态⑧)。

第十六条从 workflow 解析出全部 `bun run --cwd <pkg> typecheck`,逐条要求
alpha-check.sh 里真有那一行,并带解析自检(解析不到 ≥4 条即判「本次测量作废」
而不是绿)。诚实边界只覆盖 typecheck 这一类命令 —— 它们形状统一、能无歧义解析;
别的步骤本地或是脚本、或带 env 前缀、或登记成超集/降级档,不在本条覆盖内。

登记簿由 `bash scripts/assert-gate-files.sh --update` 写回(15 → 16),复核
158 个闸门文件全部在位、条数与登记精确一致。

Refs #1134

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QHDS2BFKuzm9uNH1svU2oj
@jinjunnn
jinjunnn merged commit ccbda7f into alpha Aug 26, 2026
5 of 6 checks passed
@jinjunnn
jinjunnn deleted the fix/1134-opencode-typecheck-gate branch August 26, 2026 07:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG][P2] packages/opencode typecheck 在 alpha 上 15 条红且不在任何门里 —— .cases.ts 的收录口径两个包不一致

1 participant