Skip to content

[P2] objectui: every package type-checks its tests or declares why not — and guards must prove they can fail (measured: 355 errors across 19 packages) #4040

Description

@os-zhuang

Migrated from objectstack-ai/objectstack#4118 under the file-at-destination ruling (objectstack-ai/objectstack#7167, maintainer 2026-08-10). Originally filed 2026-07-30T10:24:01Z. The full prior thread — including triage rulings and hold/restart conditions — remains on the source issue and MUST be read before acting on this card.


Generalizes objectui#3009 from one package to the repo, with the scope measured rather than estimated. Companion to objectstack-ai/objectstack#4115 (which prevents new forks; this one makes existing guards actually run).

Why

objectui#3009 found that packages/types' spec-derivation guards — the satisfies checks meant to stop a vocabulary re-fork — were compiled by nothing: package tsconfigs exclude test files (correctly, they are build configs), and no other tsc invocation reads them. Reverting a derived alias produced zero errors; after objectstack-ai/objectstack#3009, TS1360 on the exact line.

Tests are where agents encode their understanding of a contract. Unchecked tests let wrong understanding accumulate silently and then read as evidence: p2-spec-exports.test.ts type-imported eight names whose absence its own sibling guard asserts, green the whole time; p1-spec-alignment.test.ts passes six "should accept ⟨shape⟩" claims the type rejects. An agent citing a green suite as proof of a contract is the objectstack-ai/objectstack#2901 backwards-premise incident again.

Measured scope (2026-07-30, objectui main @ a045a32)

Method: per package whose tsconfig excludes test globs, a temp tsconfig lifting only the test exclusion (emit off), tsc, errors classified config-tier (fixable in the test tsconfig: paths/rootDir leakage TS6059, lib too low TS2550, missing types: ["node"], cross-package dist TS2307) vs code-tier (real).

package test files code-tier errors dominant codes
core 54 80 TS2741×32, TS2322×17, TS2304×10
react 28 76 TS2339×49, TS2769×9
sdui-parser 3 50 TS2304×47 (likely env globals — verify)
app-shell 240 37 TS2339×8, TS2345×6, TS2739×6
plugin-list 12 20 TS2739×6, TS2740×6, TS2741×5
plugin-form / i18n / permissions / auth 25/16/3/8 15/13/13/10 mixed
plugin-dashboard, components, plugin-kanban, plugin-detail, plugin-calendar, plugin-gantt, plugin-map, layout, plugin-chatbot, plugin-grid — 8…2 each mixed
mobile, plugin-designer, plugin-markdown, plugin-report, plugin-timeline, plugin-tree, react-runtime, runner 17 0 already clean

Totals: 27 packages, 612 test files, 355 code-tier errors in 19 packages; 8 packages are free wins. (5 further packages — fields, cli, data-objectstack, plugin-charts, plugin-editor — already include tests in tsc --noEmit and are covered today.)

The two dominant codes are not random: TS2741/TS2739 ("missing required properties") is the input-vs-output confusion objectstack-ai/objectstack#3009 fixed in p2-spec-exports (authoring fixtures typed as parsed output whose .default() fields are required), and TS2339/TS2353 ("property does not exist") is the implementation-wider-than-type dialect problem (objectstack-ai/objectstack#4074's sharing { visibility, enabled } shape). The per-package cleanup will mostly be those two playbooks applied repeatedly.

The ask

1. Coverage ratchet. Extend the check-type-check-coverage.mjs pattern (already the repo's best governance: declared, reasoned, shrink-only, objectstack-ai/objectstack#2911): every package either type-checks its tests (a tsconfig.test.json chained from type-check, per objectui#3009's template) or carries a DEBT entry with the measured error count and an issue link. Start by converting the 8 clean packages (pure plumbing, zero fixes), then burn down the table above.

2. Discrimination proof for guards. A guard PR shows the guard failing against the pre-fix code (one pasted run in the PR description). This is what exposed the inert satisfies checks, an already-vacuous "prefer canonical over alias" test (objectui#2990), and the tautological "should accept" suite — three cases in one day of applying it. Cheap for agents to execute mechanically; catches the exact failure mode ("test passes for the wrong reason") that review reliably misses.

3. Config-tier template. The per-package tsconfig.test.json should standardize: noEmit, composite: false, paths: {} (kills the TS6059×100 source-leak noise), lib matching what tests actually use, types: ["node"] where tests touch fs. objectui#3009's packages/types/tsconfig.test.json is the reference, comment included.

Same meta-rule as objectstack-ai/objectstack#4115: this lands as CI, not prose.

Refs objectui#3009, objectstack-ai/objectstack#4115, objectstack-ai/objectstack#4074, objectui#2911, objectstack-ai/objectstack#2901.

Activity

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

    @yinlianghui
    Collaborator

    CLAIM — session session_017Qqyix2QcnpUC9XeYVDzx3 (objectui whole-repo PM seat), dispatching now.

    Branch prefix: claude/issue-4040-<pkg> (per-package PRs per the objectstack#4118 ruling ⇒ per-package branches, all under the claude/issue-4040- prefix). Worktree-first, one per branch.

    Scope of this dispatch (tranche 1): burn down the smallest TEST_DEBT packages first, per the standing ruling on objectstack#4118 (2026-08-03) — mechanism already landed in #3032, this is execution only. core/react come last, in later tranches.

    Surface constraints vs in-flight work (binding):

    Discipline carried from the ruling: per-package independent PRs; any guard-affecting PR pastes a pre-fix red run (discrimination proof); TEST_DEBT shrink-only; tsconfig.test.json must actually be run by the package's type-check script (the #3009 third failure mode).


    Generated by Claude Code

  3. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    Collaborator

    ACCEPT tranche 1 — PM review of record (session session_017Qqyix2QcnpUC9XeYVDzx3)。5 包 5 PR(#4270 plugin-map / #4274 auth / #4276 plugin-chatbot / #4283 plugin-gantt / #4287 permissions)全部验收,已 ready + auto-merge。

    复核要点:

    记录三项后续:

    1. 注册表漂移:plugin-grid 声明 2 实测 4 —— 后续批次一律重测不信表(已写入批次2 派单)。
    2. 模板扩展两条(src/**/*.d.ts 入 include;测试 project 单独抬 lib)收入批次2 派单,作为参考模板的补充。
    3. 毕业包的窄 typetests project 冗余(auth / plugin-chatbot 各多编译一个文件):dev 不删治理构件的判断正确,单独立卡处理(governance 收尾,一个 PR 清十处中已毕业者)。

    批次2 即将派发:plugin-grid 收尾(按 #4277 裁决)+ plugin-form(12)+ i18n(13);plugin-detail(5)因 #4232 在飞继续排除,待其交付后入批次3;core/react/app-shell 维持最后。


    Generated by Claude Code

  4. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    Collaborator

    ACCEPT tranche 2 — PM review of record (session session_017Qqyix2QcnpUC9XeYVDzx3)。三包三 PR(#4297 plugin-grid / #4309 plugin-form / #4317 i18n)全部验收,ready + auto-merge。

    复核要点:

    • ObjectGridProps declares 12 props while ObjectGrid reads 12 more through (rest as any) — the external-pagination contract is undeclared #4277 裁决执行忠实:ObjectGridExternalPaginationProps = Partial<Pick<DataTableSchema, …11 键>>——零手抄,探针 3 证明报错原文引用 packages/types(连参数名都是);Partial 是唯一形状差且理由成立(模式整体 opt-in);onColumnStateChange 的显式声明附了扎实的"无对应物"论证(合并后的持久化布局 vs 逐事件回调,不同载荷不同时刻);renderer 的 any 按令未动;两处范围边界(不回溯派生五个既有成员、dep 数组顺手修正)如实声明。ObjectGridProps declares 12 props while ObjectGrid reads 12 more through (rest as any) — the external-pagination contract is undeclared #4277 随 PR 关闭。
    • plugin-form 的 13 错中 10 个是同一个碰撞(describe.each 的 JSX 联合类型按交集解析 → formType: never),修法(每文件一次命名参数化)与 any 的显式辩护 + 字面量联合补偿 pin 都对;案例矩阵保全有断言不是假设。
    • i18n 实测 103 vs 登记 13(8 倍):90 个 TS7053 是 Object.keys 抹掉键类型——平价套件在比较编译器从未确认存在的语言包;修复走派生(同目录三个兄弟文件的既有惯例)。
    • 测量陷阱记录极有价值:tee | head 管道让 tsc 吃 SIGPIPE 中途截断,42 行恰好落在可信边界上,产出貌似完整的假 21——靠"修后比修前多"才抓住。采纳为纪律:所有测量写文件后计数,绝不 head 编译器流。已写入批次3 派单。

    两条行动项采纳:

    1. 批次3 前先全量重测(登记表漂移已在 i18n 上到 8 倍,任何条目都不可信)——已作为批次3 的第一交付物派发。
    2. 三 PR 的注册表行竞争与批次1 同型:先落者会让后两件冲突且 GitHub 会静默解除 auto-merge——本席按批次1 的同法处理(worktree 合并解决 + 重挂)。

    #4320(ImportWizardProps.dataSource 声明必填 vs 实现全防御)留 finding 池。


    Generated by Claude Code

  5. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    Collaborator

    ACCEPT tranche 3(两包)+ 重启记录 — PM review of record (session session_017Qqyix2QcnpUC9XeYVDzx3)。

    宿主重启(本日第四次,~15:55Z)在批次3 agent 发出最终报告前将其终止;worktree 盘点显示工作已实质完成——PR #4326(plugin-detail)与 #4338(plugin-list)均已开出且正文完整(测量表、判别证明、反向验证、本地门禁记录俱全),以 PR 正文替代 JSON 报告完成 step-7 复核,验收通过,已 ready + auto-merge(#4326 CI 已核 18+2 全绿;其正文自述已解注册表冲突并提醒重挂——已挂)。

    复核要点:

    批次4 待批次3 两 PR 落地 + 重测后定规模;components 面候 #4321 相关工作,core/react/app-shell 维持最后。


    Generated by Claude Code

  6. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    Collaborator

    ACCEPT tranche 4 — PM review of record (session session_017Qqyix2QcnpUC9XeYVDzx3)。PR #4351(react,43→0)与 #4355(components,34→0)验收通过,ready + auto-merge;两包窄 typetests project 按 #4291 棘轮同 PR 退役,覆盖与判别证明齐全(listFiles 写文件 + 探针红/复原绿)。

    复核要点:

    批次5(core 56 + app-shell 62)候:双 PR 落地 + #4289 交付让出 sharedUserFeeds 测试面;app-shell 的 config-tier 药方(lib ES2022 + 三 types)已在重测表内。注册表双撞预案已知,后落者解决 + 重挂。三 finding(#4352/#4353/#4354)留池,ActionProviderProps.handlers 的同族窄化记入批次5 派单。


    Generated by Claude Code

  7. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    Collaborator

    CLAIM — tranche 5 (final) — PM seat session_017Qqyix2QcnpUC9XeYVDzx3, branch claude/issue-4040-tranche5-core-app-shell.

    Gate satisfied: PR #4366 (batch-3 sweep, #4328) merged at 22:19Z freeing the core/react surfaces, and no in-flight task holds app-shell. Scope: graduate the last two debt packages — core (56 test-compile errors) and app-shell (62) — to full test type-check coverage, removing their TEST_DEBT registry lines in scripts/check-type-check-coverage.mjs. Per #4345's ratchet, both packages' narrow typetests projects must retire in the same PR. Known recipe for app-shell's config tier: lib: ES2022 + types node/vite/client/@testing-library/jest-dom. The ActionProviderProps.handlers restatement fix rides along where the compile errors surface it. plugin-dashboard's residual 6 errors are NOT in this tranche (surface just taken by the #4330 queue candidate's family; registry line stays).

    This closes out the #4040 program to 39/40 packages; remainder tracked on the registry.


    Generated by Claude Code

  8. yinlianghui commented on Aug 13, 2026

    @yinlianghui
    Collaborator

    CLAIM (closing tranche) — session session_017Qqyix2QcnpUC9XeYVDzx3, branch claude/issue-4040-dashboard-test-typecheck. The program stands at 39/40; @object-ui/plugin-dashboard is the last TEST_DEBT entry.

    Ruling (delegated authority; open veto window): close the program.

    1. Re-measure the debt first: the ledger says 6 errors; A dashboard global filter sourced from optionsFrom commits the display LABEL as its value — every widget it drives silently returns zero rows #4465's dev measured 14 under the standard temp-project template (and noted the gate's own header says counts are re-measured, never inherited). The real number is whatever tsc -p tsconfig.test.json says today — measure, then fix every error in packages/plugin-dashboard's tests (type fixes only; if a type error reveals a REAL test defect — an assertion that cannot do what it claims — STOP on that spec and report it, the Type-check coverage gap: e2e/ is outside every tsconfig, so 30 Playwright specs are compiled by nothing #4471 discipline).
    2. Wire the project: tsconfig.test.json per the 39 sibling packages' convention (measure one recently-wired sibling and mirror), type-check script chaining it, and the coverage gate's TEST_DEBT row DELETED — the ratchet's debt-zero consequence: scripts/check-type-check-coverage.mjs must report 40/40 with zero debt entries, and per Retire the narrow per-file tsconfig.typetests.json projects in packages that have graduated out of TEST_DEBT #4291/check-type-check-coverage.mjs reports green when a "type-check" script chains a tsconfig project that does not exist #4347 the gate then structurally forbids new debt rows. Red-first: the gate pre-fix reports the debt row; post-fix 40/40; the new project goes red on a planted type error (prove the wiring is load-bearing, the objectui: 39 个普通 button 元素缺 type="button"(objectui#3344 家族的总体面);已一次一个修了三轮,建议用 lint 规则收口 —— observation #4045 direction).
    3. ⚠️ The Retire the narrow per-file tsconfig.typetests.json projects in packages that have graduated out of TEST_DEBT #4291 typetests ratchet: do NOT create a tsconfig.typetests.json — that population is closed. The test project only.

    Grading: scripts + tests + tsconfig — expect no changeset owed (check-changeset-presence decides; empty frontmatter if demanded).

    Surface (mutual exclusion): packages/plugin-dashboard/tsconfig.test.json (new) + its package.json script + test-file type fixes + scripts/check-type-check-coverage.mjs TEST_DEBT row + its self-test. ⛔ NOT: plugin-dashboard SOURCE files (type fixes live in tests; if a fix genuinely requires a source change, report first), DatasetWidget/DashboardFilterBar behavior, content/docs/releases/. Disjoint from in-flight #4213/#4513/#4518/#4490.


    Generated by Claude Code


    Generated by Claude Code

  9. yinlianghui commented on Aug 13, 2026

    @yinlianghui
    Collaborator

    程序收官 — CLOSING (session_017Qqyix2QcnpUC9XeYVDzx3)

    PR #4530(plugin-dashboard 终批)已并入 main(1ab6758fb)。门禁自己的口径(以其终端输出为准,弃用程序卡的 40/40 速记):

    ✅  type-check coverage: 44/46 via `type-check`, 1 via their own build, 0 known-broken (0 errors outstanding), 1 not compiled.
    ✅  test type-check coverage: 41/41 packages compile their tests, 0 declared debt (0 errors outstanding), 0 with a narrow type-assertion project.
    

    TEST_DEBT 表为空。 立卡时实测的 355 个 code-tier 错误(19 个包)已全部清偿,经由分批 tranche 落地(auth / components / i18n / permissions / react / core+app-shell / plugin-chatbot / plugin-detail / plugin-form / plugin-gantt / plugin-grid / plugin-list / plugin-map / plugin-dashboard 终批)。每批都履行了两项证明义务:planted error 证明新挂链的 test project 承重(且方向经查:build config 对同一错误静默通过,正是修复前的状态),ratchet 恢复已删行即 exit 1。

    空表语义已在终批写死:门禁源码实测表明空表不封闭族群——新 TEST_DEBT 行仍可作为显式、可评审的申报出现,挡住的是静默缺口(unread+undeclared 违 5c,毕业未删行违 6,挂链缺失/漏 emit 违 5a/5b)。此推理写入表注释并以 4 个自测例 pin 住。

    上游源头卡 objectstack#4118 经核实已于 2026-08-10 由原作者关闭,无需动作。终批另产出两个 finding 待分诊:#4528(forwardRef 索引签名抹 prop,#4422 的两个幸存者)、#4529(plugin-charts 未发布子路径,已派发)。


    Generated by Claude Code


    Generated by Claude Code

  10. 2 remaining items

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