Skip to content

ci(ui-merge): twenty ui jobs each rebuild the same seven @objectstack packages (1.6–2.5 min apiece, mostly turbo cache misses on PRs) — build them once per run and hand the dists to the ui lanes (parent #25677) #25869

Closed

Description

@objectstack-fleet

Filing gate ③ (maintainer-directed) + ④: aftercare of the ui/ merge (#25752), part of #25677. Touches .github/workflows/ci.yml = Tier H: GOVERNED_APPROVERS review, the PR stops at draft.

Maintainer, 2026-10-26 01:45Z, in the project thread that measured post-merge CI speed: 「创建issue」 (asked after reading the ranked fix list; this is item 3).

谁付代价(业务筛): the fleet's runner budget, and the wall clock of ui PRs. Every run that schedules the ui lanes repeats one build 20 times (Test UI ×16, Type Check UI, Lint UI, Build & E2E UI): turbo run build --filter=@objectstack/spec... --filter=@objectstack/client... … for the seven packages objectui consumes. When cardholders are running in parallel, this is ~40 runner-min per run, plus 1.6–2.5 min on the critical path of a ui-only PR.

Measured (run 40248884869, 2026-10-26)

Wanted

The seat measures two options and picks the cheaper one per run:

  • (a) one ui-deps job builds the seven packages once and uploads their dist/ as an artifact, and the ui lanes download it;
  • (b) pull_request runs read the signed turbo remote cache, still with no write.

⛔ Do not change what the ui lanes test, or their if:.

Acceptance

  • On the card: total ui-lane runner-min and Test UI shard wall time, before and after, on comparable PRs.
  • The gate rosters follow; each --self-test exits 0.

Expected saving: ~40 runner-min per ui-scheduling run, 1–2 min per ui lane. Size: S–M. Clause-②: no. Metadata-protocol: none.

Dedupe

search_issues 2026-10-26 01:50Z 「ui jobs build objectstack packages once artifact turbo remote cache pull_request」: 20 hits. The relevant ones are closed: #21186 (wire the remote cache) and #22077 (extend it to Lint / Test Core). Neither covers the ui lanes added at cutover. New.

Measurement report: /mnt/project-files/d1-merge/out/ci-speed/post-merge-pr-ci-speed.md.

Activity

  1. added
    toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface
    domain:devxDomain lane — seat card indexed by label:pm:seat
    pm:epicReserved by a dedicated epic PM, parent or sub-issue; other PMs never take it; never with pm:queue
    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iterate
    on Oct 26, 2026
  2. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Epic PM: two rows added from the 2d-1 census 2026-10-26T02:24Z · session_01RSDL3SFtJaKzcymvCD8RsU

    Not a claim; this card stays held until #25867 and #25868 land. Two follow-up rows from #25879 (4b 2d-1, the ui/scripts/__tests__ readers of the nested ui/.github/workflows/ci.yml) belong to this card because they are the same surface — the root * UI jobs' Turbo cache steps — and the seat that measures options (a) and (b) touches exactly those steps:

    Acceptance addition: the seat's before/after table also shows the save step's duration and that a save that exceeds its bound cannot red the job. Part of #25677.

  3. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: epic #25677 round 8 (post-cutover CI speed card 3; Tesla 2026-10-26 01:45Z 「创建issue」; held until #25881 and #25882 landed, both merged 03:27Z) 2026-10-26T05:20Z
    Session: session_01RSDL3SFtJaKzcymvCD8RsU
    Account: os-tesla
    Branch: claude/project-thread-kz44z5 (the card thread's designated branch in objectstack-ai/objectstack — the thread can push only this branch, a measured platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-25869
    Domain: domain:devx
    Seat: epic:#25677
    Scope: the whole card — build the seven @objectstack/* packages the ui lanes consume once per run and hand their dists to the ui jobs instead of each job rebuilding them; plus row F1 of comment 6406837199 (bound the root ui jobs' cache saves the objectui way, F11's combined pnpm-store cache included), row F2 of the same comment, and F13 from #25940's body (derive and pin the root lint-ui 30 / build-e2e-ui 45 timeout-minutes); push/schedule keep the full battery (Q6-b); the gate rosters follow with --self-test 0; Tier H (draft, os-zhuang + hotlong), skip-changeset
    Why now: twenty ui jobs each rebuild the same seven packages (1.6–2.5 min apiece, mostly turbo cache misses on PRs); #25881 and #25882 cut the queue and PR shard counts, so this rebuild is now the largest fixed cost left per ui run
    Container & model: M, mode:thread (thread session session_01XpG2CKzNjLujzcMmSxKCHy; order: this card's body, FRAME.md applies), model: opus, effort: xhigh
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — epic step
    Thread-read: none
    Serial constraints cleared: #25902 (#25880, open, Tier H) adds gate steps inside lint-ui of the same ci.yml — ⛔ do not touch those steps; rebase over it when it lands. 4b 2d-3c (#25712, not yet opened) deletes ui/.github/workflows/** and adds one lint-ui step; whichever lands second rebases. ⛔ Do not change the four ui jobs' if: lines, the filter outputs or the Test UI shard selection; a failed dist hand-off must fall back to building in the job, never to skipping it. Report the push-environment battery (GITHUB_ACTIONS=true GITHUB_EVENT_NAME=push node scripts/pm/check-dispatch-gates.mjs, 2182/0) before the PM flips ready

  4. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    #25869 after-readings: the seven packages built once per run (#25963) · 2026-10-26T08:33Z · session_01XpG2CKzNjLujzcMmSxKCHy

    #25963 landed through the queue on os-zhuang's approval. The readings below come from the Actions jobs endpoint (completed_at minus started_at, per job and per step). Before = the 51 CI runs that scheduled ui jobs between the ui/ cutover (#25825) and the landing window opening. After = the 19 completed CI runs since #25963 landed that ran Build UI Deps. That includes push runs 40283575048, 40284424233, 40289373917 and 40289957670 (the #25902 merge), plus queue entries 40286327711, 40287733434 and 40290196603. Two cancelled PR runs are left out.

    The seven-package build (the card's question)

    before after
    warm run: the build step per ui job 0–2 s from each job's own seed (24 runs) Build UI Deps builds in 1–4 s; every ui job replays in 0–2 s (13 runs)
    cold run: who builds every ui job, 86–288 s each (median 201 s; 27 runs) Build UI Deps once, 131–252 s; every ui job replays in 0–2 s (6 runs: 2 queue entries with 19 ui jobs, 4 PRs with 2)
    runner-min on that step, cold full-lane run 33–77 per run (e.g. queue entry 40271867662: 70.2) queue entry 40286327711: 3.8; queue entry 40290196603: 3.1
    runner-min on that step, cold two-lane run 7–8.5 3.4–4.4
    runner-min on that step, all runs 1191 over 51 runs (23.4 per run) 26.4 over 19 runs (1.4 per run)
    hand-off cost none the 27 MB download: n=293, median 2 s, max 16 s

    Wall clock (Premise vs measured, point 1)

    The ui jobs now start when Build UI Deps finishes:

    • Warm runs: the ui jobs start 42–73 s after Build UI Deps starts, because that job's own total is 39–71 s (13 runs). This is pure wall-clock cost, about +1 min per ui lane.
    • Cold runs: the ui jobs start 224–290 s after Build UI Deps starts (6 runs). Before, each job spent 86–288 s (median 201 s) building inside its own wall. The net change is roughly one job setup, about +0.5–1 min.

    The card's expected "1–2 min per ui lane" wall saving does not hold. The change saves runner-minutes and adds about a minute of wall clock per lane.

    Per-job wall clock, whole job (n, median, max)

    These populations are different PR mixes, so job medians compare loosely. The step readings above are the like-for-like comparison.

    job before after
    Build UI Deps (did not exist) n=19, median 65 s, max 288 s
    Type Check UI n=49, median 224 s, max 1194 s n=18, median 607 s, max 889 s
    Lint UI n=42, median 112 s, max 558 s n=14, median 337 s, max 685 s
    Build & E2E UI n=51, median 170 s, max 457 s n=19, median 137 s, max 166 s
    Test UI shard n=591, median 650 s, max 1060 s n=223, median 485 s, max 784 s

    Lint UI with #25902's gates in the job (runs since its merge): n=4, at 259 s, 429 s, 433 s and 685 s (push 40289957670). The 685 s run exceeds the population the 30-minute ceiling was derived from (max 558 s). By objectui's rule (≥ 3× max and ≥ max + 15 min), 685 s gives 35. The 30 still leaves 2.6× headroom over the slowest run seen, but n=4 is too few to re-derive from. That re-derivation belongs to the #25880 seat (ui/scripts/__tests__/turbo-cache-save-bound.test.ts pins Lint UI at 30).

    Saves (rows F1/F11)

    Every ui cache save is now a bounded, non-fatal actions/cache/save (5 min). The turbo seed saves after the landing took 0–2 s (n=12). Build UI Deps did not save the pnpm store in this window, because every run hit its exact key. No save step ran into its bound.

    Premise vs measured, point 2

    Option (b), a pull_request run reading the remote cache, was not taken. Turbo reads with the same TURBO_TOKEN that writes, and Build Core's credential ruling (#21186) withholds it and the signing key from PR code. The PM ruled that the ruling stands. Queue and main runs of Build UI Deps carry the remote-cache lines.

    Local evidence at the PR head (cd0ae004)

    • Push battery, both tiers: "✓ dispatch-gates self-test: 2182 cases pass." (fast 1917 in 341.8 s, slow 265 in 872.1 s).
    • Derived gates: 63 of 63 exit 0. check:dual-build-cjs-loads was first a PREREQUISITE NOT MET and passed after a full pnpm build. --ran reports 0 unrun.
    • The PR's only CI red was Test UI 14/16, on main's known pre-install-import-graph row.

    Relay writes for this card: 2 (pr_create, this comment). MCP calls: 0.

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

    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:devxDomain lane — seat card indexed by label:pm:seatpm:epicReserved by a dedicated epic PM, parent or sub-issue; other PMs never take it; never with pm:queuepriority:p3toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions