Repository navigation
[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
Activity
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 theclaude/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/reactcome last, in later tranches.Surface constraints vs in-flight work (binding):
- Excluded from this tranche:
packages/core,packages/react,packages/app-shell,packages/plugin-dashboard,packages/plugin-list— held by [P3] objectuiActionDef's[key: string]: anyabsorbs every retired and misspelled action key — removing a spec key produces no compile error #4046, [finding] DeclaredActionsBar keeps an inline copy of the three-way row binding now namedusePredicateRecordContext#4080, objectui: pivot bucket id 把 null 维度值编码成占位符 U+2205,与「值恰好等于该占位符」的行仍会撞 #4056/Analytics: the dataset widget's client-side dimension-label resolution is disabled for table / pivot / metric widgets, so a dotted dimension there still renders raw stored values #4263, and PR refactor(list): converge ListViewBlock onto the shared ElementDataSourceGate #4267 (in queue). They're also the large ones, so the small-first ruling and the conflict map agree. - The TEST_DEBT registry /
check-type-check-coverage.mjsDEBT list edits are the one shared file across tranche PRs — each PR removes only its own package's entry, so overlaps are trivial re-merges.
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.jsonmust actually be run by the package'stype-checkscript (the #3009 third failure mode).
Generated by Claude Code
- Excluded from this tranche:
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。复核要点:
- 裁决纪律全程成立:每包独立 PR、判别力证明在 PR 正文、TEST_DEBT 只减不增、
tsconfig.test.json实际接入type-check。 - 批次最佳回报是 plugin-chatbot:一个"绿灯但测错了"的用例(
planExtendLabel传错位置,断言恰好被默认文案满足)——判别证明保持断言强度不变、只回退放置位,红得恰到好处。plugin-gantt 删掉的手抄常数副本(已漂移出year条目)正是 build(types): type-check this package's tests, so the spec-derivation guards actually run (objectstack#4074) #3009 的分叉原型。 - auth 的
as unknown as AuthClient跟随包内既有惯例;permissions 的permissions: []判断准确(该字段无读者,连带产出 findingRoleDefinition.permissionsis required and read by nothing — every consumer uses onlyname/inherits#4288,留 finding 池待分诊)。 - plugin-grid 按"公共类型存疑即停"规则停手立卡 ObjectGridProps declares 12 props while ObjectGrid reads 12 more through
(rest as any)— the external-pagination contract is undeclared #4277 — 处理正确,裁决另行发出(见该卡)。
记录三项后续:
- 注册表漂移:plugin-grid 声明 2 实测 4 —— 后续批次一律重测不信表(已写入批次2 派单)。
- 模板扩展两条(
src/**/*.d.ts入 include;测试 project 单独抬lib)收入批次2 派单,作为参考模板的补充。 - 毕业包的窄 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
- 裁决纪律全程成立:每包独立 PR、判别力证明在 PR 正文、TEST_DEBT 只减不增、
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 派单。
两条行动项采纳:
- 批次3 前先全量重测(登记表漂移已在 i18n 上到 8 倍,任何条目都不可信)——已作为批次3 的第一交付物派发。
- 三 PR 的注册表行竞争与批次1 同型:先落者会让后两件冲突且 GitHub 会静默解除 auto-merge——本席按批次1 的同法处理(worktree 合并解决 + 重挂)。
#4320(ImportWizardProps.dataSource 声明必填 vs 实现全防御)留 finding 池。
Generated by Claude Code
- ObjectGridProps declares 12 props while ObjectGrid reads 12 more through
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 全绿;其正文自述已解注册表冲突并提醒重挂——已挂)。
复核要点:
- 重测纪律执行:plugin-detail 登记 5 实测 11(2.2×);plugin-list 登记 6 实测 23 原始(14 config / 9 code)——config/code 分层避免了 2.5× 的误报。
- plugin-detail 修正两处公共形状(gate 包装器的
objectName选填化;ObjectDefLike.fieldGroups从 spec 派生取代已漂移的手抄)——判别证明 B 用报错引用 spec 成员列表证明非手抄。 - plugin-list 的
authored()决策(legacy 拼写夹具是 fold 的唯一活覆盖,证明 B 实测)与 The legacylist-viewvocabularynormalizeListViewSchemaexists to accept has no declared type — every fixture exercising the fold must cast #4337(normalizeListViewSchema 输入类型缺失 + filter 类型真分歧)、@object-ui/fieldsdeep subpaths resolve only through the repo vitest alias — itsexportsmap publishes none of them #4325(fields 子路径打包缺口)两张 finding。 - 损失记录:批次3 的全量重测表(deliverable 1)随 agent 死亡丢失,两包的数字已在 PR 正文;其余包的重测随批次4 派发时重做(SIGPIPE 纪律不变)。
批次4 待批次3 两 PR 落地 + 重测后定规模;components 面候 #4321 相关工作,core/react/app-shell 维持最后。
Generated by Claude Code
- added a commit that references this issue
on Aug 11, 2026 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 写文件 + 探针红/复原绿)。复核要点:
- 重测继续证明登记表不可信(react 27→43 实测 1.6×;components 的 NOTE 连归因都错——26 个 implicit-any 在包源码而非测试回调,随 finding(components): noImplicitAny is off package-wide, and 26 renderer signatures depend on it #4353 立卡);test(components): type-check the whole test tree, and retire the narrow typetests project (#4040) #4355 顺带把 core 72→56、app-shell 53→62 的登记数修正为实测值并加"两个方向都错过"警示注释。
- react 的实质发现:SpecBridge.test.ts authored 了一整套 spec 没有的 ListView 方言(rowHeight/link/action/sort/filter/grouping 六处),bridgeListView 原样转发使所有断言靠回环自证——夹具改拼规范形,四个死 density 分支不顺手删(宿主 legacy 拼写的行为变化,finding(react): bridgeListView's mapDensity carries four rowHeight spellings no spec-valid list view can hold #4352 立卡候 grep 实测)。处理正确。
- components 的一圈学费:CI 抓到本地队列未及的两错,其中
snapshot-critical.test.tsx以包名自导入穿过 exports 读 dist——与 turbo 只等依赖 ^build 的契约冲突,paths 映射回 src 与仓根 vitest alias 对齐。修法正确且记录了机理。 - 锁竞争如实报告(单次获取打包全部重验证 + 提前推 draft 让 CI 并行——正是这个策略抓到了自导入)。
批次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
CLAIM — tranche 5 (final) — PM seat
session_017Qqyix2QcnpUC9XeYVDzx3, branchclaude/issue-4040-tranche5-core-app-shell.Gate satisfied: PR #4366 (batch-3 sweep, #4328) merged at 22:19Z freeing the
core/reactsurfaces, and no in-flight task holdsapp-shell. Scope: graduate the last two debt packages — core (56 test-compile errors) and app-shell (62) — to full test type-check coverage, removing theirTEST_DEBTregistry lines inscripts/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+ typesnode/vite/client/@testing-library/jest-dom. TheActionProviderProps.handlersrestatement 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
- added a commit that references this issue
on Aug 11, 2026 CLAIM (closing tranche) — session
session_017Qqyix2QcnpUC9XeYVDzx3, branchclaude/issue-4040-dashboard-test-typecheck. The program stands at 39/40;@object-ui/plugin-dashboardis the last TEST_DEBT entry.Ruling (delegated authority; open veto window): close the program.
- Re-measure the debt first: the ledger says 6 errors; A dashboard global filter sourced from
optionsFromcommits 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 whatevertsc -p tsconfig.test.jsonsays today — measure, then fix every error inpackages/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). - Wire the project:
tsconfig.test.jsonper the 39 sibling packages' convention (measure one recently-wired sibling and mirror),type-checkscript chaining it, and the coverage gate's TEST_DEBT row DELETED — the ratchet's debt-zero consequence:scripts/check-type-check-coverage.mjsmust 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). ⚠️ 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 atsconfig.typetests.json— that population is closed. The test project only.
Grading: scripts + tests + tsconfig — expect no changeset owed (
check-changeset-presencedecides; empty frontmatter if demanded).Surface (mutual exclusion):
packages/plugin-dashboard/tsconfig.test.json(new) + itspackage.jsonscript + test-file type fixes +scripts/check-type-check-coverage.mjsTEST_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
- Re-measure the debt first: the ledger says 6 errors; A dashboard global filter sourced from
程序收官 — 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
2 remaining items
- added 15 commits that reference this issue
on Aug 17, 2026
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 — thesatisfieschecks meant to stop a vocabulary re-fork — were compiled by nothing: package tsconfigs exclude test files (correctly, they are build configs), and no othertscinvocation reads them. Reverting a derived alias produced zero errors; after objectstack-ai/objectstack#3009,TS1360on 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.tstype-imported eight names whose absence its own sibling guard asserts, green the whole time;p1-spec-alignment.test.tspasses 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,libtoo low TS2550, missingtypes: ["node"], cross-package dist TS2307) vs code-tier (real).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 intsc --noEmitand 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'ssharing { 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.mjspattern (already the repo's best governance: declared, reasoned, shrink-only, objectstack-ai/objectstack#2911): every package either type-checks its tests (atsconfig.test.jsonchained fromtype-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
satisfieschecks, 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.jsonshould standardize:noEmit,composite: false,paths: {}(kills the TS6059×100 source-leak noise),libmatching what tests actually use,types: ["node"]where tests touch fs. objectui#3009'spackages/types/tsconfig.test.jsonis 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.