Repository navigation
chore(objectui): move the console pin to f8267df93aba, past the spec deletion of checkPageComponentViewData #68
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. | |
| # | |
| # ## The ruling this executes | |
| # | |
| # Ruling B on the decision card for the spec shape gate (#23238, comment | |
| # 6145029395, batch 336 item 3, 2026-10-13): the gate MOVES to objectstack -- | |
| # hourly on `main` with the two-speed full battery, read by the release gate, | |
| # a red attributed to the lane that moved the shape; objectui drops its copy | |
| # once two consecutive hourly runs here agree with objectui's. The MECHANISM of | |
| # ruling C on #19011 (consumer compilation as the ONE shape gate for the spec's | |
| # public surface) is kept unchanged; what moved is where it runs and who owns a | |
| # red. ⛔ Neither direction is re-adjudicated here. | |
| # | |
| # The objectui half -- `.github/workflows/spec-main-shape-gate.yml` there, its | |
| # script `scripts/spec-main-shape-gate.mjs` and its three pins -- was deleted | |
| # in objectstack-ai/objectui PR #12596 (card objectstack-ai/objectui#12352), so | |
| # this is the only spec shape gate and nothing runs in parallel. Both files are | |
| # readable at objectui commit `7d0c174ba8383d08b8dd1d4f7d7845009f1bf2fd`, the | |
| # first parent of that PR's merge commit `c6a42be392` and the last objectui | |
| # `main` commit that has them. This file is the objectstack port of that | |
| # workflow with the direction reversed: the spec is built from the checkout the | |
| # run is in, the CLONE is the downstream, and there is no artifact reuse -- the | |
| # spec is always the one from the tree under test. | |
| # | |
| # ## Its place in the two speeds (#22998) | |
| # | |
| # `schedule`, hourly on `main`, is this gate's place in the FULL BATTERY. A PR | |
| # or queue build carries the fast set; this job never runs on an ordinary pull | |
| # request (two-speed rule 1) and is NOT subscribed to `merge_group`. The one | |
| # `pull_request` trigger below is path-filtered to this gate's own three files, | |
| # so a change to the gate -- the workflow, the script, or a REGISTRATION in | |
| # the retirement registry -- is exercised before it merges and proves on its | |
| # own pull request that the registered diagnosis reads `known`; no other pull | |
| # request pays the roughly thirteen minutes of a downstream compile. | |
| # | |
| # `scripts/ci/scheduled-full-run.mjs --release-gate` reads this workflow's | |
| # newest `schedule` run at or before the version commit, beside `CI`'s and | |
| # `Lint & Type Check`'s, and the publish lane refuses unless all conclude | |
| # `success` (`RELEASE_GATE_WORKFLOWS` there is the registration). A run whose | |
| # every diagnostic is a registered retirement concludes `success`. | |
| # | |
| # ## Who owns a red | |
| # | |
| # ⭐ A red on the hourly run is a MAIN-RED under #22998 rule 3: it is the FIRST | |
| # WORK of the lane the report names. The report step attributes it to the | |
| # objectstack commits since this workflow's last green run on `main`, the pull | |
| # request each landed in, and the `domain:*` lane of the card that pull fixes; | |
| # a lane it cannot read is written `unattributed` -- a reading this gate could | |
| # not take, never a lane with no owner. An INTENTIONAL retirement is not a red | |
| # for long: the accepting seat files the downstream card and registers the | |
| # retirement in `scripts/ci/spec-shape-retirements.json` in the same act | |
| # (cross-repo rule 3), and the next run reads the matching diagnostics as | |
| # `known (registered: <card>)` and does not red. ⛔ The registry is one line per | |
| # retirement with a per-diagnostic pattern; it is not, and must never become, | |
| # an ignore-all switch -- `scripts/ci/spec-shape-gate.mjs` (THE REGISTRY) | |
| # refuses an entry that would be one. | |
| # | |
| # ⭐ WHAT A RED MUST SAY is the acceptance criterion, not decoration: which | |
| # objectui file fails against which objectstack commit, so that the objectstack | |
| # pull request which moved the shape is the one that answers. The script owns | |
| # that half -- it attributes every diagnostic to a repo-relative objectui path | |
| # paired with the objectstack sha, in the run summary and in annotations -- and | |
| # it refuses (exit 2, loudly) to report an unattributable failure as a shape | |
| # break or as a pass. | |
| # | |
| # ## Why the clone is objectui `main`, not the pin | |
| # | |
| # The question this gate asks is "does TODAY'S downstream compile against this | |
| # commit". `.objectui-sha` is the release artifact's pin and stays the Console | |
| # Pin Gate's question (ci.yml `console-pin`); the two gates are not the same | |
| # question and neither replaces the other. | |
| # | |
| # ## ⛔ The turbo cache is a FALSE GREEN here | |
| # | |
| # `turbo`'s hash covers objectui's sources, lockfile and a declared env list, | |
| # not the CONTENT of `node_modules`, which is the only thing this job changes, | |
| # so a cached `type-check` could replay a verdict taken against the PUBLISHED | |
| # spec. The typecheck runs with `TURBO_FORCE=true` and no turbo cache action. | |
| # `--continue` is part of the READING: without it turbo stops scheduling at the | |
| # first failed task and the log holds one package's diagnostics instead of | |
| # every package's. | |
| # | |
| # ## Two runner details that change what a reader sees | |
| # | |
| # `actions/setup-node` registers a `tsc` problem matcher; left on, the runner | |
| # parses the echoed typecheck log and mints a second annotation per diagnostic | |
| # with a path that does not resolve in this repository (measured on objectui's | |
| # own runs: ten extra annotations re-homed onto `.github`). The compile step | |
| # removes that matcher before echoing the log, so the only annotations are the | |
| # report's, each naming the objectui path and the objectstack sha in its title. | |
| # The exit code of the compile is captured with NO PIPE in between. | |
| # | |
| # The downstream's install runs its lifecycle scripts, as any consumer compile | |
| # must; the token this job holds is read-only (`permissions` below) and nothing | |
| # here writes through it. | |
| on: | |
| # The full battery's hourly run on `main`. Off the hour on purpose -- ci.yml | |
| # and lint.yml take `:00`, and scheduled workflows queue behind everyone's | |
| # `:00` cron -- and on a minute no sibling here uses. | |
| schedule: | |
| - cron: '52 * * * *' | |
| # The same reading on demand: an operator who has just registered a | |
| # retirement or landed a fix should not wait an hour to see it answered. | |
| workflow_dispatch: {} | |
| # This gate's own files only. See "Its place in the two speeds" above. | |
| pull_request: | |
| paths: | |
| - '.github/workflows/spec-shape-gate.yml' | |
| - 'scripts/ci/spec-shape-gate.mjs' | |
| - 'scripts/ci/spec-shape-retirements.json' | |
| # Least privilege, all reads: `contents` for the two checkouts, `actions` for | |
| # the report's lookup of this workflow's own earlier runs (the last green), | |
| # `pull-requests` and `issues` for the lane attribution (a pull's labels and | |
| # body, a card's labels). Nothing writes through the token. | |
| permissions: | |
| contents: read | |
| actions: read | |
| pull-requests: read | |
| issues: read | |
| # `github.event_name` is in the key on purpose: a `schedule` run and a | |
| # `workflow_dispatch` run on `main` carry the same `github.ref`, and neither | |
| # may cancel the other -- the hourly run is the reading the release gate waits | |
| # on, so a cancellation there is a missing reading rather than a saved minute. | |
| # Superseded pull-request runs are cancelled; runs on `main` never are. | |
| concurrency: | |
| group: spec-shape-gate-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| jobs: | |
| shape-gate: | |
| name: Spec Shape Gate | |
| runs-on: ubuntu-latest | |
| # Sized against objectui's measured runs (about thirteen minutes, of which | |
| # the forced, uncached downstream build + typecheck is eleven) with room | |
| # for a cold runner; a ceiling this job can reach turns a verdict into | |
| # `cancelled`, which reports nothing a reader can act on. | |
| timeout-minutes: 45 | |
| steps: | |
| - name: Checkout objectstack (the commit under test) | |
| uses: actions/checkout@v7 | |
| # The downstream, at `main`, inside the workspace (actions/checkout | |
| # refuses a path outside it). objectstack's workspace globs are rooted at | |
| # `packages/`, `apps/` and `examples/`, so nothing under `objectui/` is | |
| # swept into this repository's install, and pnpm run inside `objectui/` | |
| # finds objectui's own `pnpm-workspace.yaml` first. | |
| - name: Checkout objectui main (the downstream under test) | |
| uses: actions/checkout@v7 | |
| with: | |
| repository: objectstack-ai/objectui | |
| ref: main | |
| path: objectui | |
| fetch-depth: 1 | |
| # Resolved ONCE, into outputs every later step and the report quote, so | |
| # the sha in the summary is provably the sha that was built. | |
| - name: Resolve the two commits under test | |
| id: subjects | |
| run: | | |
| set -euo pipefail | |
| upstream=$(git rev-parse HEAD) | |
| downstream=$(git -C objectui rev-parse HEAD) | |
| subject=$(git log -1 --format=%s HEAD) | |
| committed=$(git log -1 --format=%cI HEAD) | |
| echo "upstream=$upstream" >> "$GITHUB_OUTPUT" | |
| echo "downstream=$downstream" >> "$GITHUB_OUTPUT" | |
| echo "commit=${upstream:0:12} -- $committed -- $subject" >> "$GITHUB_OUTPUT" | |
| echo "objectstack@$upstream ($committed) against objectui@$downstream" | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version: '22' | |
| - name: Setup pnpm | |
| uses: ./.github/actions/setup-pnpm | |
| - name: Get pnpm store directory | |
| shell: bash | |
| run: echo "STORE_PATH=$(pnpm store path --silent)" >> "$GITHUB_ENV" | |
| # One store for both installs (same pnpm, same store path), keyed on both | |
| # lockfiles. Restore here; the save below runs only on `main` runs, so an | |
| # hourly run seeds the next hour and a pull-request run spends nothing | |
| # from the cache pool. ci.yml's store entries are the cold fallback. | |
| - name: Restore the pnpm store | |
| id: store | |
| uses: actions/cache/restore@v6 | |
| with: | |
| path: ${{ env.STORE_PATH }} | |
| key: ${{ runner.os }}-spec-shape-gate-pnpm-store-${{ hashFiles('pnpm-lock.yaml', 'objectui/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-spec-shape-gate-pnpm-store- | |
| ${{ runner.os }}-pnpm-store-v3- | |
| # "A change to the gate runs the gate's self-test": offline, no install. | |
| - 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 | |
| # (`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: Build @objectstack/spec from this commit | |
| run: pnpm --filter @objectstack/spec 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` | |
| # because the build above already ran. | |
| - name: Pack the built spec (the file set a release from this commit would ship) | |
| id: pack | |
| working-directory: packages/spec | |
| run: | | |
| set -euo pipefail | |
| mkdir -p "$RUNNER_TEMP/spec-built" | |
| packed=$(npm pack --ignore-scripts --pack-destination "$RUNNER_TEMP/spec-built" | tail -n 1) | |
| echo "tarball=$RUNNER_TEMP/spec-built/$packed" >> "$GITHUB_OUTPUT" | |
| ls -l "$RUNNER_TEMP/spec-built/$packed" | |
| # ── The downstream's install ───────────────────────────────────────── | |
| # Corepack reads the nearest `packageManager` pin, so pnpm invoked inside | |
| # `objectui/` is objectui's pinned version (downloaded here, once, when | |
| # it differs from this repository's). A named step so a toolchain | |
| # download that fails is answerable from the checks list alone. | |
| - name: Materialise objectui's pinned pnpm | |
| working-directory: objectui | |
| run: pnpm --version | |
| - name: Install objectui dependencies | |
| working-directory: objectui | |
| run: pnpm install --frozen-lockfile | |
| - name: Save the pnpm store | |
| if: github.event_name != 'pull_request' && steps.store.outputs.cache-hit != 'true' | |
| uses: actions/cache/save@v6 | |
| with: | |
| path: ${{ env.STORE_PATH }} | |
| key: ${{ runner.os }}-spec-shape-gate-pnpm-store-${{ hashFiles('pnpm-lock.yaml', 'objectui/pnpm-lock.yaml') }} | |
| # Replaces the installed spec in objectui's pnpm virtual store -- REMOVE | |
| # then copy, because those files are hardlinks into the global store -- | |
| # 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. | |
| - name: Inject the source-built spec into objectui's install | |
| env: | |
| UPSTREAM_SHA: ${{ steps.subjects.outputs.upstream }} | |
| TARBALL: ${{ steps.pack.outputs.tarball }} | |
| run: node scripts/ci/spec-shape-gate.mjs inject --tarball "$TARBALL" --sha "$UPSTREAM_SHA" --upstream-checkout "$GITHUB_WORKSPACE" --downstream "$GITHUB_WORKSPACE/objectui" | |
| # ── The compile ────────────────────────────────────────────────────── | |
| # `pnpm type-check` is objectui's `turbo run type-check`, whose task | |
| # dependsOn `^build`: every objectui package is built against the | |
| # injected spec and then type-checked against those declarations. See the | |
| # header for `TURBO_FORCE`, `--continue`, the matcher and the no-pipe | |
| # capture. The step itself always exits 0; the verdict is the report's. | |
| - name: Type-check objectui against it | |
| id: compile | |
| working-directory: objectui | |
| run: | | |
| set +e | |
| echo "::remove-matcher owner=tsc::" | |
| TURBO_FORCE=true pnpm type-check --continue > "$RUNNER_TEMP/typecheck.log" 2>&1 | |
| code=$? | |
| set -e | |
| echo "status=$code" >> "$GITHUB_OUTPUT" | |
| cat "$RUNNER_TEMP/typecheck.log" | |
| exit 0 | |
| # ALWAYS: this step IS the delivery. It reads the registry, writes the | |
| # summary (new diagnostics, known ones with their registration, the | |
| # commit range and lanes), emits one annotation per NEW diagnostic naming | |
| # the objectui file and the objectstack commit, and carries the verdict: | |
| # exit 0 green or known-only, exit 1 a new shape break, exit 2 a reading | |
| # it could not take (never a pass, never a shape break). | |
| - name: Report which objectui file fails against which objectstack commit, known or new, and whose lane | |
| if: always() | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| UPSTREAM_SHA: ${{ steps.subjects.outputs.upstream }} | |
| DOWNSTREAM_SHA: ${{ steps.subjects.outputs.downstream }} | |
| UPSTREAM_COMMIT: ${{ steps.subjects.outputs.commit }} | |
| COMPILE_STATUS: ${{ steps.compile.outputs.status }} | |
| run: | | |
| node scripts/ci/spec-shape-gate.mjs report \ | |
| --log "$RUNNER_TEMP/typecheck.log" \ | |
| --sha "$UPSTREAM_SHA" \ | |
| --downstream "$GITHUB_WORKSPACE/objectui" \ | |
| --downstream-sha "$DOWNSTREAM_SHA" \ | |
| --commit "$UPSTREAM_COMMIT" \ | |
| --event "$GITHUB_EVENT_NAME" \ | |
| --status "${COMPILE_STATUS:-2}" |