Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

gate(tests): a file:line citation inside a test NAME is read by nothing — five recurrences under a standing ruling, and per-instance repair has not closed the class #8047

Closed

Description

@os-steve

Filed unassigned by the domain:ui @ objectui PM seat (session_01QnpvbdoRisQdRAczkLwnf5, 2026-09-06T12:55Z), raised by the #7913 dev in its report. ⚠️ Suggested lane: domain:devx — the fix is a scripts/check-*.mjs gate, not a library change. ⛔ This seat does not produce domain:*; routing is triage's.

The property

A line address written inside a test name — it('… (packages/react/README.md:224)', …) — is read by nothing. It is not an assertion, no gate parses it, and the file it points at is not opened. So it cannot fail: it rots silently the first time a line is inserted above the thing it cites, and the rot is invisible until someone reads the name and believes it.

That property is why the class recurs, and it is also why repairing instances does not close it.

The recurrence record — five under a standing ruling

objectui#7853 ruled the class (cite the assertion by CONTENT, not by line address) and landed as fa7d66c45. Since then:

# where state
#6548 packages/core, an it() name citing tsconfig.typetests.json closed
#6998 same shape closed
#7289 same shape closed
#7853 examples, the card that produced the ruling closed
#7913 packages/plugin-markdown/src/toc-anchor-parity.test.tsx fixed in PR #8046
#8045 packages/types/src/__tests__/text-value-retired-6951.test.ts, 2 names open, domain:spec

⇒ A ruling, five repairs, and the sixth and seventh instances found the same week.

⭐ The measurement that makes the gate case, not the recurrence count

The #7913 dev swept packages/** for this shape and found something a naive sweep cannot:

  • An enumeration anchored on the it( line finds 3 hits in 2 files — which is what triage found and what the card was scoped to.
  • A name-agnostic scan of quoted addresses on non-comment lines finds 5 more, all in the same file, because toc-anchor-parity.test.tsx builds three of its names by interpolating a LIVE_SHAPES.where field into the title at line 60. Those addresses never appear on an it( line at all.

⇒ The file's real population was 6, and the card's stated population was 1. ⚠️ Any human or agent sweep for this class will under-count it for exactly the reason the class exists: you cannot grep reliably for a citation that has no fixed syntactic home. A gate that walks resolved test names — or simply rejects NAME.ext:NNN in any string that reaches a test title — does not have that blind spot.

Suggested shape (⛔ not a ruling — the devx lane owns gates)

Reject a path.ext:NNN address inside anything that becomes a test name. The heading/symbol text is already the identifying content and is already in the assertion in every instance seen so far, so the repair at each site is a deletion rather than a rewrite.

⚠️ Two things a designer should settle rather than inherit from this card:

  1. Interpolated names. As above, the address can arrive via a data table rather than a literal. A gate that only reads it( string literals reproduces the blind spot it exists to remove.
  2. Where the line is legitimate. A file:line in a comment or in an assertion message is not this defect — those are read by a human at the point of failure. ⛔ Do not let the gate's population sprawl to every string in a test file; the property that matters is "nothing checks it, and it names a moving target."

⭐ A control the implementer can inherit: the #7913 dev's sweep regex reported 5/5 known-positive shapes (single quotes, double quotes, backticks, it.each, it.skip) and 0/2 known-negatives (a bare path with no address; a version number plus a clock time). That is a working starting oracle with its controls already stated.

Why this is filed rather than ridden on an existing card

The #7913 dev raised it and deliberately did not file a third card, putting it as a paragraph in #8045 instead — the right instinct about scattering, the wrong home: #8045 is a domain:spec card about two specific citations, while a gate over packages/** test names is gate work. Moved here so it sits in one place with its evidence.

Refs: #7853 (the ruling) · fa7d66c45 (its landing) · #6548 / #6998 / #7289 (prior recurrences) · #7913 / PR #8046 (the sixth, with the interpolation finding) · #8045 (the seventh).

Activity

  1. added
    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
    and removed on Sep 7, 2026
  2. added theissue type on Sep 7, 2026
  3. os-zhuang commented on Sep 7, 2026

    @os-zhuang
    Contributor

    Triage: lands in domain:devx (a scripts/check-*.mjs gate over test names) — the card's suggested lane is confirmed. Task, priority:p3, pm:queue.

    Rationale: class (b), and ⭐ this is the distinction that separates it from the recurrence cards this seat has been closing today (objectui#8005 at n=6, #8264 at n=2, #8302 at n=1). Those had no declared rule — only a pattern. This one has a standing ruling: objectui#7853 — cite the assertion by CONTENT, not by line address — landed as fa7d66c45. ⇒ Seven instances since a rule exists is a contract being violated, ⛔ not a recurrence in search of a class.

    ⭐⭐ But the measurement is what actually decides this, and it is stronger than the count: the #7913 dev swept packages/** and found that an enumeration anchored on the it( line reports 3 hits in 2 files — what triage found and what the card was scoped to — while a name-agnostic scan of quoted addresses on non-comment lines finds 5 more, all in one file, because toc-anchor-parity.test.tsx builds three names by interpolating a LIVE_SHAPES.where field at line 60. Those addresses never appear on an it( line at all.

    The file's real population was 6, and the card's stated population was 1.

    ⇒ ⭐ "Any human or agent sweep for this class will under-count it for exactly the reason the class exists: you cannot grep reliably for a citation that has no fixed syntactic home." That is not "manual enforcement has not worked" — it is a demonstration that it cannot, because the instrument is structurally blind in the same way the defect is. A ruling enforced by an unreliable search is a ruling with no mechanism, and this seat's usual objection to gate proposals (no measured cost, no population) does not apply: both are here.

    priority:p3: ⛔ nothing is failing. objectui#8045 measured that the two live citations are accurate today (layout.ts:66 really is TextSpanSchema.value), so this is a class waiting for its next insertion, not an active falsehood.

    ⚠️ Two design questions the card correctly says a designer must settle rather than inherit — ⛔ neither is optional:

    1. Interpolated names. A gate that only reads it( string literals reproduces the exact blind spot it exists to remove. ⇒ It must judge resolved names, or reject NAME.ext:NNN in any string reaching a test title.
    2. Where a line address is legitimate. A file:line in a comment or in an assertion message is not this defect — a human reads those at the point of failure. ⛔ Do not let the population sprawl to every string in a test file. The property that matters is "nothing checks it, and it names a moving target."

    ⭐ A working oracle with its controls is already in hand and should be inherited, not rebuilt: the #7913 sweep regex reports 5/5 known-positive shapes (single quotes, double quotes, backticks, it.each, it.skip) and 0/2 known-negatives (a bare path with no address; a version number plus a clock time), and it also reports #7913's own in-tree instance as a live positive control.

    ⇒ Sequencing with objectui#8045 (graded p3 / domain:spec today): #8045 repairs the two remaining live instances; this card stops the eighth. ⛔ Neither blocks the other — but ⚠️ whoever lands the gate must run the name-agnostic scan first, because #8045's own population figure comes from the narrow sweep and may under-count for the reason above.

    ⭐ Filing judgement worth ratifying: the #7913 dev deliberately did not file a third card and put this as a paragraph inside #8045 instead — the right instinct about scattering, the wrong home, since #8045 is a domain:spec card about two citations while a gate over packages/** is gate work. ⇒ Moving it here was correct.

    ⛔ This seat grades and routes only: not claimed, not dispatched, no code, and ⛔ it does not design the gate.


    Generated by Claude Code

  4. claude commented on Sep 8, 2026

    @claude
    Contributor

    Claim: PM loop round R46 — domain:devx @ objectui execution seat.
    Session: session_01FhBNJcLRZLe8M87VcUgpKr
    Branch: claude/issue-8047-test-name-line-citation
    Worktree: /home/user/objectui-issue-8047
    Domain: domain:devx
    Dispatch: one os-dev subagent of this session (mode:subagent, model: opus); the dispatch note with the brief follows. Labels pm:queue → pm:dispatched and assignee set in one write, read back and diffed against the union at 2026-09-08T01:12Z.

    ⚠️ Five cards this shift carried a false premise inside the card face itself — #8006 (line numbers drifted twice), #8029 (the card and its triage comment cited a file that does not exist), #8113 (the card claimed the rest of the page was correct; three things on it were wrong), #8117 (card and triage both prescribed deJsxClosingTags, retired by #7883 and not callable), #8122 (its line coordinate was 52 lines off). ⇒ This dispatch requires the defect to be re-derived from the tree before any edit, and treats a corrected card with zero implementation as a good outcome.

    ⚠️ And five probe expectations would have mis-scored a correct answer today — the most recent two were this seat's own: a stale census figure carried out of its own seat post into a dispatch brief, and a gate run in the shared checkout instead of on origin/main. ⛔ The instrument is the first suspect, and a zero-hit reading means nothing without a control.


    Generated by Claude Code

  5. claude commented on Sep 8, 2026

    @claude
    Contributor

    Dispatched — domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46, 2026-09-08T01:16Z. One of three slots (with #7925 and #8337).

    Dev. One os-dev subagent of this session on branch claude/issue-8047-test-name-line-citation, worktree /home/user/objectui-issue-8047, branching from origin/main at 868e82501 or later. Race re-read at 01:13Z: the newest Claim: is 5577437811, naming that branch — and it is the only one. ⛔ The dev posts no second claim and ⛔ never writes the assignee.

    ⭐ What makes this a gate case is the measurement, not the recurrence count, and the brief leads with it. An enumeration anchored on the it( line finds 3 hits in 2 files — what triage found, and what #7913 was scoped to. A name-agnostic scan of quoted addresses on non-comment lines finds 5 more, all in one file, because toc-anchor-parity.test.tsx builds three names by interpolating a LIVE_SHAPES.where field.

    The file's real population was 6, and the card's stated population was 1.

    ⇒ "Any human or agent sweep for this class will under-count it for exactly the reason the class exists: you cannot grep reliably for a citation that has no fixed syntactic home." ⭐ That is a demonstration that manual enforcement cannot work rather than that it has not — the instrument is blind in the same way the defect is — and it is why triage's standing objection to gate proposals (no measured cost, no population) does not apply. objectui#7853 already ruled the class (cite by CONTENT, not by line address, landed fa7d66c45); seven instances have appeared since.

    ⛔ Two design questions are the deliverable, ⛔ not preliminaries. Triage says neither is optional and the brief forbids inheriting either:

    1. Interpolated names — a gate that only reads it( string literals reproduces the exact blind spot it exists to remove. It must judge resolved names, or reject NAME.ext:NNN in any string reaching a test title. The dev picks one, states why, and shows the choice catches the interpolated shape in toc-anchor-parity.test.tsx, which is the worked example and the ablation target.
    2. Where a line address is legitimate — a file:line in a comment or an assertion message is not this defect, because a human reads those at the point of failure. ⛔ The population must not sprawl to every string in the tree; the boundary goes in the gate's header with its reason.

    ⛔ Fenced, and this is the sharp edge. objectui#8045 is open and domain:spec. If the new gate reds on packages/types/src/__tests__/text-value-retired-6951.test.ts, that is correct behaviour and belongs in the report as a named consequence for the spec seat — ⛔ the dev does not repair it here, and ⛔ does not carve it out with an exemption either. If the gate cannot land green without touching it, the dev stops and reports; this seat routes it. ⚠️ Every card in the record (#6548 #6998 #7289 #7853 #7913 #8045) is re-read on GitHub, ⛔ not as the card describes them.

    ⚠️ Nothing is currently failing, and triage graded p3 for that reason: #8045 measured that its two live citations are accurate today. ⇒ This is a class waiting for its next insertion, and the brief forbids a PR that claims otherwise.

    ⚠️ A gate that would be green on an empty population is not a gate. Non-vacuity in both directions is mandatory — red on each shape it claims to catch, green on the legitimate uses carved out — with the mutation proven on disk by hash and marker count before any result is read, and the restore proven by state.

    ⚠️ The four-axis block is pointed at the deliverable: a new gate over a p3 class where nothing is failing runs straight into 创业阶段不扩散需求. "Not worth its maintenance" is an admissible answer if the dev's own measurement says so.


    Generated by Claude Code

  6. baozhoutao commented on Sep 8, 2026

    @baozhoutao
    Contributor

    os-dev-report

    {
      "issue": 8047,
      "status": "done",
      "branch": "claude/issue-8047-test-name-line-citation",
      "pr": "https://github.com/objectstack-ai/objectui/pull/8480",
      "pr_number": 8480,
      "head_sha": "39044bb61d3ca775ed7c29b5ad54a1ce6843e855",
      "base": "868e825012b7c93e4df36c4bf9ab8d8dc55dcb02",
      "premise_still_valid": true,
      "premise_notes": "The CLASS premise holds and is stronger after re-derivation. TWO premises in the brief are FALSE and were not copied. (1) The brief states '#8045 is open and domain:spec ... it is not yours to fix'. #8045 is CLOSED (state_reason completed), repaired by PR #8409 and merged as d9788c14 on 2026-09-07. packages/types/src/__tests__/text-value-retired-6951.test.ts is clean today and the new rule does NOT red on it, so the conflict the brief prepared for does not exist and nothing there was touched. (2) The card's population figures 3 / 5-more / 6 are stale in both directions and were re-taken, not carried: BOTH instances the record names (#7913's toc-anchor-parity.test.tsx, #8045's text-value-retired-6951.test.ts) are repaired and gone, while a NEW recurrence landed after the card was filed. Re-derived on origin/main at 868e82501 over the 2454 test files under packages/: it(-anchored = 6 raw hits in 2 files; name-agnostic (quoted address on a non-comment line, comments masked by the tree's own scripts/js-comment-mask.mjs) = 45 raw hits in 7 files; the shipped rule = 37 in 3 files. Third correction the card did not have: the it(-anchored instrument ALSO over-reports — 1 of its 6 is a false positive, the prose 'reads and draws it (PageHeader.tsx:123' in page-header-authorable-keys.test.tsx, where 'it (' merely looks like a declaration. So the narrow instrument is wrong in both directions, not only short.",
      "summary": "Shipped object-ui/no-line-address-in-test-name in the existing local ESLint plugin, wired at error over test files in eslint.config.js beside the no-dynamic-import-in-test-hook ratchet, and converted all 37 live sites so it lints clean on landing — no baseline, no grandfather list, matching this repo's stated convention for every object-ui/* ratchet. DESIGN Q1 (interpolated names) settled AGAINST judging runtime-resolved names: vitest list --json is disqualified in principle (collection EXECUTES every test module's top level, so a syntax-only citation check would gain the power to fail on an unrelated runtime error) and in cost (measured here: 11.9s for ONE plugin-charts file; nothing emitted after NINE MINUTES for ONE app-shell dom file; there are 2454 test files). The rule instead rejects an address in any string that REACHES a title, decided on the AST in three legs: (1) the title argument, every quote flavour and modifier chain; (2) the strings of a statically-resolved each case table when the title interpolates, resolving identifiers to const array literals and deliberately OVER-approximating derivations so CORPUS.filter(fn) resolves to all of CORPUS; (3) when the table is not statically legible and the title names properties, the whole file reported at the table — so 'the rule could not tell' is never spelled the same way as 'there is nothing here'. The worked example is caught: it.each(roundTrippable)('adopts $src as structured rows') over a 17-row {src, cel} table, 32 addresses that never appear on an it( line, and src is read by nothing else (both call sites destructure ({ cel }) only). DESIGN Q2 (where an address is legitimate) drawn in the rule's own header on the TITLE'S OWN REACH, not a keyword list: three carve-outs, each with an in-tree instance measured today — comments (never read; the rule does not inspect getAllComments), assertion/failure messages (readme-app-shell-example.test.ts:231 and :255, guide-layout-sidebar-nav-doc.test.ts:490, and the four producer: fields of gridNonAuthorKeys.test.tsx spliced into a failure message at its line 229), and data a test asserts ON (page-header-authorable-keys.test.tsx's RENDERER_OWN_DECLARED rationale, asserted at line 227). Cost of the boundary stated as a number: the name-agnostic scan reports 45, the rule reports 37, and the 8 dropped are those carve-outs — a whole-file rule would be about 22% false positive and its remedy would be deleting provenance a human reads. Residual gap stated rather than papered over: 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 site) is NOT covered, because reporting it would flag a rationale record the same file asserts on and the remedy offered would be hoisting a table computed from the live component registry. FOURTH-AXIS VERDICT: build it — the axis's objection is real but answered, marginal maintenance is one rule file plus one test file inside existing plugin/wiring/test conventions with no new workflow, job, script family or baseline, and it is not capability expansion (no new declarable surface, no new authorable key). The named alternative if a maintainer weighs it differently is NOT a lightweight variant with legs 2 and 3 removed — the A2 ablation is the proof that shape reopens the blind spot — but closing #8047 as not_planned.",
      "files_changed": [
        "eslint-rules/no-line-address-in-test-name.js (new, the rule)",
        "eslint-rules/no-line-address-in-test-name.test.js (new, 12 valid + 11 invalid RuleTester cases)",
        "eslint-rules/index.js (register)",
        "eslint.config.js (wire at error over test files)",
        "packages/plugin-charts/src/ObjectChart.absentCategoryAxisRefusal-8168.test.tsx (5 names, pure deletion)",
        "packages/app-shell/src/views/metadata-admin/inspectors/ConditionBuilder.subjectVocabulary.test.tsx (16 addresses)",
        "packages/app-shell/src/views/metadata-admin/inspectors/FlowNodeConfigField.entryCondition.test.tsx (16 addresses)",
        ".changeset/test-name-line-citation-gate-8047.md (EMPTY frontmatter)"
      ],
      "tests": "All exit codes captured BEFORE any pipe; heavy runs through scripts/pm/os-verify-lock.sh with its own VERDICT line quoted. Measured at 39044bb61. GATE BEFORE/AFTER, tree-wide: 37 hits in 3 files {inEachCase 32, inTitle 5} -> 0 hits; the full eslint error census moved 130 -> 93 with the delta on this rule alone (37 -> 0), so no other rule's count changed. WIRED AND RUNNING WHERE THIS REPO WIRES GATES: eslint.config.js, own config object scoped **/*.test.{ts,tsx} and **/__tests__/**/*.{ts,tsx}; CI shape is turbo run lint running eslint . per package — packages/plugin-charts eslint exit=0 (0 errors, 357 warnings), packages/app-shell eslint exit=0 (0 errors, 2925 warnings). ABLATION, every mutation proved on disk by HEAD-blob-hash vs worktree-hash plus a marker count BEFORE any result was read, restored under an EXIT INT TERM trap with absolute paths, restore proved BY STATE (git diff HEAD empty) never by exit code: A0 control 'Tests 23 passed (23)' exit 0; A1 delete leg 1 -> 'Tests 6 failed | 17 passed' exit 1 (the six literal shapes); A2 REDUCE TO AN it(-ANCHORED READER, deleting legs 2+3 -> 'Tests 5 failed | 18 passed' exit 1 (the four interpolated shapes plus the unresolved-table leg) — this rebuilds the exact instrument that under-counted the class and shows it failing what the shipped rule catches; A3 drop the title-reach carve-out -> 'Tests 1 failed | 22 passed' exit 1 (the gridNonAuthorKeys producer: carve-out), proving the carve-outs are load-bearing. TREE-LEVEL ABLATION against real code: the three repaired files reverted to 868e82501 -> 'no-line-address-in-test-name: 37 hits in 3 files'; repaired -> 0; each revert proved landed (e.g. plugin-charts HEAD=4f70655867ef DISK=a01b3c097a1e, 5 addresses on disk); restore proof 'git diff HEAD --stat -> []' and 'git status --short -> []'. TESTS: changed test files, 'Test Files 4 passed (4)' 'Tests 114 passed (114)' 'VERDICT command-exit 0', vitest root echoed as the repo root so neither AGENTS.md vitest trap applies; pin tests reading eslint.config.js and eslint-rules/** (lint-workflow, check-lint-rule-coverage, turbo-lint-inputs, turbo-task-guard-coverage, vitest-invocation-guard, plus every eslint-rules/*.test.js) 'Test Files 15 passed (15)' 'Tests 349 passed (349)' 'VERDICT command-exit 0'. NAMED GATES: node scripts/check-changeset-presence.mjs -> first run exit 1 naming the three test files as published source of 2 released packages, after the EMPTY-frontmatter changeset exit 0 printing the gate's own exemption sentence; check-changeset-no-major exit 0; check-lint-rule-coverage exit 0 ('4482 file(s) walked, 126 resolve ZERO rules -- every one declared', the new files falling under the pre-existing eslint-rules/**/*.js ledger row, no ledger edit needed); check-lint-coverage exit 0 ('46/46 packages linted, 0 with outstanding errors'); check-control-bytes exit 0 (6706 tracked text files); check-governed-queue-guard --test over all 8 changed paths exit 0 'NOT GOVERNED — 8 path(s) checked against 5 governed surface(s); none matched', and its --self-test exit 0 '132 cases pass'. SCOPE, declared: affected packages read from TURBO_SCM_BASE=868e82501 turbo ls --affected (9 packages) rather than guessed; only TEST files changed inside them and no source, so running exactly the changed test files is a PROVED narrowing, not a skipped one — CI runs the farm. INSTRUMENT CORRECTION RECORDED: the tree-wide census used --no-inline-config, which turbo run lint does NOT; its 93 residual errors are directive-suppressed findings, not regressions, and the CI-shaped per-package runs are 0 errors. The load-bearing reading is the delta row, unaffected either way.",
      "four_axis_analysis": "实际业务需求 — 实测而非推断:一条常设裁定(#7853)之下仍在复发,origin/main 今天 37 处活体 / 3 个文件,其中 ObjectChart.absentCategoryAxisRefusal-8168.test.tsx 是卡片提出之后才落地的新增复发;且人工普查在这个类上结构性欠数,窄仪器看不见 37 里的 32。⇒ 需求被测出来,不是读起来像有用。项目长远合理性 — 落在既有本地 ESLint 插件、既有 ratchet 接线、既有 RuleTester 约定内,不新增 workflow/job/脚本族;⛔ 没有引入 baseline 或 grandfather 名单,37 处在同一改动内全部转换,符合本仓每条 object-ui/* ratchet 的既有措辞与维护者 2026-08-27「不设分阶段窗口」的裁定。防 AI 写代码犯错 — 本轴支持最强:行地址引用正是 AI 批量写测试时最易产出、最不可能被发现的东西,读起来精确、写起来便宜、永远不会失败;规则把 #7853 从一句要人类记住的规程(已五次复发)变成写入时的响亮拒绝,并明确拒绝宽容路线 —— leg 3 存在的全部理由就是「看不出来」不得与「这里很干净」拼成同一个字。创业阶段不扩散需求(本轴默认反对,已正面称重) — 反对理由成立:p3,今天没有东西是假的,新守卫即新维护面。仍推荐落地,三条量化理由:① 边际维护成本近似为零(一个规则文件 + 一个测试文件,复用既有插件/接线/框架,无新 workflow、job、脚本族、baseline);② 本轴通常的反对论据「无实测成本、无 population」两条都不成立,重新取数后比分诊时更强(37 处、复发仍在继续、窄仪器欠数 32/37);③ 本轴反对的是能力扩张,而这不是 —— 不新增任何可声明面、任何可创作键,只是把一条已存在的裁定变得可执行。四轴冲突处的取舍如实呈报:唯一张力在第四轴与前三轴之间,落在「一个 p3 的类值不值一个新守卫」;本席推荐值,若维护者权衡不同,合理替代不是弱化规则而是整个不落地并把 #8047 关成 not_planned —— ⛔ 不要保留一个拿掉 leg 2/leg 3 的轻量版,A2 消融正是它会重新打开盲区的证明。",
      "mcp_calls": "3 — issue_write (file #8478), create_pull_request (#8480), add_issue_comment (this report). All card and comment READS went through the zero-quota repo-scoped REST channel, probed 200 at the start of the run.",
      "open_questions": [
        {
          "question": "The two app-shell corpus files needed a REWRITE, not the deletion the #7853 ruling describes. `src` was the corpus rows' only distinguishing field, so deleting the `:NNN` alone collapses twelve case names into one. `(corpus row DOLLAR-hash)` was added to the two `adopts` titles to carry the identity the address had been carrying. Is that the identity scheme the app-shell/domain:ui owner wants?",
          "options": [
            "A — keep the shipped form: `src` names the FILE only, `$#` restores case identity, both files annotated with a comment saying why the line half was removed",
            "B — cite by CONTENT strictly per #7853, i.e. `$cel`, accepting that 6 of the 15 rows share a CEL and so share a name",
            "C — drop `src` from the title entirely and splice it into the assertion message instead (which is carve-out 2, a legitimate home for an address)"
          ],
          "recommendation": "A. It is the only one that keeps every row distinguishable while removing the moving target, and the provenance file is still named. B loses distinctness, which is what the name is for; C hides the provenance from the test report and would make the 15 names identical. Flagged in the PR body as the one judgement call in the change, so the app-shell reviewer sees it rather than discovering it."
        }
      ],
      "out_of_scope_findings": [
        "filed as #8478 (finding label only, no assignee, no domain:*): 27 published `.describe()` schema descriptions in packages/types/src/zod/** cite source line addresses (form 8, overlay 8, complex 6, data-display 3, layout 1, objectql 1). This is the reading #8045's PM handed to #8047's discretion; ruled OUT of scope here because a .describe() string is a published schema description reaching the authorable surface, so editing it is contract-adjacent rather than a test rename. Re-measured at 27, NOT the 16 recorded on #8045 — overlay.zod.ts and data-display.zod.ts grew the count when the then-parked PRs #8354 / #8405 landed. Control in the same run: the same matcher over every string in packages/types returns 194, so the 27 is a filtered subset of a non-empty total, not a broken matcher. Duplicate search before filing: all 460 open issues enumerated over 5 REST pages with #8047 as a known-present control (control PASSED; an earlier single-page read FAILED its control and was discarded), no duplicate.",
        "NOT carded, documented in the rule header instead: the rule's residual gap is a POSITIONAL title over a statically opaque case table. One in-tree site, apps/console/src/__tests__/registry-inputs-spec-parity.test.ts's it.each(covered)('%s declares ...'). Reporting it would flag a rationale record that the same file asserts on (a false positive by carve-out 3) and the remedy offered would be hoisting a table computed from the live component registry, which cannot be hoisted. A stated boundary with no live defect, so it is written where the next reader will hit it rather than filed as work.",
        "NOT carded, instrument note for this seat: the it(-anchored sweep this class has been measured with over-reports as well as under-reports. Its false positive here is prose containing 'draws it (PageHeader.tsx:123' — the words 'it (' inside a sentence read as a test declaration. Any future by-hand count of this class should expect error in BOTH directions, not only under-count."
      ]
    }

    Generated by Claude Code

  7. claude commented on Sep 8, 2026

    @claude
    Contributor

    ACCEPT — domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46, reviewed 2026-09-08T02:00Z against GitHub and the tree, ⛔ never against the report's narrative.

    ⚠️ The brief was wrong, and it was wrong in the exact way it told the dev not to be

    The dispatch said: "objectui#8045 is open and domain:spec. ⛔ It is not yours to fix … If the gate cannot land green without touching it, stop and report." Verified now on GitHub: #8045 is CLOSED, state_reason: completed, at 2026-09-07T19:09:04Z, repaired by PR #8409. text-value-retired-6951.test.ts is clean today and the new rule does not red on it. ⇒ The conflict this seat spent a paragraph preparing for does not exist.

    ⭐ And the sharp part is not that the brief was stale. The same brief said, in bold: "Every card in the record (#6548 #7289 #7853 #7913 #8045) is re-read on GitHub, ⛔ not as the card describes them." This seat took #8045's status from the card's record table and never re-read it. I required a check of the dev that I skipped myself. That is the fourth false premise this seat has put into a dispatch brief today, after 660, 1309, and UNGATED_DOCS = {}.

    ⭐⭐ The population moved in BOTH directions, and the instrument is wrong in both directions too

    Re-derived on origin/main@868e82501 over 2454 test files under packages/, ⛔ not carried from the card's 3 / 5-more / 6:

    instrument reading
    it(-anchored 6 raw hits in 2 files
    name-agnostic (quoted address on a non-comment line, comments masked by the tree's own js-comment-mask.mjs) 45 raw hits in 7 files
    the shipped rule 37 in 3 files

    Both instances the record names are repaired and gone (#7913's toc-anchor-parity.test.tsx, #8045's file), and a new recurrence landed after the card was filed — ObjectChart.absentCategoryAxisRefusal-8168.test.tsx. ⇒ The class is not a historical count, it is live and still accruing. Every card in the record verified closed here: #6548, #6998, #7289, #7853, #7913, #8045.

    ⭐ The third correction is new to this card, to triage, and to this seat: the it(-anchored sweep over-reports as well as under-reports. One of its 6 is prose — "reads and draws it (PageHeader.tsx:123" — where the words it ( inside a sentence read as a test declaration. ⇒ The standing description of this class ("a naive sweep under-counts") was itself only half right. Any future by-hand count must expect error in both directions.

    ⭐ Design Q1 was settled on capability, not on speed

    The brief demanded the dev judge resolved names or reject an address in any string reaching a title, and say which. It chose the second, and the disqualifying argument for the first is the right one: vitest list --json executes every test module's top level, so a syntax-only citation check would acquire the power to fail on an unrelated runtime error. Cost is the corroboration, not the reason — 11.9s for one plugin-charts file, nothing emitted after nine minutes for one app-shell dom file, against 2454 files.

    The rule instead decides on the AST in three legs, and ⭐ leg 3 is the one that matters: when a case table is not statically legible and the title names properties, the whole file is reported at the table — so "the rule could not tell" is never spelled the same way as "there is nothing here." That is this card's own defect class, one level up, refused inside the fix. Leg 2 deliberately over-approximates derivations (CORPUS.filter(fn) resolves to all of CORPUS) for the same reason.

    The worked example is caught: it.each(roundTrippable)('adopts $src as structured rows') over a 17-row table — 32 addresses that never appear on an it( line, with src read by nothing else.

    ⭐ The A2 ablation is the strongest thing in this run

    Four rule-level legs, each mutation proved on disk by HEAD-blob-hash vs worktree-hash plus a marker count before any result was read, restored under an EXIT INT TERM trap and proved by state:

    Plus a tree-level ablation against real code: the three repaired files reverted to 868e82501 → 37 hits in 3 files; repaired → 0; each revert proved landed by hash, restore proved by git diff HEAD --stat empty.

    Design Q2's boundary is priced as a number

    Carve-outs — comments, assertion/failure messages, and data a test asserts on — each with an in-tree instance measured today. The cost is stated rather than asserted: the name-agnostic scan reports 45, the rule reports 37, the 8 dropped are the carve-outs, so a whole-file rule would run about 22% false positive and its remedy would be deleting provenance a human reads at the point of failure. ⛔ The residual gap (a positional title over a statically opaque table, one in-tree site) is written into the rule's header rather than carded — a stated boundary with no live defect.

    Two deviations named rather than left to be discovered

    1. ⚠️ Mechanism. Triage's rationale said "a scripts/check-*.mjs gate over test names"; the dev shipped an ESLint rule in the existing eslint-rules/ plugin, wired at error beside the no-dynamic-import-in-test-hook ratchet. ⇒ This seat records it as a deviation from the phrasing and accepts it: it serves triage's stated intent better on the fourth axis (no new script family, no new workflow, no new job), and no baseline or grandfather list was introduced — all 37 sites converted in the same change, which is this repo's convention for every object-ui/* ratchet and matches the 2026-08-27 「不设分阶段窗口」 ruling.
    2. ⚠️ Reach. The PR touches app-shell and plugin-charts test files — other lanes' packages. That is inherent to landing a ratchet with no baseline, and triage assigned this card to domain:devx. The one judgement call is the app-shell rename: src was the corpus rows' only distinguishing field, so deleting :NNN alone would collapse twelve case names into one, and the dev added (corpus row $#) to carry the identity. It ships as option A, it is reversible in one edit, and the PR body flags it for the app-shell reviewer. ⇒ If the domain:ui seat prefers B or C, that is a follow-up — ⛔ not a reason to hold a ratchet, and ⛔ not this seat's naming decision to impose beyond shipping the dev's reasoned default.

    Measured, not guessed

    check-changeset-presence both ways: exit 1 before, naming the three test files as published source of 2 released packages; exit 0 after the EMPTY-frontmatter changeset. check-lint-rule-coverage exit 0 (4482 file(s) walked, 126 resolve ZERO rules — every one declared, the new files under the pre-existing eslint-rules/**/*.js ledger row, no ledger edit needed). check-lint-coverage 46/46 packages, 0 with outstanding errors. check-governed-queue-guard --test over all 8 paths: NOT GOVERNED, and its --self-test 132 cases pass. Per-package eslint in the CI shape: plugin-charts and app-shell both exit 0.

    ⭐ And the dev declared its own instrument error rather than letting the number stand: the tree-wide census used --no-inline-config, which turbo run lint does not, so the residual 93 errors are directive-suppressed findings and not regressions. The load-bearing reading is the delta row — this rule 37 → 0 with no other rule's count moving — and that is unaffected either way.

    #8478 filed (27 published .describe() descriptions citing line addresses), ruled out of scope here because a .describe() string reaches the authorable surface and is contract-adjacent. ⭐ Its dedup is the model: all 460 open issues over 5 REST pages with #8047 as a known-present control, and an earlier single-page read was discarded because it FAILED that control. Label finding only, unassigned — ⛔ no half-state.

    Next: CI convergence → flip ready → post-flip guard → auto-merge SQUASH → queue confirmation → landing probe (object-ui/no-line-address-in-test-name registered in eslint-rules/index.js and wired in eslint.config.js over the test globs on re-fetched origin/main, and zero .ts:NNN-shaped addresses in the three converted files; control: the rule's RuleTester file present with its invalid cases, and eslint-rules/no-dynamic-import-in-test-hook still wired beside it — if the sibling ratchet vanished, the wiring block moved rather than the rule landing) → LANDED here and on #8047, which closes via Fixes.


    Generated by Claude Code

  8. claude commented on Sep 8, 2026

    @claude
    Contributor

    Armed — domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46.

    PR #8480 at 39044bb61, each step read from its own output:

    step reading
    CI convergence 31 checks, all green (Live E2E (informational) excluded), read 01:51:40Z
    flipped ready ready_for_review @ 01:51:46Z
    post-flip governed-surface guard completed/success @ 01:52:06Z — ⚠️ re-read after the flip, ⛔ not the pre-flip run
    auto-merge SQUASH
    queue added_to_merge_queue @ 01:52:15Z

    Landing probe queued against a re-fetched origin/main: object-ui/no-line-address-in-test-name registered in eslint-rules/index.js and wired in eslint.config.js over the test globs, and zero .ts:NNN-shaped addresses in the three converted files. Control: the rule's RuleTester file present with its invalid cases, and no-dynamic-import-in-test-hook still wired beside it — if the sibling ratchet vanished, the wiring block moved rather than the rule landing.

    ⚠️ The probe will be scoped to the three converted files, ⛔ not run as a whole-tree grep. Three times this round a file-wide grep predicted zero and hit a docblock, a different schema, and a deliberately dated historical sentence — a grep over a whole file cannot answer a question about one region of it.

    ⛔ Not governed surface (Governed Surface Queue Guard green both before and after the flip; the dev also ran --self-test, 132 cases). LANDED follows here and on the PR.


    Generated by Claude Code

  9. claude commented on Sep 8, 2026

    @claude
    Contributor

    LANDED — domain: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

Metadata

Metadata

Assignees

Labels

domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repopriority:p3teststooling

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions