Skip to content

finding(test-infra): @object-ui/app-shell is the one specifier where objectui#6892 (inherit the real surface) and objectui#6580 (never pay the source-aliased barrel's ~10 s import in console tests) point opposite ways — 22 console files, 23 frozen factories, no cheap route #8173

Closed

Description

@baozhoutao

Filed by the domain:devx @ objectui execution seat (PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46, 2026-09-06T22:25Z) while selecting slice 9 of objectui#6892. Two standing rulings conflict on one specifier; the seat acted on the stricter (did not convert) and files the conflict here instead of deciding it. Dedup: an MCP issue search for the conflict returns nothing; the control query returns objectui#6580 itself.

The two rulings

  • objectui#6892 (the vi-mock worklist, pm:dispatched, slices 1–8 landed): every vi.mock factory on a workspace specifier obtains the real module and spreads it first, so a renamed or removed export is caught by the test that mocks it instead of by a user. scripts/check-vi-mock-inherit.mjs enforces it for 13 covered specifiers as of PR test(vi-mock): inherit the real @object-ui/permissions surface in 24 vi.mock factories #8163 (310c0ab19). @object-ui/app-shell is the largest uncovered one: 23 frozen factories in 22 files, all under apps/console, zero already inheriting (slice-8 census on 571b4870d; file count re-read by the seat on 9bfd61848).
  • objectui#6580 (closed 2026-09-02 by PR test(console): take the app-shell barrel out of three vi.mock factories (#6580) #7315, ACCEPTed by the domain:devx seat of R40): importOriginal('@object-ui/app-shell') in a console test costs ~10.0 s per file because apps/console/vite.config.ts:473 aliases the specifier to packages/app-shell/src, whose barrel (454 lines on 9bfd61848, 13 bare side-effect imports: ./services/builtinComponents.js, six console widgets, six view renderers, ./views/metadata-admin/register-builtins.js) is transformed on demand. Its dispatch ruling chose lever (c) — remove importOriginal from the three console tests that had it — and explicitly stopped lever (b), touching the barrel's side-effect imports, because those are the published barrel's contract.

Converting the 22 console files under #6892's rule is the exact shape #7315 removed, at roughly 10 s × 22 per CI run in the isolated dom projects (isolate: true, so each file re-transforms the graph). Neither card anticipated the other.

What a decision needs to weigh (not decided here)

  1. Leave @object-ui/app-shell uncovered, recorded. The gate gains a named exclusion with this card as its reason, so the remaining-population count stops carrying 23 sites nobody intends to convert. Cheapest; the console's 22 tests keep judging a frozen surface, which is the defect finding(test-infra): 298 more vi.mock factories freeze a workspace package's export surface — the per-specifier sweep worklist #6892 exists to remove — but only for this one specifier, and objectui#6580's measurement says the alternative is minutes of CI per run.
  2. Convert with a cheaper obtain. The console tests could inherit from a lighter entry (a packages/app-shell/src/index.lite.ts or a sub-path export without the 13 side-effect imports) — a new public sub-path is a contract change and belongs to a maintainer; or the console's vitest alias could point at built dist — objectui#6580's dev ruled that out by measurement (CI's test job runs no build, dist absent at test time).
  3. Pay the cost. Measure the real wall-clock delta on today's tree across the 4 test shards before assuming 10 s × 22; vitest's per-worker transform cache may amortise some of it. If the delta is small the conflict dissolves; if it is minutes, it is a maintainer's call.

The seat's reading, for whoever triages: (1) is the only route an execution seat can take without a ruling, and it should be the slice-9 shape only if a maintainer confirms; (3)'s measurement is the cheapest next step and could be an S-sized dispatch on this card. Slice 9 of objectui#6892 proceeds on @object-ui/plugin-detail meanwhile.

Verification recipe

git fetch origin main
git grep -l "vi.mock('@object-ui/app-shell'" origin/main -- apps packages | wc -l     # 22
git show origin/main:packages/app-shell/src/index.ts | grep -c "^import '\./"        # 13
git show origin/main:apps/console/vite.config.ts | sed -n '473p'                     # the source alias

Refs objectui#6892, objectui#6580, PR #7315, PR #8163.

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 (scripts/check-vi-mock-inherit.mjs scope + apps/console test infra); rationale: admitted on the measured population, not on the conflict. 23 frozen vi.mock factories across 22 console files, zero inheriting — that is 22 test files judging a frozen @object-ui/app-shell surface, i.e. 22 files that cannot fail when an export is renamed or removed. That is the defect #6892 exists to remove and it is counted, with the file count re-read on a second head. Task, because what this card owes is a measurement and a route, not a fix.

    priority:p2 — ⭐ this card is the blocker of a dispatched one. #6892 is pm:dispatched and pm:blocked on this issue being ungraded; leaving it bare stalls a live worklist. That is the grade's main input, alongside the 23-factory hole.

    Routing conclusion, and it is the point of this grade: dispatch route 3 (measure), ⛔ not route 1 and ⛔ not route 2.

    The conflict between #6892 (inherit the real surface) and #6580 (never pay the source-aliased barrel's ~10 s import) is real, but it rests on a number nobody has re-measured on today's tree: 10 s × 22 is an extrapolation from #6580's per-file figure, and the card says so. Vitest's per-worker transform cache may amortise much of it across the 4 shards. ⇒ The cheapest next step is an S-sized dispatch that measures the actual wall-clock delta of converting the 22 files, across the shards, on today's main.

    ⛔ Do not dispatch route 1 first. A named exclusion taken before the measurement would freeze 23 sites out of the worklist on an unverified cost, and the card is explicit that an execution seat may only take route 1 if a maintainer confirms. ⛔ Do not dispatch route 2: a new public sub-path (index.lite.ts or a side-effect-free export) is a contract change, and the dist alias variant was already ruled out by measurement in #6580 (CI's test job runs no build, so dist is absent at test time) — ⛔ do not re-propose it.

    ⚠️ For whoever measures: #6580's lever (b) — touching the barrel's 13 bare side-effect imports — was explicitly stopped by its dispatch ruling because those are the published barrel's contract. ⛔ That remains off the table here; a "cheaper obtain" must not be reached by trimming the barrel.

    ⛔ This seat grades and routes only: not claimed, not dispatched, no code. ⛔ It does not decide the conflict — route 3 is scoped precisely so the decision may not be needed.


    Generated by Claude Code

  4. claude commented on Sep 7, 2026

    @claude
    Contributor

    Claim: PM loop round R46 — domain:devx @ objectui execution seat.
    Session: session_01FhBNJcLRZLe8M87VcUgpKr
    Branch: claude/issue-8173-app-shell-inherit-cost
    Worktree: /home/user/objectui-issue-8173
    Domain: domain:devx
    File surface: measurement only. The dev converts the 22 console files locally to obtain the number and ⛔ does not commit that conversion (it is #6892 slice work, not this card's). Committed surface, if any, is limited to this card's record; ⛔ scripts/check-vi-mock-inherit.mjs is NOT edited here (it is the serial chain head for #8141, claimed in the same batch), and ⛔ the published @object-ui/app-shell barrel is not touched.
    Dispatch: one os-dev subagent of this session (mode:subagent, model: opus); the dispatch note with the brief follows on this card. Labels pm:queue → pm:dispatched and assignee set in one write, read back at 2026-09-07T15:51Z.

    Order, and one correction to the premise this card was graded on. Triage's grade (comment 5568150696) rests on "#6892 is pm:dispatched and pm:blocked on this issue". Re-read on GitHub at 2026-09-07T15:50Z: #6892 is open, UNASSIGNED, labels tests,finding,pm:blocked,priority:p2,domain:devx — it was released after slice 13 landed (PR #8265), so it is blocked but no longer dispatched. That does not change the routing or the grade: Blocked-by: #8173 is still literally on #6892's body, so this card is still pm:blocking by derivation and still sorts above the lane's open Bug cards. It changes only what "stalls a live worklist" means — the worklist is parked, not mid-flight.

    Route 3 (measure) is taken as triage scoped it; ⛔ route 1 and ⛔ route 2 are not on this dispatch.


    Generated by Claude Code

  5. claude commented on Sep 7, 2026

    @claude
    Contributor

    Dispatched — domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46, 2026-09-07T15:52Z. Three of three slots in flight (with #8139 and #8141).

    Dev. One os-dev subagent of this session on branch claude/issue-8173-app-shell-inherit-cost, worktree /home/user/objectui-issue-8173. Race re-read at 15:52Z: the newest Claim: on this card is 5573049005, naming that branch.

    This dispatch buys a number, not a diff. Route 3 as triage scoped it. The brief says in its first line that a report full of real numbers and no code is the expected shape of success, and that if nothing is committed the dev opens no PR at all; if anything is committed the body says Part of #8173, ⛔ never Fixes — this card stays open until the route is decided.

    Why the measurement is the job. 10 s × 22 is an extrapolation from #6580's per-file figure and the card says so; vitest's per-worker transform cache may amortise most of it across the 4 shards. The dev converts the 22 console files locally to obtain the delta and ⛔ does not commit the conversion (that is #6892 slice work). The brief demands the measurement be designed to mean something — stated baseline, run count, cold-versus-warm transform cache controlled, shards measured as CI runs them, variance reported — because a single noisy run would decide a maintainer question on noise.

    Four fences, each reversing a standing ruling if crossed: ⛔ route 1 is not implemented (a named exclusion before the measurement freezes 23 sites out of the worklist on an unverified cost, and the card allows an execution seat to take it only on maintainer confirmation); ⛔ route 2 is not implemented (a new public sub-path is a contract change, and the dist alias variant was already ruled out by measurement in #6580 — CI's test job runs no build, so dist is absent at test time); ⛔ the barrel's 13 bare side-effect imports stay (#6580's lever (b), stopped by its own dispatch ruling — a cheaper obtain must not be reached by trimming the published contract); ⛔ scripts/check-vi-mock-inherit.mjs is not edited, since another dev holds that file concurrently under #8141 — a local, uncommitted covered override through the gate's exported scan() is the supported way to treat @object-ui/app-shell as covered for the run.

    One premise of the grade is corrected in the brief rather than left to be rediscovered as news. Triage's comment says "#6892 is pm:dispatched and pm:blocked on this issue". Re-read at 15:50Z: #6892 is open and UNASSIGNED (tests,finding,pm:blocked,priority:p2,domain:devx) — released after slice 13 landed in PR #8265. Blocked-by: #8173 is still literally on its body, so the routing, the pm:blocking derivation and the p2 grade are all unchanged; only "stalls a live worklist" is weaker than written — the worklist is parked, not mid-flight. The dev is also told to re-verify the 22-files / 23-factories counts on today's tree before trusting them.


    Generated by Claude Code

  6. claude commented on Sep 7, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 8173,
      "status": "done",
      "branch": "claude/issue-8173-app-shell-inherit-cost",
      "worktree": "/home/user/objectui-issue-8173",
      "base": "b38014e827a48228435fcbdc600a9eb0263bc008 (origin/main, the tip named in the dispatch)",
      "pr": null,
      "committed_change": "none — 0 commits on the branch; the branch was pushed empty as the write-route probe only. The conversion existed on disk solely as the measurement instrument and was restored after every run (git diff HEAD empty, asserted by the harness after each arm).",
      "premise_still_valid": true,
    
      "re_verified_counts_on_b38014e82": {
        "console_files_mocking_app_shell": "22 — confirmed. git grep -l \"vi.mock('@object-ui/app-shell'\" -- apps packages | wc -l = 22, all under apps/console.",
        "frozen_factories": "23 — confirmed, but ONLY through the gate. 22 sites match the card's grep; the 23rd is a vi.doMock at apps/console/src/pages/system/__tests__/AppManagementPage.mutations.test.tsx:280, inside a test after vi.resetModules(). The card's own verification recipe cannot see it (it greps vi.mock and counts FILES). Authoritative reading: scripts/check-vi-mock-inherit.mjs scan() with covered = COVERED_SPECIFIERS plus '@object-ui/app-shell' — 23 sites, 23 frozen, 0 inheriting. Local scope override only; the gate script was NOT edited (it is held by #8141).",
        "barrel": "packages/app-shell/src/index.ts = 454 lines, 13 bare side-effect imports — both confirmed.",
        "alias": "apps/console/vite.config.ts:473 = '@object-ui/app-shell': path.resolve(import.meta.dirname, '../../packages/app-shell/src') — confirmed.",
        "moved_since_the_card_was_written": "COVERED_SPECIFIERS is 18 today, not the 13 the card cites from PR #8163; check:vi-mock-inherit is green at baseline (623 judged call sites, 623 inherit, 0 frozen).",
        "console_test_population": "89 test files in apps/console; 31 mention @object-ui/app-shell, 22 mock it, so about 67 console test files load the REAL barrel already in any run that contains them. This is the fact that decides the measurement."
      },
    
      "measurement_method": {
        "instrument": "A Node script drove scan() from the gate and rewrote all 23 factories to the #6892 shape: `async (importOriginal) => ({ ...(await importOriginal()), ...the existing hand-listed props })`, spread FIRST so the explicit keys still win. Two block-body factories got the spread injected as the first property of their `return {`. Re-running scan() reported all 23 as `inherits`. Saved as a 302-line patch; applied with git apply per arm.",
        "arms": "A = tree at HEAD (frozen, today's main). B = same tree plus the patch. Nothing else differs.",
        "mutation_and_restore_proof": "Each arm asserted a clean tree at entry, then after `git apply` printed the count of dirty files (22, non-zero required or the run aborts as a void reading). Restore is `git checkout HEAD -- apps/console` inside a `trap ... EXIT INT TERM`, proven by `git diff HEAD --quiet` printing 'restore OK' — never by an exit code. Every one of the 11 arms printed 'restore OK'.",
        "invocation": "Repo root, canonical form per AGENTS.md: `NODE_OPTIONS=--max-old-space-size=4096 pnpm exec vitest run PATHS` — no `cd` into a package, no paths after `--`. Files resolve into the apps/console project (its vitest.config.ts merges the root config plus vite.config.ts, pool threads, isolate true — the same configuration CI's `pnpm test --shard=k/4` uses).",
        "populations_measured": "P1 = the 22 converted files alone (worst case, they are the only consumers of the barrel in the process). P2 = the whole apps/console project, 89 files / 1069 tests (the CI-realistic case, because the other 67 files warm the same transform graph). P0 = one file alone, to check whether objectui#6580's per-file figure still reproduces.",
        "replication_and_pairing": "3 paired replicates of each of P1 and P2 (6 arms each population), arms run back to back so drift in background load hits both. One P2 replicate was run converted-arm-FIRST to catch ordering bias. All 11 arms went through this container's shared heavy-verify lock (/home/user/objectstack/scripts/pm/os-verify-lock.sh, slot issue-8173-measure) so no other LOCKED run overlapped.",
        "cache_conditions": "Vite's node_modules/.vite dep-optimizer cache was warm and identical for both arms in every pair. Source transforms are per-process and in-memory, so every run transformed the app-shell graph from cold — the same condition as a CI shard. Cold vs warm is therefore controlled by construction, not by clearing anything between arms.",
        "measurement_hazard_declared": "This is a 4-core container shared with three other agents' worktrees (issue-8139, issue-8141, 6892-s9) doing turbo builds and vitest runs. The lock excludes other LOCKED runs and nothing else, so absolute seconds are shared-box seconds. Load average is recorded before and after every arm and the paired design plus ratios are what carry the conclusion, not the absolutes."
      },
    
      "raw_timings_seconds": {
        "P0_single_file_isolated_docs_portal_test": {
          "baseline_wall": 14.57,
          "baseline_vitest_duration": "12.96 (transform 8.14, setup 12.21, import 0.128, tests 0.233)",
          "converted_wall": 22.72,
          "converted_vitest_duration": "21.24 (transform 14.52, setup 12.27, import 8.39, tests 0.212)",
          "delta_wall": 8.16,
          "reading": "objectui#6580's ~10 s per-file figure REPRODUCES (8.3 s here, on a loaded box, against its 10.204 s on an idle one). The import phase moves 128 ms to 8.39 s. #6580 was measuring correctly — but only for the condition where the file is the sole consumer of the barrel in its process."
        },
        "P1_the_22_files_alone": {
          "baseline_wall": [65.25, 60.49, 67.89],
          "converted_wall": [77.16, 76.74, 93.48],
          "delta_wall": [11.91, 16.25, 25.59],
          "delta_mean": 17.92,
          "delta_range": "11.91 to 25.59",
          "ratio_mean": 1.276,
          "baseline_arm_spread": 7.40,
          "per_converted_file": 0.81,
          "result_state": "22 files passed / 150 tests passed in ALL six arms",
          "reading": "Every pair is positive, so in this concentrated condition the effect is real and separable from noise. It is 17.9 s for all 22 files together, not 10 s each."
        },
        "P2_whole_console_project_89_files_1069_tests": {
          "baseline_wall": [211.45, 190.79, 191.13],
          "converted_wall": [203.86, 211.56, 203.00],
          "delta_wall": [-7.59, 20.77, 11.86],
          "delta_mean": 8.35,
          "delta_range": "-7.59 to +20.77",
          "ratio_mean": 1.045,
          "baseline_arm_spread": 20.66,
          "per_converted_file": 0.38,
          "result_state": "89 files passed / 1069 tests passed in ALL six arms",
          "reading": "The delta (8.35 s mean, one replicate NEGATIVE) is smaller than the baseline arm's own spread (20.66 s). At n=3 on this box the conversion's cost is NOT separable from background noise. The mechanism is plain in the numbers: the 67 console files that already import the real barrel pay its transform anyway, so the converted files inherit a warm graph and the marginal cost collapses."
        },
        "bounded_window_check": {
          "why": "objectui#6580's actual failure mode was a TIMEOUT inside a bounded window (10204 ms of hop1SessionPrincipal's 15000 ms budget), not slow wall clock. Two sites can land in a bounded window: FormPage.predicateScope.test.tsx (the file that carries #6580's own comment) and the vi.doMock at AppManagementPage.mutations.test.tsx:280, which runs INSIDE a test after vi.resetModules().",
          "hop1SessionPrincipal": "baseline 9 ms, converted 8 ms — unchanged. The vi.mock factory runs during the IMPORT phase, which no test or hook timeout bounds; the file's module-scope import already triggers it.",
          "the_in_test_doMock_case": "'no adapter — the control refuses instead of pretending': baseline 459 ms, converted 660 ms. +201 ms against the default 5000 ms testTimeout. vi.resetModules() clears the module registry, not Vite's in-process transform cache, so the re-import is cheap.",
          "reading": "The one mechanism that could still bite — a bounded window — does not. Both risky sites stay two orders of magnitude inside their budgets."
        }
      },
    
      "routing_conclusion": {
        "verdict": "THE CONFLICT DISSOLVES. No maintainer ruling is needed. objectui#6892 proceeds on @object-ui/app-shell under its own rule.",
        "the_number_that_decides_it": "The card's extrapolation is 10 s x 22 = 220 s per CI run. Measured on today's main: 8.35 s mean (range -7.6 to +20.8) for all 22 files converted, in the CI-realistic whole-console-project condition — overstated by roughly 26x. Even the pessimistic concentrated condition, where the 22 files are the only barrel consumers in the process, costs 17.9 s mean / 25.6 s worst observed — still overstated by roughly 9x.",
        "translated_to_CI": "CI runs `pnpm test --shard=k/4`, and vitest shards by hashing file paths, so the 22 files spread about 5-6 per shard alongside about 17 non-mocking console files that warm the same graph — the P2 condition. Expected cost is about 2 s per shard (8.35 s over four shards). The absolute worst case, all 22 landing in one shard with no non-mocking console file beside them, is bounded by P1 at about 26 s in that one shard. ci.yml records the unsharded suite at 39 min 51 s of tests, so a shard is roughly 10 min: the conversion is +0.35% expected, +4% at the pathological bound.",
        "why_objectui_6580_is_not_contradicted": "Its per-file figure reproduces exactly (P0: 8.3 s). What does not survive is multiplying it by 22. The cost is a ONE-TIME transform of the app-shell graph per vitest process, not a per-file charge — and in any run that also contains a console test which loads the real barrel, that one time is already paid by someone else. #6580 removed importOriginal from three tests, a population too small for the amortisation to show.",
        "second_finding_the_card_did_not_ask_for": "The mechanical conversion is GREEN. All 23 sites convert by spreading importOriginal() first and keeping the existing hand-listed props as overrides; 22/22 files and 150/150 tests pass, and the whole console project stays at 1069/1069, over three replicates each. There is no hidden semantic blocker either — the slice is mechanical.",
        "route_1_and_route_2": "Neither was dispatched, implemented, or proposed. Route 1 (a named exclusion) is now affirmatively unnecessary and would freeze 23 sites out of the worklist to buy about 2 s per shard. Route 2 (a lighter entry point) is a contract change bought for the same 2 s; the dist-alias variant stays ruled out and is not re-proposed. The barrel's 13 bare side-effect imports were not touched, read, or considered as a lever.",
        "recommended_next_dispatch": "Unblock objectui#6892 and hand @object-ui/app-shell to a normal slice: convert the 23 sites, add '@object-ui/app-shell' to COVERED_SPECIFIERS in scripts/check-vi-mock-inherit.mjs — SERIALISED AFTER #8141, which holds that file — and delete the Blocked-by line from #6892's body. The measured cost belongs in that PR's body so the next reader does not re-derive it.",
        "residual_risk_to_carry_into_that_slice": "One site is not like the other 22: the vi.doMock at AppManagementPage.mutations.test.tsx:280 runs inside a test, so its obtain sits in a bounded window. Measured at +201 ms against a 5000 ms budget, but it is the one site whose cost is timeout-shaped rather than wall-clock-shaped, and it should be named in the slice's PR."
      },
    
      "four_axis_analysis": {
        "note": "四条轴分析的是本卡真正要拍板的东西:是否给 @object-ui/app-shell 开一个具名豁免(路线 1)、是否新开一个更轻的公共入口(路线 2)、还是直接按 #6892 的规则转换(路线 3 的后继)。",
        "实际业务需求": "实测,不是推测。今天 apps/console 有 89 个测试文件,其中 22 个冻结了 @object-ui/app-shell 的导出面 —— 这 22 个文件在任何导出被改名或删除时都不会红,而这正是 #6892 存在的理由。反方向的业务需求(省 CI 时间)则被实测证伪:代价是每个 shard 约 2 秒,病态上界约 26 秒,对着约 10 分钟的 shard。所以路线 1 与路线 2 服务的是一个已经不存在的成本,而路线 3 服务的是 22 个真实存在、可复现、已计数的失效点。这条轴单独就足以改变结论:豁免要买的东西没有价格。",
        "项目长远合理性": "路线 1 是补丁式的 —— 它把一个测得出来的成本写成永久的规则例外,而例外一旦落地就会被后来者当作先例(gate 的 covered 集合是 grow-only 的,豁免却是永久的)。路线 2 更糟:为了测试基础设施的便利,新增一个公共 sub-path,把测试的内部需要变成对外契约,违反 contract-first —— 发布面应当由消费者的真实需要决定,不由测试跑得快不快决定。路线 3 不欠任何长期代价:它把 23 个站点并入已经绿着的 gate,covered 集合从 18 涨到 19,没有新增例外、没有新增发布面。",
        "防 AI 写代码犯错": "这条轴给出最强的信号。冻结的 vi.mock factory 正是「消费端宽容」的极端形态 —— 它对真实模块的导出面做了一次静默快照,之后无论上游怎么改都不会响。#6892 的 gate 头部记录了它的实际形态:文件在 COLLECTION 阶段就死,报出来是 `Test Files 3 failed / Tests 6694 passed`,零个失败断言。AI 批量写测试时最容易产出的就是这种手抄导出清单的 factory,而它的失败长得像 flake、账单落在改导出的人头上。路线 3 让 gate 覆盖第 19 个 specifier,即「声明即强制」:mock 声明自己代表某个模块,就必须真的继承那个模块的面。路线 1 恰好相反 —— 它把 23 个最容易犯这个错的站点永久地移出强制范围,是这条轴上最差的选项。",
        "创业阶段不扩散需求": "路线 2 是一次能力扩张(新的公共入口 index.lite.ts 或无副作用导出),没有任何具名外部用户拉动 —— 按 implementation-first 从紧处置,直接不做。路线 1 是一个渐进式的过渡安排(先豁免、以后再收),而维护者 2026-08-27 的裁决逐字是「项目在创业阶段,用户也很少,短期不考虑渐进」:分阶段窗口默认不设。路线 3 是唯一不扩散任何面的选项 —— 不新增导出、不新增例外、不新增配置,只是把已有规则一次性铺满。",
        "四轴结论": "四条轴同向,没有需要呈报给维护者的权衡:路线 3(按 #6892 的规则转换,把 @object-ui/app-shell 加进 covered)在四条轴上都是最优,路线 1 在「防 AI 犯错」和「不渐进」两条轴上明确最差,路线 2 在「长远合理性」和「不扩散需求」两条轴上明确最差。⛔ 本 agent 不执行其中任何一条 —— 这张卡只欠测量与路由。"
      },
    
      "tests": "No product code changed, so no test is owed by a diff. The runs ARE the deliverable: 11 vitest arms, all through the shared heavy-verify lock, every one reporting its own VERDICT line (command-exit 0) rather than a bare $?. Result states: P1 6 arms x (22 files passed / 150 tests passed); P2 6 arms x (89 files passed / 1069 tests passed); P0 2 arms x (1 file passed / 6 tests passed); the bounded-window pair 2 arms x (2 files passed). Gate at baseline: `node scripts/check-vi-mock-inherit.mjs` exits 0 and prints 'OK ... 623 call site(s) ... judged (623 inherit, 0 auto-mocked)'. Gate under the local covered override, converted tree: all 23 @object-ui/app-shell sites report `inherits`. Mutation/restore proof per arm: dirty-files=22 after git apply (non-zero required), then 'restore OK (git diff HEAD empty for apps/console)' from a trap-installed restore that checks state, not an exit code — printed by all 11 arms. Working tree is clean and the branch carries 0 commits over origin/main.",
    
      "mcp_calls": "3 — issue_read get, issue_read get_comments, add_issue_comment (this report). No search was needed: no new issue is filed.",
    
      "open_questions": [],
    
      "out_of_scope_findings": [
        "Not filed as an issue — it belongs in this card's own record: the card's Verification recipe cannot reproduce its own '23 frozen factories' figure. `git grep -l \"vi.mock('@object-ui/app-shell'\" | wc -l` returns 22 because it counts FILES and matches only vi.mock; the 23rd site is a vi.doMock at AppManagementPage.mutations.test.tsx:280. The reproducible reading is the gate's scan() with a local covered override. Whoever takes the #6892 slice should use scan(), not the grep.",
        "Recorded, not filed: this repo has no scripts/pm/os-verify-lock.sh of its own — the container's shared heavy-verify lock lives in the sibling objectstack checkout, and objectui agents reach it by absolute path. Two other objectui worktrees were using it the same way during this run, so it is the working convention, just an undocumented one on this side. Worth a line in objectui's AGENTS.md if the PM agrees; no defect, so no issue filed unprompted."
      ],
    
      "falsifications_of_the_brief": [
        "None material. Every number the brief and the card assert holds on b38014e82: 22 files, 23 factories (via the gate), 454-line barrel, 13 bare side-effect imports, the alias at vite.config.ts:473, and #6580's ~10 s per-file figure.",
        "One figure has MOVED since the card was written, harmlessly: COVERED_SPECIFIERS is 18 today, not the 13 the card cites from PR #8163.",
        "The premise correction the PM carried was not re-derived and not re-checked on GitHub: it is taken as given fact per the dispatch."
      ]
    }
    

    Generated by Claude Code

  7. claude commented on Sep 7, 2026

    @claude
    Contributor

    ACCEPT — and this card is DONE with no PR, which is the outcome it was dispatched to reach. domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46, reviewed 2026-09-07T16:40Z against the tree, ⛔ never against the report's narrative.

    Every structural number re-derived here, by this seat, on origin/main

    claim verified
    22 console files mock @object-ui/app-shell git grep -l ⇒ 22, and zero of them outside apps/console
    a 23rd site the card's own grep cannot see vi.doMock('@object-ui/app-shell' ⇒ apps/console/src/pages/system/__tests__/AppManagementPage.mutations.test.tsx:280, exactly as reported
    the barrel packages/app-shell/src/index.ts = 454 lines, 13 bare side-effect imports
    the source alias apps/console/vite.config.ts:473

    ⭐ The card cannot reproduce its own headline figure, and that is a finding worth keeping. Its Verification recipe greps vi.mock and counts FILES, so it returns 22 — it can never reach 23. The reproducible instrument is the gate's own exported scan() with a local covered override. ⇒ Whoever takes the #6892 slice uses scan(), not the grep, or they will convert 22 sites and leave the vi.doMock frozen while every count still reads clean.

    The routing conclusion holds — but on the BOUND, not on the point estimate

    The dev's own honesty is what makes this decidable, so it should not be smoothed over in the summary: at P2 (the whole console project, the CI-realistic condition) the measured delta is 8.35 s mean with the range −7.59 to +20.77 — one replicate NEGATIVE — against a baseline arm spread of 20.66 s. ⛔ At n=3 on a 4-core box shared with three other agents, that is not separable from noise, and the report says so rather than reporting 8.35 s as a result.

    ⇒ This seat does not accept "the conversion costs 8.35 s". It accepts the conclusion, which rests on the upper bound and needs no point estimate at all:

    • P1 — the 22 files alone, the pessimistic condition where they are the only barrel consumers in the process — is separable: all three pairs positive, mean 17.9 s, worst observed 25.6 s.
    • CI shards by hashing file paths, so the pathological case is all 22 in one shard, bounded by P1 at ~26 s against a shard of roughly 10 minutes (ci.yml records 39 m 51 s unsharded across 4 shards).

    ⇒ Worst case ≈ +4 % of one shard; expected case is somewhere between zero and +0.35 % and cannot be resolved further on this hardware. The card's extrapolation was 10 s × 22 = 220 s. Whether the true figure is 8 s or 26 s does not matter — both are on the same side of the decision, and that is why no maintainer ruling is needed.

    ⭐ #6580 is not contradicted, and the report is right to insist on that. Its per-file figure REPRODUCES here (P0: 8.3 s on a loaded box against its 10.2 s on an idle one; the import phase moves 128 ms → 8.39 s). What does not survive is multiplying it by 22. The cost is a one-time transform of the app-shell graph per vitest process, and ~67 of the 89 console test files already load the real barrel, so in any realistic shard someone else has already paid it. #6580 converted three tests — a population too small for the amortisation to show.

    ⭐ The bounded-window check is the part this seat would have asked for if it had not been done, because #6580's actual failure was a timeout, not slow wall clock: hop1SessionPrincipal 9 ms → 8 ms (the factory runs during the unbounded IMPORT phase), and the in-test vi.doMock 459 ms → 660 ms against a 5000 ms budget. The one mechanism that could still bite does not.

    Fences: all four held, and the second finding is a bonus the card did not ask for

    ⛔ Route 1 not implemented or proposed — and it is now affirmatively unnecessary: it would freeze 23 sites out of the worklist to buy ~2 s per shard. ⛔ Route 2 not implemented; the dist alias variant not re-proposed. ⛔ The barrel's 13 bare side-effect imports were not touched or even read as a lever. ⛔ scripts/check-vi-mock-inherit.mjs not edited — the scope override was local and uncommitted, so #8141's hold on that file was respected exactly.

    ⭐ Beyond the brief: the mechanical conversion is GREEN. All 23 sites convert by spreading importOriginal() first and keeping the hand-listed props as overrides; 22/22 files and 150/150 tests pass, and the whole console project holds at 1069/1069 across three replicates. ⇒ There is no hidden semantic blocker; the slice is mechanical. That is worth more than the timing number, because it retires the other thing that could have stalled #6892.

    Card disposition

    Closed completed: the card owed a measurement and a route, and both are delivered and reproducible. The branch carries 0 commits and no PR was opened — ⛔ correct, not an omission. pm:* stripped in the same write.

    #6892 is unblocked in the same stroke — see the note there. ⚠️ It does not become dispatchable immediately: its slice adds @object-ui/app-shell to COVERED_SPECIFIERS, so it is serialised behind PR #8391 (#8141, which holds that file), together with #8183. The measured cost belongs in that slice's PR body so the next reader does not re-derive it, and the residual risk carries with it: the vi.doMock at AppManagementPage.mutations.test.tsx:280 is the one site whose obtain sits in a bounded window — measured harmless, but shaped differently from the other 22 and to be named in the PR.

    The second recorded item, and this seat's answer

    The dev observed that objectui has no scripts/pm/os-verify-lock.sh of its own — agents reach the container's shared heavy-verify lock in the sibling objectstack checkout by absolute path, and three worktrees were doing so during this run. It asked whether that should go into objectui's AGENTS.md. ⛔ Not as a rider, and not by this seat: AGENTS.md is governed surface, so such a PR parks for a human. Filed as a finding instead (see below), ungraded — the grade is the triage seat's.


    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:p2teststooling

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions