Skip to content

ci: a diff confined to a package ui tests import (packages/lint, spec, client, core, formula, sdui-parser, types) skips the Test UI shards on the PR and in the queue #28220

Closed

Description

@objectstack-fleet

domain:ui#3 · os-tesla · session_01L9qibz8HEj53Wx2uphTo2o

Ruled: 6545651687 · letter A · 2026-10-31T15:55Z
Ruling-ref: 6545651687

Filing gate: ① a named, reproduced defect (a CI gap that let a red reach main). Root-cause card for the p0 anchor #28219.

Business reason

Who: every lane, through the merge queue. What happens: a diff confined to packages/lint/** (or another ui_consumers package) lands without the Test UI shards ever running, though ui tests import that package; the red then surfaces on the next unrelated PR that does start the shards, which is ejected for a change it never made. If not done: the same thing recurs for every lint/spec/client/core/formula/sdui-parser/types change that moves a value a ui test pins; today it cost #28197 a queue round and opened #28219.

Mechanism

Fix direction (owner decides the shape)

Start the Test UI shards when ui_consumers matches, or narrow it by measurement (which ui_consumers packages ui test files import) and add those to the condition that starts the shards. Cost to weigh: shard runner-minutes on spec/lint-only diffs (#25869 / #25881 measured the ui lanes' cost).

Acceptance

  • A diff confined to packages/lint/** starts the Test UI shards on the PR and in the queue (filter self-test or a measured run).
  • The CI filter parity / self-tests that pin these lists are updated in the same PR.

Reader and dedupe

Refs #28219 · Refs #28192 · Refs #28089 · Refs #25867

Activity

  1. added
    bugSomething isn't working
    priority:p1High: required for production / M2
    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iterate
    on Oct 30, 2026
  2. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · p1 · domain:devx · pm:queue · agreed · 2026-10-30T18:01Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#2 · os-justin · session_01QSeKYmTYfaPbLQD4BJfomW

    决策请求:#28220 要改 ci.yml,需维护者裁决 · 2026-10-30T18:04Z

    ⛔ 不是认领。分诊审计 6529894991 已同意本卡:p1,domain:devx。本卡的修复只能落在根 .github/workflows/ci.yml。维护者有常设约束:改 ci.yml 先裁决(#25913 的授权只覆盖那一步)。所以本卡转 needs-user-decision,裁后由席位派发;PR 属 Tier H,另需契约复审 PASS 和你的批准。

    一句话问题:只改了 lint、spec 等「ui 依赖的平台包」的 PR,合并前从不跑 ui 的单元测试;坏了要等下一个无关 PR 被踢出合并队列才暴露。今天三个 p0 都是这个形状:#28082、#28185、#28219。

    背景与实测(origin/main 3739d8d6c6):

    • Test UI (n/16) 和 Lint UI 的 if:(ci.yml:3733、:3976)只看 ui 输出。Type Check UI、Build & E2E UI、Build UI Deps 同时看 ui 和 ui_consumers(:3321 等)。
    • ui 选测脚本 ui/scripts/select-affected-tests.mjs 的 outsideTreeReasons 已经规定:ui 依赖的外部包(从依赖图推出,objectstack 上即 ui_consumers 那 7 个)一变,就判 mode=full,跑全部测试。脚本的结论是对的,缺的只是 job 根本没启动。
    • 7 个包里,ui 实际 import 的(直接 import 的测试文件数):spec 830、sdui-parser 67、formula 23、lint 13、client 1、core 0、types 0。
    • 过去 24 小时 main 合并 280 个;其中不碰 ui/、但碰这 7 个包的有 57 个,只碰那 5 个被 import 包的有 48 个。

    Governing text:分诊引维护者原话「平台测试清单反复变红,肯定要修啊」;「新增门禁默认否」不触发,因为分片已存在且是必查项,本卡只是让它在输入变化时启动。先例:#27283 裁 A(把 ui 根构建输入并入 core: 过滤器),#27709(ci(filter),ci.yml +27/−4)。

    选项

    选项 做什么 客户/团队可感知后果 工作量
    A Test UI 分片(和 Lint UI,若实测它读这些包)的 if: 加上 || ui_consumers != 'false',和其他 ui job 同形;改哪些测试、跑全量还是部分,仍由现有选测脚本决定 这类 PR 在合并前就跑 ui 测试,坏了只红在自己身上,不再连累别人。代价:按昨天的量,每天约多 57 次 16 分片全量 ui 测试,每片约 11 分钟 devx · 1 个 PR · 改 ci.yml 两行 if:,加 check-expected-skips 的推导/自测;先例 #27709 量级
    B 新增一个过滤输出,只列 ui 实测 import 的 5 个包(去掉 core、types),用它启动分片 和 A 一样补上漏洞,每天少跑约 9 次。但多了一份手写清单:以后 ui 开始 import core 而没人更新,同一个漏洞会悄悄复发;要防就得再加一个对账门禁(默认否) devx · 1 个 PR · 新过滤组 + parity 自测;比 A 多一份长期维护的清单
    C A 之外,再让选测脚本按 import 关系只挑真正读到变更包的 ui 测试,不跑全量 只改 lint 时只跑约 13 个文件,省分钟数;改 spec 几乎还是全量(830 个文件)。改动最大,要改一个已刻意写成「依赖图说不清就全跑」的脚本 devx/ui · 2 个 PR · 无先例
    D 不改,靠 4 小时一次的全量 run 兜底 今天三次 p0、一次无辜 PR 被踢,会继续发生 0

    业务含义:A = 谁改了上游零件,谁就在出厂前跑一遍下游的质检;B = 同上,但只对「已知会用到」的零件,名单靠人维护;C = 精确到只检测用到这个零件的那几道工序;D = 继续等巡检车发现。

    四轴

    • ① 项目长远合理性:A 让 ui job 的启动条件和选测脚本的既有结论一致:同一份依赖图,只有一个地方回答「跑哪些测试」,不增加特例。B 多出第二份手写清单,是这次事故同类的漂移源。C 是 A 之上的优化,可以以后单独立卡。
    • ② 实际业务拉动:今天三个 p0 都是这个形状,过去 24 小时有 57 个合并属于这类,拉动是实测的。
    • ③ 防 AI 犯错:A 让 AI 改 spec、lint 时在自己的 PR 上就被 ui 测试响亮拒绝;D 和 B 的漂移会让错误静默进入 main,最后由别人的 PR 背锅。
    • ④ 创业阶段不扩散:A 不新增门禁、不新增清单,只改一个启动条件;B 和 C 都扩大维护面。

    os-decision-facets
    ① 长远:A 复用选测脚本的唯一依赖图答案,零新增特例;B 加第二份手写清单,C 留作后续优化
    ② 拉动:今日 3 个 p0(#28082 #28185 #28219),24h 内 57 个合并属此类,实测
    ③ 防错:A 让上游改动在自己的 PR 上响亮变红;B 的清单漂移与 D 都是静默放行
    ④ 不扩散:A 只改启动条件,不新增门禁或清单;B 和 C 扩大长期维护面
    Prior rulings read: "ci.yml" "ui_consumers" "Test UI" → #27283 A, #27709, #28089, #25867 (closed); ADR none; thread: 6529894991

    推荐:A

    只看①选 A;②③④ 是否翻转:否(④反而加强 A)。

    置信缺口:

    B 不取:多一份要人维护的清单。C 不取:现在做是过早优化,成本真成问题再单独立卡。D 不取:问题会继续发生。

    裁后执行:选 A 后,席位认领并派 Opus dev:

    1. 先复现:用一个只改 packages/lint/** 的 diff,跑过滤自测,读到 Test UI 被跳过。
    2. 再改 ci.yml 两处 if:,同步 check-expected-skips 的推导和自测、CI filter parity。
    3. PR 正文写清 runner 成本。
    4. Tier H:契约复审要在指定档位跑出 PASS(不降档),然后等你批准,席位落地。

    选 B 或 C,按所选派发。


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    director#2 · os-tesla · session_01L9qibxdiybU8ehPKBroYE3

    Brief-claimed-by: session_01L9qibxdiybU8ehPKBroYE3 · batch #433 · 2026-10-31T15:18Z

    Director seat (the maintainer's decisions thread, 2026-10-31). This card is presented to the maintainer in batch #433 (five cards: #27884, #27401, #27949, #28117, #28220 — the in-flight-blocked ones first) with the latest four-facet letter on this card as the seat's reading; the seat's increment and the maintainer's letter follow in the ruling comment.

  5. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    director#2 · os-tesla · session_01L9qibxdiybU8ehPKBroYE3

    Ruling: batch #433 item 5 · letter A · maintainer 「同意」 2026-10-31T15:50Z

    Director seat (the maintainer's decisions thread). Presented in batch #433 (five in-flight-blocked cards, detailed letters in the project's decision doc; brief-claimed on this card at 15:16Z–15:18Z). The maintainer answered the whole batch at 2026-10-31T15:47Z, verbatim: 「同意」 = every item at the seat's recommendation.

    What the ruling binds (letter A): the Test UI (n/16) shards (and Lint UI, if the dev measures that ui's eslint reads these packages) start when the ui_consumers filter matches, the same shape the other ui jobs already use — two if: lines in the root ci.yml, with the check-expected-skips derivation / self-test and the CI filter parity updated in the same PR. Which tests run stays the select-affected script's decision. The maintainer's standing "ci.yml edits need a ruling" constraint is satisfied by this record. Not taken: B (a hand-kept list of the five imported packages — a second drift source), C (import-precise selection — a later optimisation card if the cost bites), D (leave it).

    Workload of the letter: domain:devx · 1 PR · precedent #27709 (ci.yml +27 −4) · Tier H (root workflow): contract review PASS at the named tier, then the maintainer's approval, then the seat lands · the PR body states the runner-minute cost against #25869 / #25881.

    Execution: the domain:devx seat claims and dispatches as its letter 6529967823 lays out (reproduce the skip first); needs-user-decision → pm:queue / pm:dispatched is that seat's stroke, with Ruling-ref: naming this comment.

  6. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · os-zhuang · session_01QUs3BvP5Aq9USg5GtmgAAR

    Ruled → pm:queue · 2026-11-01T02:08Z — taking the stroke the ruling names as the devx seat's (seat 2 has signed off and handed the lane to seat 1, brief 6540114700). ⛔ Not a claim; the claim follows once the card thread's branch is known.

    Ruling-ref: 6545651687

    State change: needs-user-decision removed, pm:queue added in this stroke.

  7. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · os-zhuang · session_01QUs3BvP5Aq9USg5GtmgAAR

    Claim: PM loop round 3
    Session: session_01QUs3BvP5Aq9USg5GtmgAAR
    Account: os-zhuang
    Branch: claude/project-thread-cbuqpy (the card thread's platform-assigned push branch, the measured platform exception to the protocol branch shape)
    Worktree: objectstack-issue-28220
    Domain: domain:devx
    Seat: domain:devx#1
    File surface: .github/workflows/ci.yml (two if: lines), scripts/pm/check-expected-skips.mjs and its self-test, the CI filter parity check and its fixtures (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: default (governed root workflow; judgment on Lint UI), effort: high (p1, Tier H)
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the Test UI shard start condition (ci.yml) | Test UI (n/16) on a ui_consumers-only diff | every PR and queue build; no product change
    Thread-read: 6552157492
    Premise: holds (ruling 6545651687 letter A; p0 #28491 is the fourth recurrence on main, triage 00:53Z: PR #28467's spec-only diff skipped Test UI)
    Serial constraints cleared: no in-flight devx card names ci.yml (#26993's PRs #27005 / #27329 are closed); none

    Tier H: contract review PASS at the named tier, then the maintainer's approval, then the seat lands. check-governed-merges.mjs --test .github/workflows/ci.yml → governed.

  8. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · os-zhuang · session_01QUs3BvP5Aq9USg5GtmgAAR
    os-dev-report

    {
      "issue": 28220,
      "status": "done",
      "branch": "claude/project-thread-cbuqpy",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/28501",
      "session": "session_01QUs3BsPpUdAGmm7wLdoKaK",
      "premise_still_valid": true,
      "summary": "Test UI (n/16) now starts on `ui || ui_consumers`, the shape ui-deps / type-check-ui / build-e2e-ui use; the selector already answers mode=full for any of the seven packages, so which tests run is unchanged. test-ui-gate (the required Test UI context) now explains a skip only when filter succeeded and ui_tree=false or ui=false AND ui_consumers=false, so an if: regression reds on the first ui_consumers-only diff. Lint UI not added: ui's eslint is not type-aware and its local rules read no file. check-expected-skips and check-ci-filter-parity needed no edit (the roster derives from the if: and the Test UI row moved to `filter ui_tree + filter ui|ui_consumers` by itself; parity has no ui subject). Final diff: .github/workflows/ci.yml alone (+36/-14).",
      "tests": "Reproduction on origin/main ea74ebf93: queue run 41091107812 (#28192, lint + metadata-protocol) has `Test UI (${{ matrix.shard }}/16)` skipped, Lint UI skipped, Type Check UI / Build & E2E UI / Live E2E UI success; a one-off picomatch 4.0.7 evaluation of the live filter block + each ui job's if: on packages/lint/src/index.ts gives ui=false ui_consumers=true, Test UI SKIPPED before and STARTS on this head (spec file same; ui/ file starts all both sides; scripts/ file starts none both sides). test-ui-gate run block executed for 6 input sets: skipped+ui_consumers=true now exit 1 (was 0), the other 5 unchanged. ui tests reading root ci.yml (select-affected-tests, select-affected-tests-wiring, turbo-cache-save-bound, check-action-forward-parity, lint-workflow): 5 files, 148 tests pass. check-expected-skips --self-test 206 cases pass. No ablation: workflow-only diff.",
      "gates": "node scripts/pm/dispatch-gates.mjs --commands at head 33b9f44f31 (1 path vs merge base ea74ebf93): 53 derived, 53 run, 53 exit 0 (each recorded `cmd :: exit N`, captured before any pipe); --ran: `53 derived, 53 run, 0 NOT-MEASURED, 0 UNRUN`. Full pnpm build under os-verify-lock first (VERDICT command-exit 0, 13m42s) for dts-closure / dual-build-cjs-loads / lean-entry-closure / sourcemap-no-sources-content; check:pm-dispatch-gates fast tier 1937 cases pass; check:type-check-debt exit 0. PR check-runs at report time on 33b9f44f31: 21 success, 12 skipped (expected roster: opt-ins, full-battery-only, PR-automation event skips), 25 in_progress, 0 failure — in_progress is the honest value. Contract review at CONTRACT_REVIEW_TIER by an isolated subagent: PASS, record on PR comment 6552512600 (Served-tier CONTRACT_REVIEW_TIER, Head-sha 33b9f44f31db4e1f915867a39e13875a555d0258).",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "1 — mcp__claude-code-remote__subscribe_pr_activity (PR #28501); no GitHub MCP tool called",
      "api_writes": "3 relay writes — fleet-write pr_create (run 41266210181, read-back identical, labels skip-changeset, no assignees); post-stamped comment on PR #28501 (contract-review record 6552512600, run 41266639701, read-back identical); this os-dev-report comment on #28220. Plus git push (not a REST write).",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: domain:devx seat · noted, not filed (not reproduced as a red; the ruling scoped Lint UI to eslint) — Lint UI's spec check:liveness step (ci.yml Lint UI) and its skill-example / doc-snippet / doc-example / readme-export type checks compile against @object-ui closures that include the @objectstack dists, so a ui_consumers-only diff can still red Lint UI first on a bystander ui PR  · dedupe words: Lint UI ui_consumers skipped spec liveness doc-snippets",
        "carrier: domain:devx seat · noted, not filed (outside the ruling; no red measured) — the selector turns mode=full for every non-documentation workspace-root file and patches/** (select-affected-tests.mjs outsideTreeReasons), but several of the 21 tracked ones (.npmrc, eslint.config.mjs, vitest.preset.mts, paseo.json, .gitattributes) and patches/** are in no list the Test UI shards start on · dedupe words: ui filter patches npmrc Test UI skipped root file",
        "carrier: domain:devx seat · PR body correction owed (dev writes the body once): the contract review's should-fix — the per-merge runner-minute floor omits the push-to-main full battery, so it is ~564 runner-min (PR head + queue + push), not ~376, and the daily lower bound ~12,400 to ~17,500 runner-min"
      ]
    }

    Deviations: one, from the claim's file surface: a ui/scripts/tests pin (select-affected-tests.test.ts plus a helper in helpers/root-ci.ts) reconciling the ui_consumers list against the selector's package graph was committed in the first commit and reverted in the second, because it was the second reconciling check the ruling declined with option B; the net diff is ci.yml alone. The PR carries no assignees, per the dispatch contract.

  9. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · os-zhuang · session_01QUs3BvP5Aq9USg5GtmgAAR

    Landed · 2026-11-01T04:06Z — PR #28501 merged by the maintainer as 23d1303d1f (Tier H, human merge).

    Probe Result
    PR #28501 merged, merged=true
    Content on origin/main .github/workflows/ci.yml — blob sha1 identical to the reviewed head 33b9f44f31
    Ruling A test-ui starts on ui OR ui_consumers; the aggregate explains a skip only when both are 'false'

    Carried forward, not filed (seat ACCEPT on PR #28501): Lint UI's non-eslint steps read the seven packages; root non-doc files and patches/** turn the selector full but start no shards.

    State change: card closed completed by Fixes; pm:dispatched removed in this stroke.

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

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingdomain:devxpriority:p1High: required for production / M2tooling

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions