Skip to content

chore(deps): resolve @objectstack/* 17.6.0 in pnpm-lock.yaml and follow its mechanical contract moves; five owner-held reds reported by row (objectui#11438) #68

chore(deps): resolve @objectstack/* 17.6.0 in pnpm-lock.yaml and follow its mechanical contract moves; five owner-held reds reported by row (objectui#11438)

chore(deps): resolve @objectstack/* 17.6.0 in pnpm-lock.yaml and follow its mechanical contract moves; five owner-held reds reported by row (objectui#11438) #68

Workflow file for this run

name: Lockfile Dedupe
# objectui#8333 — the committed `pnpm-lock.yaml` must already be DEDUPED, i.e.
# `pnpm dedupe` on it may not collapse anything. The mechanism, what it catches
# and what its green does NOT mean are documented at length in
# `scripts/check-lockfile-dedupe.mjs`.
#
# ── Why this exists next to `lockfile-integrity.yml` rather than inside it ────
#
# They answer different questions. objectui#8326's gate reports a DELTA against
# the merge base — "did THIS change duplicate something?" — and needs two
# revisions of one file to do it. This one reports a PROPERTY of a single tree —
# "would `pnpm dedupe` still have work to do here?" — and needs no base at all,
# but does need pnpm and the registry. Different subject, different cost,
# different failure modes, so they are separate jobs and each stays readable.
#
# ── Why it is path-filtered ──────────────────────────────────────────────────
#
# Its subject is one file. A pull request that does not touch `pnpm-lock.yaml`
# cannot change whether that lockfile is deduped, so the filter costs no
# coverage. A manifest edit that WOULD change the resolution cannot reach `main`
# without moving the lockfile either, because `ci.yml` installs with
# `--frozen-lockfile` and fails first. Same argument shape as its neighbour.
#
# The filter also lists this gate's RUNTIME CLOSURE — the files the job actually
# executes — which is this repository's convention for a path-filtered gate
# (`performance-budget.yml` states it at length): without that, a change to the
# checker itself ships having never run once, and the wiring bug then surfaces on
# somebody else's next lockfile PR, reading to them as a problem of their own.
#
# ⚠️ The filter is also what makes it UNREQUIRABLE: objectui#3523's rule,
# enforced by `scripts/__tests__/dependabot-merge-gate.test.ts`, is that a
# REQUIRED context must come from a workflow whose `pull_request` trigger has no
# path filter. It is enrolled as an OPTIONAL context instead — present it must
# be green, absent it is not waited for. `Bundle Analysis` is enrolled on
# exactly those terms and its filter also includes `pnpm-lock.yaml`.
#
# ── ⚠️ Its VERDICT no longer blocks a pull request — a ruling, not a default ──
#
# objectui#9562 measured this gate returning FOUR GREEN AND ONE RED on a
# byte-identical `pnpm-lock.yaml` inside ninety minutes: same blob at every ref,
# no lockfile edit anywhere, and no registry publish that day to explain it. The
# red printed a confident `VERDICT not deduped` naming an `esbuild` peer split —
# and then told the reader to fix it HERE by committing a dedupe, to a file every
# open pull request shares. ⛔ The mechanism was never identified, and this file
# ⛔ does not adopt one.
#
# The maintainer ruled letter A on 2026-09-17 (card objectui#9562, ruling comment
# 5717182406): a gate that is non-deterministic BY CONSTRUCTION does not block.
# So the job now passes `--report-only` to the checker below. What that changes,
# and ⛔ what it deliberately does not:
#
# * CHANGED — on a pull request a `not deduped` verdict is a `::warning::`
# annotation plus a step-summary block naming the split, and the step exits
# 0. The finding text, in BOTH modes, now says what the instrument is:
# re-run before acting, and dedupe only when the split reproduces.
# * UNCHANGED — the job keeps its name (`Lockfile Dedupe Check`), its path
# filter and its `OPTIONAL_CONTEXTS` classification, so ⛔ nothing in branch
# protection or the merge queue moves. The BARE script keeps its 0/1/2 exit
# codes for whoever runs it by hand; that is the way to ask for the hard
# verdict, and ⛔ it is not an oversight to tidy away.
# * ⚠️ STILL HAS TEETH, and this is why the classification stays honest rather
# than becoming vestigial: `--report-only` relaxes the VERDICT, not the JOB.
# A red checkout, a failed `ci-setup-pnpm.sh`, or a `--self-test` that stops
# passing still fails this context and still stops a Dependabot auto-merge.
# What no longer reds is a live registry reading disagreeing with itself.
#
# ⚠️ `scripts/dependabot-merge-gate.mjs`'s entry for this name still reads
# "Blocking when it runs". After this change that is imprecise for the verdict
# (true for the job, per the bullet above); the classification itself is correct
# and ⛔ was deliberately not edited here — that file is held by another open
# pull request, and prose is not worth the collision.
#
# ⇒ what this gate still defends is objectui#9215's paydown: on `main` after that
# collapse it is green, on `main` before it the same command reds. It now defends
# it by REPORTING the drift rather than by refusing the pull request.
#
# ── No `merge_group` trigger ─────────────────────────────────────────────────
#
# Deliberate, and the same shape as `lockfile-integrity.yml` and
# `performance-budget.yml`: under objectui#3523 a required context that does not
# report on a queue build stalls the queue until the ruleset timeout fails it, so
# a path-filtered, non-required gate must not subscribe one.
on:
pull_request:
branches: [main, develop]
paths:
- 'pnpm-lock.yaml'
# This gate's runtime closure — the files this job executes. An edit to any
# of them must be able to red this gate on its OWN pull request.
- '.github/workflows/lockfile-dedupe.yml'
- 'scripts/check-lockfile-dedupe.mjs'
# `isEntrypoint` decides whether `main()` runs at all, so a regression in it
# makes this checker exit 0 having measured nothing — the exact silent-green
# failure the rest of this gate exists to prevent. Same reason
# `performance-budget.yml` and `half-state-patrol.yml` list it.
- 'scripts/invoked-as.mjs'
# The toolchain step this job runs before pnpm is on PATH.
- 'scripts/ci-setup-pnpm.sh'
workflow_dispatch:
concurrency:
group: lockfile-dedupe-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
lockfile-dedupe:
name: Lockfile Dedupe Check
runs-on: ubuntu-latest
# ── The job ceiling, DERIVED FOR THIS JOB ────────────────────────────────
#
# ⛔ NOT inherited from `lockfile-integrity.yml`'s 5 or `ci.yml`'s ladder —
# objectui#7048 fences exactly that. The arithmetic below is this job's own,
# and its weakness is stated rather than hidden.
#
# - Population: `pnpm dedupe --check` on a clean `origin/main` worktree,
# n=3 consecutive runs, pnpm 10.31.0 / Node 22.22.2, WARM pnpm metadata
# cache, on a developer box and not a runner: 22.8s / 22.9s / 23.2s, all
# exit 0. Spread 0.4s, so the command itself is not variable.
# - ⚠️ That population is the wrong one for a ceiling and is known to be:
# this workflow has no run history yet, and a runner starts with a COLD
# metadata cache, which is the dominant term. So the max is not measured
# but BOUNDED, pessimistically, at 5 minutes for checkout + Node + the
# Corepack fetch + a cold resolve of ~1,767 identities.
#
# Ceiling = the smallest round number that is both >= 3x that bound (15min)
# and >= bound + 15min (20min); the second leg binds => 20.
#
# ⚠️ Re-derive this from real runs once this workflow has a history — the
# instrument is the Actions jobs endpoint (`completed_at` minus
# `started_at`, per job, never the run total), the same one
# `performance-budget.yml` names. ⛔ Do not copy the number above forward as
# if it were a measurement of this job in CI; it is a bound on one.
timeout-minutes: 20
steps:
# No `fetch-depth: 0` here, unlike its neighbour: this gate's subject is a
# single tree, not a delta, so it needs no merge base and no history.
- name: Checkout code
uses: actions/checkout@v7
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: '22.x'
# No `cache: 'pnpm'` — that action's store probe shells out to pnpm
# before the step below has put it on PATH (objectui#8099 documents
# that exact ordering trap), and this job runs no `pnpm install`, so
# there is no node_modules for a cache to pay for.
- name: Enable Corepack and download the pinned pnpm
run: bash scripts/ci-setup-pnpm.sh
- name: Verify pnpm version
run: pnpm --version
# The checker's own cases, before the checker is trusted to judge anything.
- name: Self-test the checker
run: node scripts/check-lockfile-dedupe.mjs --self-test
# No `pnpm install`: `pnpm dedupe --check` resolves and reports without
# writing a lockfile or a node_modules tree (verified on objectui#8333 —
# the lockfile's sha256 is unchanged across repeated runs).
# `--report-only` (objectui#9562, ruling A): the reading is unchanged, the
# consequence is not — `::warning::` + a step summary, exit 0. ⛔ Do not
# drop the flag to "make the gate strict again": the gate is a live
# registry reading that was measured disagreeing with itself, and a
# blocking version of it teaches every seat to re-run on red, which hides
# the real failures of this same check.
- name: Check the committed lockfile is deduped
run: node scripts/check-lockfile-dedupe.mjs --report-only