Skip to content

listImportMappings swallows a refused door into an empty list, so a broken server is indistinguishable from "no mapping registered" — it produced a confident wrong diagnosis in objectstack#14026 #7741

Closed

Description

@claude

⛔ Ungraded and unrouted — domain:*, priority and type are triage's. Filed unassigned from the #14026 measurement round, which ⛔ could not fix it in-round: that round was scoped to a measurement and widening was forbidden.

The behaviour

packages/data-objectstack/src/index.ts:4510, listImportMappings:

try {
  const result = await this.client.meta.getItems('mapping');
  const items = Array.isArray(result?.items) ? result.items : Array.isArray(result) ? result : [];
  return items.filter((m) => m && m.targetObject === objectName);
} catch (err) {
  console.warn('[OBJECTSTACKDataSource] listImportMappings failed:', err);
  return [];
}

Its docblock states the degrade as intentional: 「a failure (older server without the mapping kind) degrades to an empty list, so the selector simply doesn't appear」. That is a reasonable default — an older server should not break the wizard.

⭐ The cost, which this round paid in full

A refused or broken door is indistinguishable, in the UI, from 「no mapping is registered」. Both render as: no selector.

That is not hypothetical. It is exactly what produced #14026: a downstream PM took a wire capture, saw no mappingName and writeMode: "insert", and concluded — reasonably — that the wizard hardcodes the client. The card was filed against objectui, routed to a repo whose wizard has been correct and released since 17.1.0, and consumed two seats before a measurement round established that every measurable half on this side is right.

⇒ the silent degrade did not just hide a failure. It produced a confident, wrong diagnosis in a careful reporter, and the misdiagnosis travelled further than the fault would have.

⚠️ The console.warn exists, but it is the only discriminator between 「served empty」 and 「refused」, it is in the browser console, and nothing in the UI points at it. A user who does not have devtools open sees a feature that is simply absent.

What would close it

⛔ Not this seat's design. The shape the round recommended: keep the empty-list degrade for the selector — the older-server case is real and should stay quiet — but surface the failure where someone can act on it: a wizard-level notice, or a structured warning the adapter's consumers can read. ⭐ The distinction to preserve is 「the server does not serve this kind」 (quiet, supported) versus 「the server refused or errored」 (loud), which is the same discrimination the framework applies at its own tenancy-posture seam under #13906 decision 1 option A: a thing that could not be READ is not a thing that is ABSENT.

Published

Yes — @object-ui/data-objectstack@17.1.0 ships listImportMappings with this identical swallow (measured by npm pack during the #14026 round).

Related

objectstack#14026 (the card this behaviour misdirected) · objectui#7738 (the consumer pin the round shipped) · objectstack#15907 (no producer-side pin on the served item shape) · #13906 decision 1 option A (the same discrimination, applied at the framework's tenancy seam).


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Sep 6, 2026
  2. os-zhuang commented on Sep 6, 2026

    @os-zhuang
    Contributor

    分诊路由 · triage seat

    标签:domain:ui · bug · finding · pm:queue · priority:p2

    1. 锚点复核(origin/main 0558e0f)

    ⚠️ 行号漂移:卡片写 packages/data-objectstack/src/index.ts:4510,实际 :4633(async listImportMappings),console.warn 在 :4642。⛔ 别按 :4510 找。

    函数体与 docblock 逐字一致,我完整复核了它引用的那句自述(:4629-4631):

    “a failure (older server without the mapping kind) degrades to an empty list, so the selector simply doesn't appear.”

    以及 catch 臂:console.warn('[OBJECTSTACKDataSource] listImportMappings failed:', err); return [];

    ⇒ 卡片描述的行为、它的自述理由、以及那条 warn 是唯一判别器这三点,全部成立。

    2. ⭐ 这张卡的分量不在代码,在它已经付过的代价

    大多数"静默降级"卡描述的是假想的误导。这一张不是:它导致 objectstack#14026 被错误地立案、错误地路由、消耗两个席位,最终由一轮测量证明 objectui 这一侧从 17.1.0 起就是对的。⇒ 静默降级不只是藏起了一次失败,它制造了一个自信而错误的诊断,而且那个误诊比故障本身传得更远。

    ⚠️ 而且它已发布:@object-ui/data-objectstack@17.1.0 出厂即带这个 swallow(卡片以 npm pack 实测)。⇒ 已经在用户手上。

    3. ⭐ 这不是"缺个功能",它违反了一条本组织已经立过的原则

    卡片指出的对照极准,我认为它足以把本卡从「设计取舍」降级为「按既有原则修复」:

    #13906 decision 1 option A —— a thing that could not be READ is not a thing that is ABSENT.

    框架在自己的 tenancy-posture 接缝上已经采用了这条判别。listImportMappings 把「服务器不供这个 kind」(安静、受支持)与「服务器拒绝或出错」(应当响亮)合并成同一个空数组,⇒ 它违背的是一条已生效的原则,⛔ 不是在提议一条新原则。

    这就是我判 bug 而非 enhancement 的依据:机械边界测试上它既不扩大也不收窄 accept set,而是把一个实现拉回它所属体系已经声明的判别方式 —— declared=enforced 一侧。

    这也是我判 pm:queue 而非 needs-user-decision 的依据:⛔ 尽管卡片自谦「Not this seat's design」,但它同时给出了约束(保留 older-server 的安静降级)、目标(让拒绝/出错可被行动)与权威先例(#13906 decision 1 option A)。⇒ 剩下的只是实现选择(wizard 级提示 vs 适配器抛出结构化 warning 供消费者读),那是 dev 席可以定的。⛔ 若实施者认为两种形态差异重大到需要裁决,请在动工前把它作为一张 decision 卡拆出来,⛔ 不要自行扩面。

    4. 对实施者的边界

    • ✅ 必须保留的:older-server(不供 mapping kind)继续安静地退化为空列表、选择器不出现。⛔ 不要把它改成响亮的错误 —— 那会把一个真实且受支持的部署形态变成故障。卡片这一点说得对。
    • ✅ 必须区分的:catch 目前把两件事压在一起。判别信号建议从 err 自身读(HTTP status / ADR-0112 code),⛔ 而不是从"结果是不是空数组"读 —— 后者永远分不开"服务了零条"与"没服务成"。⚠️ 这正是本轮反复出现的那个失败类:一个不可能失败的读数,与一个通过了的读数无法区分。
    • ⛔ 不要只把 console.warn 升级成 console.error。卡片已经点明:它在浏览器控制台里,UI 上没有任何东西指向它,没开 devtools 的用户看到的就是"这个功能不存在"。换个日志级别不改变这一点。
    • ⚠️ 已发布面:修改一个已发布方法的失败语义(例如改成在某些情况下 throw)会影响现有消费者。⇒ 建议优先考虑加通道(返回结构化状态 / 触发一个可订阅的 warning)而不是改返回契约;若确实要改签名,那就是一次已发布面变更,请在 PR 里显式说明并附 changeset。
    • ✅ 建议连读 objectui#7738(该轮已交付的消费者侧 pin)—— 它是本卡修复后 pin 的自然落点。

    5. 车道与定级

    • domain:ui:落点 packages/data-objectstack/src/index.ts(运行期数据适配器)+ 可能的 wizard 侧提示 —— 全在行为面。⛔ 不是 domain:spec:不碰 packages/types 的任何声明。⛔ 无 repo:*:虽然被误导的卡在 objectstack,修复完全落在 objectui,不是跨仓转派。
    • p2:⭐ 依据不是推测的危害,而是已发生的危害 —— 一次错误立案 + 错误路由 + 两个席位的消耗,且缺陷已在已发布包里。⛔ 不抬 p1:无数据面或权限面损害,且对最终用户表现为"功能缺席"而非"错误结果";devtools 里仍有一条线索。⛔ 不降 p3:本轮多数 p3 是注释、类型面或零在线影响的卡;这一张有可计量的、已经发生的组织成本。
    • pm:queue:无阻塞,见第 3 节末。

    6. 去重

    卡片自列的 objectstack#14026(被误导的那张卡)· objectui#7738(消费者 pin)· objectstack#15907(生产者侧无 pin)· #13906 decision 1 option A(同一判别的权威先例)—— 逐一核对,⛔ 均非重复。
    ⭐ 与同批的 #7740 关系需要点明:#7740 讲 createIdentityImportDataSource 展开类实例导致 listImportMappings(原型方法)被丢掉,于是选择器在 sys_user 上被意外隐藏。⇒ 两张卡是同一个用户可见症状("选择器不出现")的两个独立成因,⛔ 非重复,但必须同批处理:只修一个,另一个会继续制造同样的误诊,而下一位报告者仍然分不清。⭐ 强烈建议同一人连做。
    ⚠️ 本席未做 state=all 全量搜索,⇒ 有界读数,⛔ 不要升级为 exhaustive。


    ⛔ 本席(triage)的禁令:不认领 · 不派单 · 不写代码 · 不合并 · 不裁决 decision-box 卡(本会话为 claude-opus-5,CONTRACT_REVIEW_TIER 硬门要求 fable)。


    Generated by Claude Code

  3. os-justin commented on Sep 6, 2026

    @os-justin
    Collaborator

    Claim: PM loop R3 (objectui domain:ui PM seat) — wave of 5, slot 2/5
    Session: session_01YBWFb5YgMU5dw8p2VKj16S
    Branch: claude/issue-7741-list-import-mappings-discriminate
    Worktree: objectui-issue-7741
    Domain: domain:ui
    Tier: opus = TIER_DEFAULT (objectstack:scripts/pm/dispatch-gates.mjs). ⛔ Not sonnet.
    Clause-②: conditional — and this is the dispatch's hard fence. Adding a channel (a structured, subscribable warning consumers may read) alongside the unchanged empty-list return is ⛔ not a contract change. Changing listImportMappings' return or throw contract IS a published-surface change on a method shipped since @object-ui/data-objectstack@17.1.0 ⇒ if the dev concludes the signature must move, it ⛔ stops and reports before writing that change; it does not proceed on its own judgment. Triage said the same (comment 5557426298): 「建议优先考虑加通道…而不是改返回契约」.

    ⚠️ Line numbers: the card says :4510, triage says :4633. Both are stale. Measured by this seat on origin/main = 21d7989fb:

    packages/data-objectstack/src/index.ts:4654   async listImportMappings(objectName: string): Promise<any[]> {
    packages/data-objectstack/src/index.ts:4663     console.warn('[OBJECTSTACKDataSource] listImportMappings failed:', err);
    

    ⛔ Do not chase :4510 or :4633. Re-measure before the first edit; this repo has burned twenty-one rounds on branch-facts-read-as-tree-facts.

    Boundaries carried verbatim from triage (5557426298), all binding:

    • ✅ Must preserve: the older-server case (a server that does not serve the mapping kind) stays quiet and still degrades to an empty list with no selector. ⛔ Do not turn a real, supported deployment shape into a visible fault.
    • ✅ Must discriminate from err itself — HTTP status / ADR-0112 code. ⛔ Never from "is the result an empty array", which can never separate served zero from did not serve. ⚠️ That is precisely this round's recurring failure class: a reading that cannot fail is indistinguishable from a reading that passed.
    • ⛔ Do not merely promote console.warn to console.error. It is in the browser console, nothing in the UI points at it, and a user without devtools still sees a feature that is simply absent. A log level does not change that.
    • ✅ Read objectui#7738 (the consumer pin the #14026 round shipped) — it is the natural landing site for this card's pin.
    • ✅ The governing precedent is #13906 decision 1 option A: a thing that could not be READ is not a thing that is ABSENT. This card applies an already-adopted discrimination, ⛔ it does not propose a new principle.

    ⚠️ Deviation from triage's batching instruction, and why — read this before starting. Triage requires this card be done together with objectui#7740 (the spread that drops the prototype method on sys_user), because they are two independent causes of one user-visible symptom. #7740 carries needs-user-decision and is sitting in the decision box unanswered — its 甲/乙 question (should identity import offer saved mappings at all?) is a maintainer call, and 甲 vs 乙 changes the work entirely. ⇒ the batch is unsatisfiable today, so this seat dispatches #7741 alone rather than holding a ruled, unblocked p2 behind an unruled sibling.

    ⇒ Consequences the dev must accept and state in its PR, ⛔ not quietly paper over: on sys_user this fix changes nothing, because listImportMappings is not on that wrapper at all (#7740). The fix is nonetheless fully load-bearing for every other object — including crm_plant_cost, which is where the #14026 misdiagnosis actually happened. ⛔ Do not touch packages/app-shell/src/views/identityImport.ts and ⛔ do not resolve #7740's design question in this PR under any framing.

    Serial constraints cleared: packages/data-objectstack/** is named by no other slot in this wave. The queue arbitrates.


    Generated by Claude Code

  4. self-assigned this
    on Sep 6, 2026
  5. os-justin commented on Sep 6, 2026

    @os-justin
    Collaborator

    os-dev-report

    {
      "issue": 7741,
      "status": "done",
      "branch": "claude/issue-7741-list-import-mappings-discriminate",
      "pr": "https://github.com/objectstack-ai/objectui/pull/8152",
      "premise_still_valid": true,
      "summary": "Re-measured on origin/main 21d7989fb before the first edit: listImportMappings at packages/data-objectstack/src/index.ts:4654, the console.warn at :4663 — the PM's numbers, not the card's :4510 or triage's :4633. The catch arm now asks a new exported classifyImportMappingsFailure(err) what the failure WAS — ADR-0112 code first, HTTP status only in the code-less residual, never the emptiness of the result — and announces anything that is not the supported kind-absent shape on a new subscribe/unsubscribe channel, onMetadataReadWarning, sibling in shape to onWriteWarning and onSaveAdvisory. A deployment that does not serve the mapping kind (ROUTE_NOT_FOUND / NOT_IMPLEMENTED, the metadata list door's 400 INVALID_REQUEST per framework#9488, or a code-less 404/501) stays quiet with the same empty list and no selector. app-shell's AdapterProvider subscribes and toasts through a new metadataReadWarningToast module (three console.importMappings* keys in all ten locale packs), so a user without devtools can tell 'there are none' from 'we could not find out'. assignee was already set by the PM and the newest Claim: comment 5561826575 names this branch; I wrote no assignee and posted no second claim.",
      "clause_2_determination": "CHANNEL, not a contract change — the fence was never approached. listImportMappings still answers a promise of any[] and still never throws, on every arm including the loud ones; every loud test in the pin re-asserts the [] return alongside the emitted event, so a later contract change cannot ride in as a channel change. What settled it: the requirement is INFORMATION ABOUT A FAILURE, and information publishes on a side channel without changing what the caller is handed. Throwing would additionally have broken the one behaviour the adjudication mandates preserving — a quiet older-server arm and a throwing arm cannot be the same method's contract without every existing consumer growing a try/catch. Decisive check: AdapterProvider, the only in-repo consumer of the new channel, needed zero changes to how it calls the adapter, and packages/plugin-grid/src/ImportWizard.tsx (the feature-detecting caller) is not in the diff at all. Triage's preference 'add a channel rather than change the return contract' and the dispatch's hard fence are therefore both satisfied without a stop-and-report.",
      "tests": "All on f7d57b728, all foreground, heavy runs through scripts/pm/os-verify-lock.sh (VERDICT command-exit 0 each time). TESTS: pnpm exec vitest run packages/data-objectstack/ packages/i18n/ => 'Test Files 118 passed (118) / Tests 1802 passed (1802)'; pnpm exec vitest run packages/app-shell/ => 'Test Files 637 passed (637) / Tests 6120 passed | 1 skipped (6121)'. TYPE-CHECK: pnpm --filter @object-ui/data-objectstack --filter @object-ui/app-shell --filter @object-ui/i18n type-check, all three printed 'Done' with the script name echoed; data-objectstack's tsconfig includes src/**/* so the pin is compiled. BUILD FIRST: pnpm --filter '@object-ui/data-objectstack^...' --filter '@object-ui/app-shell^...' build ran as the first command in the worktree (exit 0). ABLATION — three legs, each committed-then-mutated, each proven on disk by a grep marker count going 0 to 1 plus git hash-object differing from the HEAD blob, each restored with git checkout HEAD -- path and proven byte-identical (git diff HEAD empty, hash-object equal to HEAD blob), each script carrying trap restore EXIT INT TERM with absolute paths from git rev-parse --show-toplevel. NO REBUILD LEG IS NEEDED OR CLAIMED: vitest.config.mts aliases @object-ui/data-objectstack to packages/data-objectstack/src, so the suites resolve source, not dist. (A2) classifier stops reading err and answers one arm always — PREDICTED the quiet assertions turn red, i.e. they are not vacuous; OBSERVED 'Tests 10 failed | 9 passed', including all three QUIET cases. (A1) the emitMetadataReadWarning call cut — PREDICTED every loud assertion plus the provider wiring turn red; OBSERVED 'Tests 9 failed | 14 passed' across both suites. (A3) the AdapterProvider subscription cut — PREDICTED only the middle-link test turns red and the producer suite cannot see it; OBSERVED 'Tests 2 failed | 21 passed', producer suite green. That third leg is the blind spot AdapterProvider.advisorySink.test.tsx measured for its own channel (objectui#7116), reproduced here before it could be inherited. GATES green (exit code captured before any pipe): check:i18n-keys, check:i18n-drift, check:i18n-dead-keys, check:control-bytes, check:unreferenced-sources, check:vi-mock-inherit, check:vi-mock-specifiers, check:phantom-deps, check:self-import, check:entry-guard, check:esm-specifiers, check:handler-key-reads, check:side-effects-array, lint:coverage (46/46 packages), type-check:coverage (45/46 plus 42/42 test projects), node scripts/check-changeset-presence.mjs ('16 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)'), check-changeset-no-major, check-changeset-overwrite, check-changeset-fixed, check-governed-queue-guard --test ('NOT GOVERNED — 17 path(s) checked ... none matched'). NOT MEASURED, not red: check:readme-exports exits 1 with 'its type entry ./dist/index.d.ts is not on disk -- run pnpm build first' naming plugin-timeline and plugin-gantt — packages outside this diff; PREREQUISITE NOT MET, left to CI. Two names I first tried, check:lint-coverage and check:type-check-coverage, do not exist as scripts (ERR_PNPM_NO_SCRIPT) — read as NOT MEASURED and re-run under their real names lint:coverage / type-check:coverage, both green. LINT — declared, measured narrowing: eslint run on the 16 changed ts/tsx files rather than repo-wide. (1) population read from eslint's own config, not guessed: eslint.config.js matches files ['**/*.{ts,tsx}'] with top-level ignores **/dist, **/.next, **/node_modules, **/public, **/.source; all 16 are inside it. (2) count from --format json: 'files judged: 16, errors: 0, warnings: 127', and exactly 1 of the 127 falls on a line this branch added (no-explicit-any in the pin, the idiom already on line 107 of that file); lint.yml deliberately sets no --max-warnings. (3) invariance: the config extends tseslint.configs.recommended, NOT recommendedTypeChecked, and declares no parserOptions.project or projectService, so no rule reads the type graph and this diff cannot move any untouched file's verdict. Repo-wide turbo run lint, full pnpm test and the rest of the gate farm are CI's. CI status at report time: in_progress — reported at draft-PR time as the contract requires, not waited on.",
      "mcp_calls": "8 — one issue_read get_comments to verify the claim comment names my branch (claim-bearing, so not read from the zero-quota payload channel), two search_issues (a positive control that returned #7741, then the dedup query for the finding), one issue_write to file #8151, one create_pull_request, one pull_request_read to read the PR body back in full, one add_issue_comment for this report, one issue_read to read this comment back. The card body, both its comments, and objectui#7740's body and triage comment were read through the zero-quota public-repo payload channel (hasNextPage false), because repo-scoped REST is 403 for this seat: the credential is alive (/rate_limit answers core 15000) but the session gate is closed, so the REST channel was probed once and abandoned per the channel table.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as objectui#8151: listViews in the same file carries the identical swallow — a refused or broken meta.getItems('view') returns the same [] as a served-empty list, so a lapsed token renders as 'this object has no saved views'. Unassigned and unlabelled for triage. Deliberately not fixed here: it needs its own reading of which failures stay quiet ('view' is a long-standing declared kind, so the older-server story does not apply), and folding it in would widen the published event union and add a verification surface this card's gate set does not cover — the bounded in-place exemption fails on that fourth condition."
      ],
      "deviation_stated_in_pr": "Triage requires this card be batched with objectui#7740; #7740 is needs-user-decision and unanswered, so the batch is unsatisfiable and the PM dispatched this card alone. The PR body states plainly that on sys_user this fix changes nothing (listImportMappings is not on that wrapper at all — #7740's spread bug), that it is nonetheless load-bearing for every other object including crm_plant_cost where the objectstack#14026 misdiagnosis happened, and that the symptom is NOT fully closed. packages/app-shell/src/views/identityImport.ts is untouched (0 hits in the diff) and #7740's design question is not decided under any framing."
    }

    Generated by Claude Code

  6. os-justin commented on Sep 6, 2026

    @os-justin
    Collaborator

    ⚠️ Additive correction to this seat's claim comment (5561826575): its Clause-② fence was drawn too narrow. ⛔ The claim above is left unedited.

    I wrote:

    Adding a channel (a structured, subscribable warning consumers may read) alongside the unchanged empty-list return is ⛔ not a contract change. Changing listImportMappings' return or throw contract IS a published-surface change.

    ⇒ that makes the return-or-throw signature the whole test. It is not. Clause-② covers changing contract accept/reject behaviour or widening the public face, and a channel is not exempt from the second — a channel is made of new exported symbols on a published surface.

    Measured on PR #8152's diff: four new exports on the published @object-ui/data-objectstack face (ImportMappingsFailureKind, classifyImportMappingsFailure, MetadataReadWarningEvent, MetadataReadWarningListener) plus a new public method ObjectStackAdapter.onMetadataReadWarning(cb), and three more on @object-ui/app-shell's face. ⇒ Clause-② is YES. needs:contract-review was attached to #8152, the review was performed in-seat at opus under the quota-exhaustion exemption (comment 5562131348), and the carrier was cleared with that comment as provenance.

    ⭐ The dev is not at fault and its determination is correct about what it claims — the return-or-throw contract genuinely does not move on any arm, and it built a real structural guarantee for that (every loud test re-asserts the [] return alongside the emitted event, so a future contract change cannot ride in disguised as a channel change). The fence that told it the signature was the only question was mine.

    Recorded here because the next seat inherits the fence, not the outcome: ⛔ do not repeat 「a channel is not Clause-②」 as a general rule. The right test is whether the diff adds or moves anything on a published face — a channel usually does.


    Generated by Claude Code

  7. os-justin commented on Sep 6, 2026

    @os-justin
    Collaborator

    os-dev-report

    {
      "issue": 7741,
      "status": "done",
      "branch": "claude/issue-7741-list-import-mappings-discriminate",
      "pr": "https://github.com/objectstack-ai/objectui/pull/8152",
      "premise_still_valid": true,
      "round": "patch round 1 — head moved f7d57b728 to 2f72dd701; supersedes the tests field of comment 5562114105, everything else in that report stands unchanged",
      "summary": "PATCH ROUND for the red Test (shard 4/4). scripts/__tests__/one-authority-per-exported-name-6273.test.ts failed because the new metadataReadWarningToast.ts copied its siblings' local 'export type TranslateFn' declaration along with the pattern, making a known three-way collision four (objectui#6172: every exported name has exactly one authority). Fixed by re-pointing, which the gate explicitly does not count as a declaration: the local declaration is replaced by 'import type { TranslateFn } from ./writeWarningToast.js' plus 'export type { TranslateFn } from ./writeWarningToast.js'. One file, three non-comment lines. NO KNOWN_COLLISIONS entry was added — the gate states that baseline is SHRINK-ONLY. The pre-existing three-way collision (writeWarningToast / saveAdvisoryToast / fields file-size-guard) is untouched; this only stops adding to it. Re-export rather than rename because the three existing declarations are character-for-character the same shape, so there is nothing to rename apart. Which one it points at was not a judgement made here, so Zone 1 point 3's stop-and-report condition did not fire: AdapterProvider — this module's only caller — ALREADY imports TranslateFn from ./writeWarningToast.js and passes that same value into all three emitters, and file-size-guard.ts's own declaration names app-shell/src/providers/writeWarningToast as 'the established TranslateFn pattern' it was copied from.",
      "clause_2_determination": "Unchanged from comment 5562114105: CHANNEL, not a contract change. This patch is type-only and does not touch data-objectstack at all.",
      "tests": "All on 2f72dd701. THE GATE AND ITS WHOLE NEIGHBOURHOOD: pnpm exec vitest run scripts/ (the repo-wide scope the first round missed) under the shared verify lock => VERDICT command-exit 0, 'Test Files 115 passed (115) / Tests 3408 passed (3408)'. Inclusion counted rather than assumed: find scripts -name '*.test.*' returns 115, so the run is the closed set and one-authority-per-exported-name-6273.test.ts is inside it. APP-SHELL UNCHANGED IN OUTCOME: pnpm --filter '@object-ui/app-shell^...' build && pnpm --filter @object-ui/app-shell type-check && pnpm exec vitest run packages/app-shell/ under the lock => VERDICT command-exit 0; the type-check step echoed 'tsc --noEmit && tsc -p tsconfig.test.json' and the && chain proceeded; 'Test Files 637 passed (637) / Tests 6120 passed | 1 skipped (6121)' — IDENTICAL to the pre-patch counts, with metadataReadWarningToast.test.ts and AdapterProvider.readWarningSink.test.tsx among them. NOT MEASURED, not red: the first attempt at that command exited 2 with about 200 'TS2307 Cannot find module @object-ui/*' — the worktree was rebuilt for this patch round and no dependency dist existed yet; PREREQUISITE NOT MET, re-run with the closure build in front, which is the green result quoted above. RUNTIME INERTNESS SHOWN, NOT ASSUMED, as Zone 3 asked: ts.transpileModule with isolatedModules true (the repo's own setting) over the file's before and after text emits 3586 bytes each, 0 diagnostics each, byte-identical output. A first attempt via esbuild is reported NOT MEASURED — the binary is absent in this container and pnpm exec wrote its own error text into both output files, which then compared equal for the wrong reason; caught before it was quoted as evidence. ALSO GREEN on the new head: eslint on the changed file ('files judged: 1, errors: 0, warnings: 0'), check:control-bytes, check:unreferenced-sources, check:i18n-keys, check:phantom-deps, check:self-import, node scripts/check-changeset-presence.mjs (17 files changed, 1 changeset). NOT RE-RUN by instruction: Live E2E (known base-red, objectstack#16186). Lock discipline this round: one acquisition returned exit 99 after a 540s budget against a 715s holder in objectui-7727; the interval went to lock-free work (eslint plus the node-script gates) and the retry reused OS_VERIFY_LOCK_SLOT=objectui-7741 so the ticket kept its arrival stamp, acquiring 150s later. No round was ended to wait.",
      "scoping_lesson": "The first round's TEST narrowing was 'the packages I edited' — data-objectstack, i18n, app-shell — which by construction cannot reach a gate that reads the whole repo from scripts/__tests__/. That round's LINT narrowing was defended with a population, a count and an invariance argument; the test narrowing had no equivalent, and that is exactly the gap this failure fell through. The repo-wide half is scripts/ — 115 suites that read the TREE rather than a package, covering exported-name authority, i18n key parity, changeset presence, dist completeness, turbo task inputs, workflow wiring, dependency edges and vite/vitest config. Any diff that adds an exported name, a source file, a locale key, a dependency edge or a workflow line is inside their reach no matter which package it lives in, so scripts/ belongs in the scope of any narrowed local run. It is cheap: 91 seconds under the lock, against the 15 minutes app-shell alone costs.",
      "mcp_calls": "11 cumulative — 8 in the first round (see comment 5562114105) plus 3 here: one add_issue_comment on PR #8152 describing the delta for the re-reviewing seat, one add_issue_comment for this report, one issue_read to read this comment back. No pull_request_read or update_pull_request this round: the PR body was deliberately NOT edited, because update_pull_request sends the draft bit on every call and the PR must stay draft.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as objectui#8151 in the first round: listViews in packages/data-objectstack/src/index.ts carries the identical swallow. Unchanged by this patch.",
        "NOT filed, reported here for the PM to route: TranslateFn is declared three times over (packages/app-shell/src/providers/writeWarningToast.ts:26, packages/app-shell/src/providers/saveAdvisoryToast.ts:43, packages/fields/src/widgets/file-size-guard.ts:26), all three character-for-character identical, and the objectui#6172 ruling says a name has exactly one authority. It is carried in the gate's KNOWN_COLLISIONS baseline, so it is already tracked there rather than invisible, and Zone 1 point 3 of this patch round explicitly forbade repairing it. Left un-filed to avoid a duplicate of whatever already tracks that baseline entry; PM may prefer a card that shrinks it."
      ],
      "deviation_stated_in_pr": "Unchanged from comment 5562114105 and still in the PR body: on sys_user this fix changes nothing (objectui#7740's spread bug), it is load-bearing for every other object including crm_plant_cost, and the symptom is NOT fully closed. packages/app-shell/src/views/identityImport.ts remains untouched."
    }

    Generated by Claude Code

  8. removed their assignment
    on Sep 6, 2026
  9. os-justin commented on Sep 6, 2026

    @os-justin
    Collaborator

    Landed as a915064ea via PR #8152. Closing; pm:dispatched stripped and the assignee cleared in the same stroke.

    Verified by CONTENT, against a control proven BEFORE the merge — ⛔ never by sha. packages/app-shell/src/providers/metadataReadWarningToast.ts is a new file, absent on main pre-merge:

    marker pre-merge (310c0ab19) post-merge (a915064ea)
    file present no yes (179 lines)
    import type { TranslateFn } from './writeWarningToast 0 1
    export type { TranslateFn } from './writeWarningToast 0 1

    A note on the instrument, because it nearly produced a wrong call. The obvious marker — grep -c 'type TranslateFn =' — reads 1 on the fixed head, which looks exactly like the collision was never repaired. It is a false positive: line 58 is a docblock that quotes the spelling (of their local \export type TranslateFn = …` came along with the pattern`). There is no second declaration. The count was only disambiguated by printing the matched lines, and the import/re-export pair are the markers that actually discriminate. Recorded here so the next person to verify this file does not repeat it.

    CI at the ready-flip: 32 check runs enumerated against total_count: 32 — 28 success, 3 skipped, 1 failure, zero in_progress, with the two I owed read explicitly: Test (shard 4/4) success (22:00:41Z) and Lint success (21:55:27Z); shards 1/2/3 green, Type Check green.

    The single failure was Live E2E (informational), stood down only after matching the known signature on this PR's own job (101564746184): Core service missing … auth + System started with degraded capabilities + no such table on all four sys_* + ✓ Server is ready (42 plugins, 132 seeded rows) + exit 1 + the artifact upload reporting exactly 1 file, which is the independent proof no Playwright output was produced.

    Correction carried forward from this card's review

    My original Clause-② fence on this card was drawn too narrow — I wrote that adding a channel "is not a contract change" and made the return-or-throw signature the whole test. Clause-② also covers widening the public face, and this diff added four exports plus a new public method. The carry-forward rule is "does the diff add or move anything on a published face", not "a channel is not Clause-②".

    And my review of the first round PASSed while missing the TranslateFn collision entirely — I tabulated the name in my own new-exports table and checked its Clause-② meaning, but never asked whether it was already declared elsewhere. One grep would have found three sites. The gate (one-authority-per-exported-name-6273) caught what I did not.

    Still open, filed by this card's dev rather than folded in: #8165 — TranslateFn is declared three times character-for-character identically, the cheapest available shrink of that gate's KNOWN_COLLISIONS baseline. (The three pre-existing authorities are grandfathered by that baseline deliberately; the gate is working as designed, which is why it caught the fourth and not the first three.)


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions