Skip to content

feat(lint): reject a source line address inside a test name - #8480

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-8047-test-name-line-citation
Sep 8, 2026
Merged

feat(lint): reject a source line address inside a test name#8480
baozhoutao merged 2 commits into
mainfrom
claude/issue-8047-test-name-line-citation

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #8047

Adds object-ui/no-line-address-in-test-name to the local ESLint plugin, wires it at error over test files, and converts the whole live population so it lints clean on the day it lands — the same shape as every other object-ui/* ratchet in eslint.config.js.

A File.tsx:123 address inside a test name is read by nothing: it is not an assertion, no gate parses it, and the cited file is never opened. So it cannot fail. It rots the first time a line is inserted above what it cites, and the rot is invisible until a reader believes it. objectui#7853 ruled the class — cite the assertion by CONTENT, not by line address — and five per-instance repairs followed it without closing it.

⚠️ Nothing is currently false. This PR does not repair a live falsehood; it closes a class that is waiting for its next insertion. That is why objectui#8047 is priority:p3.


1. The population, re-derived on origin/main at 868e82501

⛔ The card's 3 / 5 more / 6 is not today's figure and was not carried forward. Both instruments were re-run over the 2454 test files under packages/, comments masked by the tree's own scripts/js-comment-mask.mjs:

instrument raw hits files
anchored on the it( line 6 2
name-agnostic — a quoted address on any non-comment line 45 7
this rule — an address in a string that REACHES a test name 37 3

Three readings the card did not have, and each one changes something:

  1. The narrow instrument also over-reports. One of its 6 is a false positive: a rationale string in page-header-authorable-keys.test.tsx containing the words "reads and draws it (PageHeader.tsx:123", where it ( merely looks like a declaration. So the it(-anchored sweep is wrong in both directions, not only short.
  2. The gap is not all defect. 8 of the 45 are legitimate by the boundary in section 3 — assertion messages and data a test asserts on. A rule taking the name-agnostic scan wholesale would be about 22% false positive, and its remedy would be deleting provenance a human reads at the point of failure.
  3. The class recurred again after the card was filed, in a new shape. The two instances objectui#7913 and objectui#8045 were about are both repaired and gone. Today's 37 are: 5 literal it( names in packages/plugin-charts/src/ObjectChart.absentCategoryAxisRefusal-8168.test.tsx, and 32 addresses reaching names through it.each(CORPUS)('adopts $src …') in two app-shell metadata-admin inspector tests — a different interpolation shape from the card's worked example, and one the it(-anchored instrument sees zero of.

⇒ The card's central claim survives re-derivation and gets stronger: the narrow instrument misses 32 of 37.

2. Design question 1 — interpolated names. Settled: strings that REACH a title, decided on the AST

The rejected alternative was to judge genuinely resolved names by collecting them with the runtime (vitest list --json). Disqualified twice:

  • In principle — collection executes every test module's top level, so a syntax-only citation check would acquire the power to fail on an unrelated runtime error, a missing DOM global, or a slow transform. A check on the text of a name must not depend on the suite booting.
  • In cost, measured in this containervitest list --json over one plugin-charts file took 11.9 s; over one app-shell dom file it had emitted nothing after nine minutes. There are 2454 test files.

So the rule reads the AST, in three legs:

leg what it reads why it exists
1 the title argument itself — every quote flavour, every it.skip / it.each / describe chain the literal shape
2 the strings of a statically-resolved each case table, when the title interpolates the blind spot the card is about
3 when the table is not statically legible and the title names properties: the whole file, reported at the table "the rule could not tell" must never be spelled the same way as "there is nothing here"

Table resolution deliberately over-approximates: CORPUS.filter(fn) resolves to all of CORPUS, because a filter can only drop rows and over-reporting is loud where under-reporting is silent. That is not hypothetical — ConditionBuilder.subjectVocabulary.test.tsx has exactly that shape, and it is one of the 32.

The worked example is caught. it.each(roundTrippable)('adopts $src as structured rows', …) where roundTrippable = CORPUS.filter((c) => !c.parens) and CORPUS is a 17-row table of { src, cel } — the addresses never appear on an it( line. Reported: 16 per file, 32 total. ⭐ And src there is read by nothing else: both call sites destructure ({ cel }) only, so the field exists solely to be spliced into a name.

3. Design question 2 — where a line address is legitimate. The boundary, and its cost

Drawn in the rule's own header, with the reason, and it is drawn on the title's own reach rather than on a keyword list. Three carve-outs, each with an instance measured in this tree today:

  1. Comments. A human reads them beside the code they annotate, and the next reader of that code corrects a wrong one. This rule never reads a comment — nothing in it inspects getAllComments.
  2. Assertion and failure messages. Read by a human at the point of failure, where the assertion that failed is the context. In tree: readme-app-shell-example.test.ts:231 and :255, guide-layout-sidebar-nav-doc.test.ts:490, and the four producer: fields in gridNonAuthorKeys.test.tsx that its line 229 splices into a failure message.
  3. Data the test asserts ON. page-header-authorable-keys.test.tsx's RENDERER_OWN_DECLARED rationale carries an address and is read — line 227 asserts on that very string. Something checks it, so it is not the unreadable class.

The mechanism that makes carve-out 2 automatic: for it.each(ROWS)('the spec refuses DOLLAR-key as an unrecognized key') only the key field of a row can arrive in the name, so a sibling producer: field is data. A positional title (%s, DOLLAR-0) or a template title cannot say which field arrives, so those read the whole row.

⚠️ The residual gap is stated, not papered over. Leg 3 fires only for a named title over an unresolvable table. A positional title over an opaque table — it.each(covered)('%s declares …') in apps/console/src/__tests__/registry-inputs-spec-parity.test.ts, the one in-tree instance — is not covered. Reporting it would mean flagging a rationale record that the same file asserts on (carve-out 3, a false positive), and the remedy offered would be to hoist a table computed from the live component registry, which cannot be hoisted. One site today, written into the rule header so the next reader does not mistake silence there for coverage.

4. Non-vacuity — the rule is proved able to fail, on each shape it claims

Every mutation was proved to have reached disk by HEAD blob hash versus worktree hash plus a marker count, before any result was read; every restore ran under an EXIT INT TERM trap with absolute paths and was proved by state (git diff HEAD empty), never by exit code.

Rule ablation (eslint-rules/no-line-address-in-test-name.test.js, 12 valid + 11 invalid cases):

leg mutation expected observed
A0 none — control green Tests 23 passed (23), exit 0
A1 delete leg 1, the title read the six literal shapes red Tests 6 failed / 17 passed, exit 1
A2 reduce to an it(-anchored reader (delete legs 2 and 3) the interpolated shapes red Tests 5 failed / 18 passed, exit 1
A3 drop the title-reach carve-out, read the whole row the producer: carve-out red Tests 1 failed / 22 passed, exit 1

A2 is the one that matters. It rebuilds precisely the instrument that under-counted this class and shows it failing five cases the shipped rule passes — the blind spot demonstrated rather than asserted.
A3 proves the carve-outs are load-bearing, not decoration: widening the rule breaks a legitimate in-tree shape.

Tree-level ablation, against real code rather than fixtures — the three repaired files reverted to 868e82501 and the rule re-run:

REPAIRED (HEAD)     0 hits in 0 files
UNREPAIRED (BASE)  37 hits in 3 files  {'inEachCase': 32, 'inTitle': 5}
restore proof: git diff HEAD --stat -> []   git status --short -> []

Every mutation's landing was proved before its result was read, e.g.
LANDED plugin-charts/…-8168.test.tsx HEAD=4f70655867ef DISK=a01b3c097a1e addresses on disk: 5.

5. The conversion — 37 sites, and one naming decision a reviewer should look at

  • packages/plugin-charts/src/ObjectChart.absentCategoryAxisRefusal-8168.test.tsx — 5 names, pure deletion. The content is already in the name (plugin-list ListView …) and the cited source lines are already quoted verbatim in the comment under each it(. Names stay distinct.

  • packages/app-shell/src/views/metadata-admin/inspectors/{ConditionBuilder.subjectVocabulary,FlowNodeConfigField.entryCondition}.test.tsx — 32 addresses. The :NNN half is deleted from each src value, which keeps the provenance file and drops the moving target; these point into another repository's example apps, so nothing here could ever have checked them.

    ⚠️ The one judgement call in this PR, flagged for the app-shell reviewer. src was the corpus rows' only distinguishing field, so deleting the line number alone would collapse twelve case names to one. (corpus row DOLLAR-hash) was added to the two adopts titles to carry the identity the address had been carrying. That is a rewrite, not a deletion, and it is the reason these two files were not treated as a mechanical in-scope repair without saying so.

6. Named consequences

  • ⚠️ objectui#8045 is CLOSED, not open. The dispatch brief for this card carried it as "open and domain:spec". It merged as d9788c14 on 2026-09-07, and packages/types/src/__tests__/text-value-retired-6951.test.ts is clean today: this rule does not red on it, so the conflict the brief prepared for does not exist. Nothing in that file was touched here.
  • objectui#8478 filed (finding label only, no assignee, no domain:*) for the reading objectui#8045 handed to this card: 27 published .describe() schema descriptions in packages/types/src/zod/** cite source line addresses. Ruled out of scope here — a .describe() string is a published schema description reaching the authorable surface, so editing it is contract-adjacent, not a test rename. ⚠️ Re-measured at 27, not the 16 on objectui#8045: overlay.zod.ts and data-display.zod.ts grew the count when objectui#8354 / objectui#8405 landed.
  • The residual gap of section 3 is documented in the rule header rather than carded — it is a stated boundary with one site and no live defect.

7. Verification

All exit codes captured before any pipe. Heavy runs through the shared verify lock, quoting its own verdict line. Measured at 39044bb61.

check reading
the new rule, tree-wide, before no-line-address-in-test-name: 37 hits in 3 files
the new rule, tree-wide, after 0 hits; the census of every other rule's error count is unchanged (130 -> 93, delta only on this rule, 37 -> 0)
wired where this repo wires rules eslint.config.js, its own config object scoped **/*.test.{ts,tsx} and **/__tests__/**/*.{ts,tsx}, mirroring the no-dynamic-import-in-test-hook ratchet block
running there — CI shape (turbo run lint runs eslint . per package) packages/plugin-charts eslint exit=0 (0 errors) · packages/app-shell eslint exit=0 (0 errors)
changed test files Test Files 4 passed (4) · Tests 114 passed (114) · VERDICT command-exit 0, root vitest confirmed (RUN v4.1.10 /home/user/objectui-issue-8047)
pin tests that read eslint.config.js and eslint-rules/** Test Files 15 passed (15) · Tests 349 passed (349) · VERDICT command-exit 0
node scripts/check-changeset-presence.mjs first run exit 1, naming the three test files as published source; after the EMPTY-frontmatter changeset, exit 0 with the gate printing its own exemption sentence
node scripts/check-changeset-no-major.mjs exit 0
node scripts/check-lint-rule-coverage.mjs exit 0 — the new files fall under the existing eslint-rules/**/*.js ledger row
node scripts/check-lint-coverage.mjs exit 0 — 46/46 packages linted, 0 with outstanding errors
node scripts/check-control-bytes.mjs exit 0 — 6706 tracked text files scanned
node scripts/check-governed-queue-guard.mjs --test (all 8 changed paths) exit 0 — NOT GOVERNED; self-test 132 cases pass

⚠️ One instrument correction worth recording: the tree-wide census above was taken with --no-inline-config, which is not what turbo run lint runs. The 93 residual errors it shows are findings suppressed by in-file directives, not regressions — the CI-shaped per-package runs are 0 errors. The delta row is the load-bearing reading, and it is unaffected either way.

Affected packages read from turbo ls --affected against 868e82501, not guessed. Only test files changed in those packages, so running exactly the changed test files is a proved narrowing rather than a skipped one; CI runs the farm.


四轴分析

实际业务需求. 服务的是真实场景,不是投机能力面。判据是实测而非"读起来有用":这个类在一条已有常设裁定(objectui#7853)之下仍然复发,今天在 origin/main 上有 37 处活体,分布在 3 个文件里,其中 ObjectChart.absentCategoryAxisRefusal-8168.test.tsx 是卡片提出之后才落地的新增复发。同时,人工/agent 普查在这个类上结构性欠数:窄仪器看不见 37 处里的 32 处。⇒ 需求是被测出来的,不是被推断出来的。

项目长远合理性. 契约优先,不是临时补丁。规则落在已存在的本地 ESLint 插件里,用已存在的接线(eslint.config.js 的 ratchet 段)和已存在的 RuleTester 约定,不新增 workflow、不新增 CI job、不新增 baseline 文件。⛔ 特别地:没有引入 baseline/grandfather 名单 —— 全部 37 处在同一个改动里转换完毕,符合本仓每一条 object-ui/* ratchet 的既有措辞("existing sites pre-cleaned first, so the rule lints clean on the day it lands"),也符合维护者 2026-08-27「不设分阶段窗口」的裁定。

防 AI 写代码犯错. 这条轴给出最强的支持。行地址型引用正是 AI 批量写测试时最容易产出、也最不可能被发现的东西:它读起来精确、写起来便宜、而且永远不会失败。声明即强制 —— 规则把"这条引用没有任何东西在读"从一句人类要记住的规程(objectui#7853,五次复发)变成一次写入时的响亮拒绝。⛔ 反面做法(在消费端加宽容、或让规则对看不懂的表保持沉默)已被明确拒绝:leg 3 存在的全部理由就是"看不出来"不得和"这里很干净"拼成同一个字。

创业阶段不扩散需求 —— 本轴默认反对新增门禁,必须正面回应. 反对的理由成立:p3,今天没有任何东西是假的,新增守卫就是新增维护面。⇒ 逐条称重后仍然推荐落地,理由有三,且都是量出来的:① 边际维护成本近似为零 —— 一个规则文件加一个测试文件,复用既有插件、既有接线、既有测试框架,没有新 workflow、新 job、新脚本族、新 baseline;② 反对新增门禁的通常论据(没有实测成本、没有 population)在这里两条都不成立,分诊已经论证过,本轮重新取数后结论更强(37 处、复发仍在继续、窄仪器欠数 32/37);③ 本轴反对的是能力扩张,而这不是能力扩张 —— 它不新增任何可声明面、不新增任何可创作键、不给任何人新的东西可写,它只是把一条已经存在的裁定变得可执行。

四轴冲突处的取舍,如实呈现给维护者: 唯一的张力在第四轴与前三轴之间,而它落在"一个 p3 的类值不值一个新守卫"上。本席的推荐是,理由是上面的 ①②③;若维护者读到的权衡不同,合理的替代不是弱化规则,而是整个不落地并把 objectui#8047 关成 not_planned —— ⛔ 不要保留一个把 leg 2/leg 3 拿掉的"轻量版",那正是 A2 消融证明会重新打开盲区的那个形状。


🤖 Generated with Claude Code

https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr


Generated by Claude Code

A `File.tsx:123` address in a test NAME is read by nothing — not an
assertion, no gate parses it, the cited file is never opened — so it
cannot fail, and it rots the first time a line is inserted above what it
cites. objectui#7853 ruled the class (cite by CONTENT, not by line
address); five per-instance repairs followed it without closing it.

Adds `object-ui/no-line-address-in-test-name` to the local plugin and
wires it at `error` over test files, then converts the whole live
population so it lints clean on the day it lands.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
…thing

Tooling plus test names only; empty frontmatter is the gate's own explicit
exemption (scripts/check-changeset-presence.mjs).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3473.5 KB 3512.7 KB
Main entry chunk (gzip) 143.9 KB 350 KB
Entry file index-CjjYG-tk.js
Status PASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 498.87KB 114.10KB
core (index.js) 7.48KB 2.96KB
create-plugin (index.js) 10.12KB 3.28KB
data-objectstack (index.js) 191.36KB 53.16KB
fields (index.js) 243.15KB 61.40KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 15.16KB 3.68KB
plugin-calendar (index.js) 49.00KB 13.91KB
plugin-charts (index.js) 71.39KB 19.92KB
plugin-chatbot (index.js) 194.52KB 46.34KB
plugin-dashboard (index.js) 131.48KB 34.45KB
plugin-designer (index.js) 213.21KB 43.63KB
plugin-detail (index.js) 248.78KB 63.98KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 131.01KB 32.32KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 208.58KB 56.63KB
plugin-kanban (index.js) 55.40KB 15.71KB
plugin-list (index.js) 112.74KB 27.70KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.33KB 3.25KB
plugin-view (index.js) 84.54KB 20.84KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.55KB 2.45KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 13.64KB 4.59KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@claude

claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

LANDEDdomain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46. PR #8480 merged 2026-09-08T02:08:06Z as 61e649ab1; probe run on a clean extract of re-fetched origin/main, ⛔ never on the shared working tree, and ⛔ scoped to the three converted files rather than run as a whole-tree grep.

leg reading on origin/main@61e649ab1
probe — rule registered eslint-rules/index.js:13 (import) and :26 ('no-line-address-in-test-name': noLineAddressInTestName)
probe — wired at error over the test globs eslint.config.js:283 files: ['**/*.test.{ts,tsx}', '**/__tests__/**/*.{ts,tsx}']:286 'object-ui/no-line-address-in-test-name': 'error'
probe — the three converted files .ts:NNN-shaped addresses: 0 · 0 · 0
control — the RuleTester file survives with its cases eslint-rules/no-line-address-in-test-name.test.js, 7096 bytes, invalid block present
control — the sibling ratchet still wired beside it eslint.config.js:268 'object-ui/no-dynamic-import-in-test-hook': 'error' — the wiring block landed, ⛔ it did not move
control — the grep fires the same pattern still matches in other test files across the tree, so the three zeros are about those files, ⛔ not about a dead pattern

That last control is worth reading twice, and it corroborates the design rather than contradicting it. .tsx:NNN still appears in other test files — and the rule is wired at error, and CI was green over 31 checks. ⇒ Every surviving address sits in one of the three carve-outs the rule's header draws on the title's own reach: a comment, an assertion/failure message, or data a test asserts on. The boundary is not a claim in the PR body; it is enforced and demonstrated by the tree.

⚠️ This probe was deliberately scoped after three whole-file greps mis-fired earlier in this round — on a docblock, on a different schema, and on a deliberately dated historical sentence. A grep over a whole file cannot answer a question about one region of it, and this card's own subject is exactly that distinction.

⭐ What landed beyond the fix: the rule judges an address in any string that reaches a title — literal, each-table, and, when the table is not statically legible, the whole file reported at the table — so "the rule could not tell" is never spelled the same way as "there is nothing here." All 37 live sites converted in the same change; ⛔ no baseline, ⛔ no grandfather list.

pm:dispatched stripped in the same pass; domain:devx, tests, tooling, priority:p3 stay. The card closed via Fixes.


Generated by Claude Code

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

Projects

None yet

2 participants