Skip to content

[finding] PINS_CHECKED detects only @ts-expect-error, so other compile-time pin idioms in a hidden test layer are invisible to it — measured on a live example #8113

Description

@os-zhuang

Restart-when: any PR reworks the PINS_CHECKED detector half of scripts/check-type-check-coverage.mjs (the PIN_DIRECTIVE recogniser), or a second non-@ts-expect-error pin is measured proving phantom in a landed PR

Found while implementing #7809 (PR #8106). Out of scope there, filed unassigned.

What

scripts/check-type-check-coverage.mjs carries a PINS_CHECKED invariant with a good rationale:

A @ts-expect-error outside every invoked tsc program is a retirement guard that cannot fail — deleting the directive changes nothing, which is the definition of a phantom check.

It detects that condition by looking for @ts-expect-error directives (pinFiles). That is one idiom. A compile-time pin written any other way is not seen by it.

The measured example

While writing a vocabulary pin for #7809 I first expressed it as a mutual-assignability pair — the ordinary way to assert "these two types are the same" without a directive:

const _forward: Declared = undefined as unknown as EngineOp;   // red if EngineOp widens
const _reverse: EngineOp = undefined as unknown as Declared;   // red if EngineOp narrows

in packages/objectql/src/*.test.ts. packages/objectql/tsconfig.json excludes the test layer and its typecheck script is tsc --noEmit against that config, so the mandatory ablation (widen the union by one member, predict red) passed both pnpm typecheck and vitest. The pin asserted nothing in either runner a developer runs locally, and PINS_CHECKED had nothing to say about it because it carries no @ts-expect-error.

What this is NOT

Worth stating precisely, because the first version of this analysis was wrong and the correction matters:

These files are not unchecked. check:type-check-coverage's TEST_DEBT ledger lifts the exclusion, compiles the test layer, and ratchets the raw error count (@objectstack/objectql: 355). Verified from both sides on PR #8106 — three type errors in a new test file made it report 358 and go red; fixing them returned it to exactly 355. So a widened union would have been caught in CI.

The gap is therefore about signal, not coverage:

  • what fires is TEST_DEBT +1 on a package with a 355-error pile, not "the dispatch vocabulary changed";
  • it fires only in CI, not in the pnpm typecheck the author just ran locally and watched pass;
  • PINS_CHECKED, the mechanism whose whole job is to say "this pin is a phantom", stays silent — so the author gets no warning that the idiom they chose is not load-bearing where they put it.

Why it is worth recording

The repo already decided this class matters (#5286, #5449, #4642, the PINS_CHECKED invariant, and scripts/check-test-typecheck.mts, which packages/spec and packages/client have onboarded to). This is a narrow hole in the detector, not a new class: an author who avoids @ts-expect-error — as anyone asserting type equality rather than expected-failure naturally does — falls outside it.

Non-obvious, and the reason this is filed rather than fixed in passing: the naive repair (flag any type-only const in a hidden test file) would be noisy and wrong. Something narrower is probably right — e.g. treat a package's hidden test layer as a place where compile-time pins are reported as unenforced-by-the-local-runner, or extend PINS_CHECKED's notion of a pin, or simply document at the ledger entry that pins in these packages should be expressed at runtime. Which of those is correct is a judgement call, hence a finding rather than a patch.

Ancillary datum

24 of 71 package tsconfigs exclude the test layer; a keyword scan finds type-level assertion idioms (@ts-expect-error, satisfies, assignability consts) in test files across ~12 of those packages. Most are presumably covered as counts by TEST_DEBT exactly as objectql is — that number is offered as scope context for whoever picks this up, not as a claim that any of them is broken.


Generated by Claude Code


Generated by Claude Code

Activity

  1. added theissue type on Aug 12, 2026
  2. hotlong commented on Aug 12, 2026

    @hotlong
    Contributor

    Triage: lands in domain:devx; rationale: the detector is scripts/check-type-check-coverage.mjs (gate-family script, general dev tooling, not spec-contract-centred). Held as finding — the card itself argues the naive repair is wrong and the right detector shape is a judgement call. Type: Task.


    Generated by Claude Code

  3. hotlong commented on Aug 12, 2026

    @hotlong
    Contributor

    Finding-grading round: hold confirmed, now with the concrete re-grade trigger the earlier triage comment lacked: promote when (a) a second measured instance of a non-@ts-expect-error pin proving phantom in a landed PR appears, or (b) any PR reworks the PINS_CHECKED detector half of check-type-check-coverage.mjs (note: #7888's --lower is ledger-data only and does not fire this). Premise re-verified exact: PIN_DIRECTIVE at :297 is the only pin recogniser.


    Generated by Claude Code

  4. hotlong commented on Aug 13, 2026

    @hotlong
    Contributor

    State-machine migration, maintainer-authorized (2026-08-13, live triage session: 「我授权你先迁标签」; semantics on #8449): this card is a graded hold — verdict and re-grade triggers are in the grading comments above — so finding → pm:on-hold. Verdict, triggers, domain unchanged.


    Generated by Claude Code

  5. objectstack-fleet commented on Sep 23, 2026

    @objectstack-fleet
    Contributor

    关 not_planned —— 维护者逐张复核 on-hold 卡时同意关闭;纯开发工具,对产品没有影响

    分诊席(session_01Tw7jnJinGHvoGSi8aFkhPJ),2026-09-23T08:16Z。维护者 2026-09-23 在分诊会话里逐张复核 pm:on-hold 卡,对第三组的回复原文:「第三组 同意」。 本卡在那一组里的建议是关闭。

    呈给维护者的事实

    • 缺口是真的:scripts/check-type-check-coverage.mjs 的 PINS_CHECKED 只认 @ts-expect-error 这一种写法。在一个测试层不参与本地类型检查的包里,用别的写法(比如互相赋值来断言类型相等)写的编译期断言,本地跑 pnpm typecheck 会静默通过。CI 仍然能抓到,只是表现为 TEST_DEBT 计数 +1,而不是一句「这个断言是假的」。
    • 这是开发工具,对产品零影响。挂起 6 周,没有出现第二个实测到的「假断言」,也没有人改过这个检测器。
    • SKILL.md:374:产品仓的 P0/P1 开着时,没有解锁对象的 p2/p3 tooling 卡一律关闭。

    重开条件

    又一个不是 @ts-expect-error 写法的编译期断言,在已合并的 PR 里被实测证明是假的;或者有 PR 重写 PIN_DIRECTIVE 这个识别器。按正文里的三个方向接着做。

    关闭理由:not_planned,同时摘掉 pm:on-hold。


    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

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions