From bfa828074c6a520345376060fa71b2330c42ca5c Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 05:01:27 +0000 Subject: [PATCH 1/3] ci(spec-shape-gate): install objectui through its pinned-build directories, packed from this commit objectui now overrides every @objectstack/* package it installs to file:node_modules/.cache/objectstack/, a directory only its scripts/objectstack-pin.mjs build fills, so the gate's bare frozen install died on ENOENT there (exit 254) and every run reported exit 2. The gate now builds the packages those overrides name (read from objectui's package.json, --filter-prod closure) from this commit, packs them into those directories with OBJECTSTACK_ROOT set to this checkout, and then runs the frozen install. objectui compiles against all seven packages built here, not only the spec. The inject step stays: it still judges the spec's dependency ranges and proves every consumer resolves the injected copy, which the pin script does not (it compares dependency names only). Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp Co-authored-by: Claude --- .github/workflows/spec-shape-gate.yml | 52 +++++++++++++++++++++------ 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/.github/workflows/spec-shape-gate.yml b/.github/workflows/spec-shape-gate.yml index 664ae55b9a2..a2a7980a5cc 100644 --- a/.github/workflows/spec-shape-gate.yml +++ b/.github/workflows/spec-shape-gate.yml @@ -1,8 +1,9 @@ name: Spec Shape Gate # Compile the downstream consumer -- objectstack-ai/objectui at `main` -- against -# `@objectstack/spec` built from THIS commit, and name the objectui file, the -# objectstack commit and the owning lane when it does not compile. +# `@objectstack/spec` (and every other `@objectstack/*` package it installs) +# built from THIS commit, and name the objectui file, the objectstack commit and +# the owning lane when it does not compile. # # ## The ruling this executes # @@ -205,16 +206,30 @@ jobs: - name: Self-test the gate script run: pnpm check:spec-shape-gate - # ── Build the spec from THIS commit, and PACK it ────────────────────── - # `--filter @objectstack/spec...` installs the spec and its dependencies - # only; `inject` later reads the dependencies this install resolved + # ── Build what objectui installs, from THIS commit, and PACK the spec ── + # objectui consumes every `@objectstack/*` package BY COMMIT: its root + # `package.json` overrides each to `file:node_modules/.cache/objectstack/ + # `, which its `scripts/objectstack-pin.mjs build` fills (below, with + # OBJECTSTACK_ROOT naming this checkout). The names are read from those + # overrides, the list the pin script packs; `--filter-prod` keeps the + # closure to runtime edges as that script's own build does (`...` alone + # would pull in the client's dev closure). The install brings the root's + # toolchain too, and `inject` later reads the dependencies it resolved # (`packages/spec/node_modules`) to re-point a store sibling the packed # manifest's range refuses. - - name: Install the spec's dependency closure - run: pnpm install --frozen-lockfile --filter @objectstack/spec... + - name: Install the closure of the @objectstack/* packages objectui installs by commit + run: | + set -euo pipefail + filters=$(node -e ' + const m = JSON.parse(require("fs").readFileSync("objectui/package.json", "utf8")); + const names = Object.keys(m.pnpm?.overrides ?? {}).filter((n) => n.startsWith("@objectstack/")).sort(); + if (names.length === 0) { console.error("objectui/package.json overrides no @objectstack/* package"); process.exit(2); } + process.stdout.write(names.map((n) => `--filter-prod=${n}...`).join(" "));') + echo "PINNED_FILTERS=$filters" >> "$GITHUB_ENV" + pnpm install --frozen-lockfile $filters - - name: Build @objectstack/spec from this commit - run: pnpm --filter @objectstack/spec build + - name: Build them from this commit + run: pnpm $PINNED_FILTERS run build # What gets injected is what a RELEASE from this commit would ship -- the # `files` field's file set, not a working tree. `--ignore-scripts` @@ -238,6 +253,20 @@ jobs: working-directory: objectui run: pnpm --version + # Packs every package built above into the directories objectui's + # overrides name; without it the frozen install dies on ENOENT there. It + # refuses (exit 1, its own `objectstack-pin` annotation) when a packed + # manifest's dependency NAMES differ from what objectui's lockfile records: + # the downstream owes a pin bump with its lockfile, and the report reads the + # missing compile as exit 2. A changed peer, engines or dependency range is + # not refused there -- a frozen install links it and exits 0 -- and the + # spec's ranges stay `inject`'s to judge. + - name: Pack this commit's @objectstack/* into objectui's pinned-build directories + working-directory: objectui + env: + OBJECTSTACK_ROOT: ${{ github.workspace }} + run: node scripts/objectstack-pin.mjs build + - name: Install objectui dependencies working-directory: objectui run: pnpm install --frozen-lockfile @@ -254,7 +283,10 @@ jobs: # re-points a store sibling the packed manifest's range refuses at the # copy this tree's spec build resolved, and proves every workspace # consumer resolves the injected copy before returning. Nothing tracked - # by git is touched in either checkout. + # by git is touched in either checkout. The pinned build above already + # installed this commit's spec (the same declaration files); what this + # step still adds is that range judgement and the consumer proof, which + # the pin script does not make (it compares dependency names only). - name: Inject the source-built spec into objectui's install env: UPSTREAM_SHA: ${{ steps.subjects.outputs.upstream }} From 46aa30bdfd55d50830f639f67c6bca0bc6a61079 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 05:15:13 +0000 Subject: [PATCH 2/3] ci(spec-shape-gate): call objectui's pin build by its script name `node scripts/objectstack-pin.mjs build` in a step whose working directory is the objectui checkout names a path that exists only there, and check-self-test-wired's live corpus keys every workflow `node ` to a file in this repository (its self-test went red on it). objectui's own `objectstack:build` script is the same command under the name objectui declares for it, and names no path here. Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp Co-authored-by: Claude --- .github/workflows/spec-shape-gate.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/spec-shape-gate.yml b/.github/workflows/spec-shape-gate.yml index a2a7980a5cc..fe8c792cb56 100644 --- a/.github/workflows/spec-shape-gate.yml +++ b/.github/workflows/spec-shape-gate.yml @@ -253,8 +253,10 @@ jobs: working-directory: objectui run: pnpm --version - # Packs every package built above into the directories objectui's - # overrides name; without it the frozen install dies on ENOENT there. It + # objectui's `objectstack:build` script (its pin script's `build`) packs + # every package built above into the directories its overrides name; + # without it the frozen install dies on ENOENT there. Called by objectui's + # script NAME: a path here would read as one in this repository. It # refuses (exit 1, its own `objectstack-pin` annotation) when a packed # manifest's dependency NAMES differ from what objectui's lockfile records: # the downstream owes a pin bump with its lockfile, and the report reads the @@ -265,7 +267,7 @@ jobs: working-directory: objectui env: OBJECTSTACK_ROOT: ${{ github.workspace }} - run: node scripts/objectstack-pin.mjs build + run: pnpm run objectstack:build - name: Install objectui dependencies working-directory: objectui From eb080715a549acb5a9f04474f317e880a0f7b534 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 05:32:58 +0000 Subject: [PATCH 3/3] ci(spec-shape-gate): the summary names every @objectstack/* package the compile read objectui now installs all seven @objectstack/* packages it uses from this commit's build, so "compiled against @objectstack/spec built and packed from" understated the subject of every reading. Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp Co-authored-by: Claude --- scripts/ci/spec-shape-gate.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ci/spec-shape-gate.mjs b/scripts/ci/spec-shape-gate.mjs index 152248ee73b..92686ddc4b0 100644 --- a/scripts/ci/spec-shape-gate.mjs +++ b/scripts/ci/spec-shape-gate.mjs @@ -1135,12 +1135,12 @@ export function renderSummary({ sha, downstreamSha = null, commit = null, event const link = `https://github.com/${UPSTREAM_REPO}/commit/${sha}`; const out = []; out.push(`### Spec Shape Gate -- \`${UPSTREAM_REPO}@${short}\` against \`${DOWNSTREAM_REPO}@${String(downstreamSha ?? 'main').slice(0, 12)}\``, ''); - out.push(`Compiled ${DOWNSTREAM_REPO} at \`main\`${downstreamSha ? ` (\`${String(downstreamSha).slice(0, 12)}\`)` : ''} against \`${SPEC_PACKAGE_NAME}\` built and packed from [${short}](${link})${event ? ` (\`${event}\` run)` : ''}.`); + out.push(`Compiled ${DOWNSTREAM_REPO} at \`main\`${downstreamSha ? ` (\`${String(downstreamSha).slice(0, 12)}\`)` : ''} against \`${SPEC_PACKAGE_NAME}\` and every other \`@objectstack/*\` package it installs, built and packed from [${short}](${link})${event ? ` (\`${event}\` run)` : ''}.`); if (commit) out.push('', `> ${commit}`); out.push(''); if (status === 0 && rows.length === 0) { - out.push(`✅ ${DOWNSTREAM_REPO} type-checks against \`${SPEC_PACKAGE_NAME}\` at that commit.`); + out.push(`✅ ${DOWNSTREAM_REPO} type-checks against \`@objectstack/*\` at that commit.`); } else if (rows.length === 0) { out.push( `⛔ The typecheck failed (exit ${status}) and **no diagnostic could be attributed to a file**.`,