Skip to content

fix(app-shell): bridge Is null to the spec's $null instead of erasing the dataset filter #317

fix(app-shell): bridge Is null to the spec's $null instead of erasing the dataset filter

fix(app-shell): bridge Is null to the spec's $null instead of erasing the dataset filter #317

name: Line Citation Gate
# The DIFFERENTIAL cross-file line-address citation gate (objectui#8875, ruling
# clause 2). It reads what THIS pull request ADDED against its base — never a
# tree-wide total. An absolute count was explicitly refused: PR #8887's line
# shifts flipped one citation from `drifted` to `resolves` by accident, moving
# the tree-wide false count 540 -> 539, and that unearned green belonged to
# nobody. The 540 existing citations are not this gate's denominator.
#
# ── REPORT-ONLY, and the workflow does not decide that ───────────────────────
# The enforcement state lives in the script (`ENFORCEMENT`), not here, so there
# is one place to read it and one place to flip it. Report-only means findings
# exit 0; a failed CONTROL still exits 1 in both modes, because a differential
# gate reporting zero through a broken differ is indistinguishable from a clean
# branch — the failure this card names one level up.
#
# ── Why its own workflow rather than a step in `lint.yml` ────────────────────
# `lint.yml` decides inside the job whether the change "needs a full run", and
# its exclusion list skips every expensive step on a markdown-only, docs-only or
# changeset-only change. Those are precisely the changes that carry citations:
# the ruling names 注释、消息串、文档、changeset、脚本 in that order. A gate
# that cannot see the pull request shape most likely to trip it rebuilds the
# hole it exists to close — the same conclusion `shell-escape-residue.yml`,
# `docs-links.yml`, `control-bytes.yml` and `changeset-presence.yml` each
# reached in their own headers. One gate, one home.
#
# Hence no `paths` and no `paths-ignore`, deliberately.
#
# ── Why `pull_request` only ──────────────────────────────────────────────────
# This gate needs a BASE to be differential at all, and only a pull request has
# one. It is therefore ⛔ not requirable while it is report-only, and
# `scripts/dependabot-merge-gate.mjs` classifies it in `NOT_A_GATE` with that
# reason. When `ENFORCEMENT` flips to `blocking` this workflow owes a
# `merge_group` leg before the context may be required — a required check that
# does not report on a queue build stalls the queue until the ruleset's
# 60-minute timeout fails it (objectui#3523).
#
# It needs no install and no build: a checkout plus one `node` call over this
# branch's own diff. Keep it that way; the import graph is node builtins plus
# repo-relative modules only, which `pre-install-import-graph.yml` enforces.
on:
pull_request:
branches: [main, develop]
workflow_dispatch:
concurrency:
group: line-citation-gate-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
line-citation-gate:
name: Line Citation Gate
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
# `fetch-depth: 0` is load-bearing, not caution: the gate resolves the
# merge base with the target branch, and a shallow clone cannot. When it
# cannot, the gate exits 2 naming PREREQUISITE NOT MET rather than
# printing a zero — "the diff could not be computed" must never be
# indistinguishable from "nothing was added" (objectstack#4928).
- name: Checkout code
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: '22.x'
- name: Report cross-file line-address citations this pull request adds
env:
CITATION_GATE_BASE: origin/${{ github.event.pull_request.base.ref }}
run: node scripts/check-new-cross-file-line-citations.mjs