Skip to content

finding: lint.yml 的两个 required context 名(ESLint / TypeScript Type Check)在仓内无任何断言 —— 改名即静默丢门,而 ci.yml 的同类名字有 check:shard-attestation 守着 #6865

Description

@os-project-manager

观察级发现(observation-class:今天没人被咬,是一条潜伏的改名陷阱)。核 #5617 时量到,单独立案不夹带在任何 PR 里。

事实

#5617 的设置面已于 2026-08-07 落地:ESLint 与 TypeScript Type Check 现在是 main 分支保护的 required context。这两个名字是 job 级 name: 的值,不是 workflow 名、也不是 rollup:

  • .github/workflows/lint.yml:24-25 — job id lint → name: ESLint(承载 25 条 check:* 门禁族)
  • .github/workflows/lint.yml:506-507 — job id typecheck → name: TypeScript Type Check

也就是说这两行字面量就是分支保护的契约。改掉任意一行,GitHub 上报的 check-run 名字随之改变,而分支保护里那条 required context 从此永不上报 —— 表现为永久 pending(与红等效地卡住 PR),或者(若同时把该 context 从设置里摘掉)整个门禁族静默降级为 advisory,即 #5617 事故的原始形状。

为什么说这是缺口:同类风险在 ci.yml 那侧是有守的

ci.yml 的两个聚合门禁名有机器断言:

  • scripts/check-shard-attestation.mjs:106-107
    /** The gate jobs that carry a branch-protection-required context. */
    const REQUIRED_GATE_JOBS = ['test-gate', 'dogfood-gate'];
  • 同文件 :410-412 在 job 消失/改名时判红:job '...' carries a branch-protection-required context but no longer counts shard attestations (#6082/#3622);:344 另守聚合 job 的 always()(「a gate that skips publishes no required context」)。self-test :655 里有 test-gate → test-gate-renamed 的改名用例,方向明确。

ci.yml 正文也把这条契约写了至少 8 处(:123、:329-330、:423-424、:858-869、:1177-1178,原话「the NAME is the required check, so renaming it would silently drop the gate」)。

lint.yml 的两个名字享受不到其中任何一条:check:shard-attestation 只读 ci.yml 的分片/聚合拓扑,check:workflow-status-functions 管的是 status() 函数用法,两者都不断言 ESLint / TypeScript Type Check 这两个字面量存在。lint.yml 里也没有对应的契约注释(它只在 :10-13 说明了 merge_group 触发器的必要性,没说 job 名是契约)。

可能的处置(留给分诊定,本人不预设)

  1. 把 lint.yml 的两个 job 名加进一条 check:* 的字面量断言(最小改动:在 check-shard-attestation.mjs 之外新开或扩一条,断言 lint.yml 中 job lint 的 name 恰为 ESLint、job typecheck 的 name 恰为 TypeScript Type Check,且两者均无 if: / matrix / continue-on-error,且 on: 含 merge_group 与不带 paths: 的 pull_request)。
  2. 仅补注释(与 ci.yml 同形的 doc block),不加机器断言。
  3. 不做 —— 认为改这两个名字的概率足够低。

⚠️ 无论走哪条,都有一个结构性限制必须先说清:任何 agent 座位都读不到分支保护配置(实测 GET /repos/objectstack-ai/objectstack/branches/main/protection 返回 HTTP 403 GitHub access is not enabled for this session),所以这类 check 只能断言「仓内这一侧的名字没变」,不能断言「它确实在 required 集里」。前者可测、有价值;后者只能靠维护者核对(#5617 的剩余项正是如此)。把它写成「断言 required 集」会是一道读不到自己声称要读的东西的门。

关联:#5617(required 集缺口与审计表)、#6082 / #3622(同族的分片改名陷阱)。

Activity

  1. os-project-manager commented on Aug 9, 2026

    @os-project-manager
    CollaboratorAuthor

    Triage (maintainer-authorized one-off pass, 2026-08-09, registered on #6015): promoted finding → pm:queue (domain:devx kept). A rename of either job name: silently detaches a branch-protection contract carrying the whole check:* gate family — the exact #5617 incident shape — while ci.yml's equivalent names are machine-asserted. Deliverable: extend the check:shard-attestation idiom (or a sibling script) to pin lint.yml's required context names. Scope update from #5617's closure (2026-08-09): the maintainer approved a second batch of required contexts — Build Core, Build Docs, Console Pin Gate, Temporal Conformance (live PG + MySQL) — so the guard should enroll those names in the same assertion, not just the original two.


    Generated by Claude Code

  2. os-project-manager commented on Aug 9, 2026

    @os-project-manager
    CollaboratorAuthor

    Claiming this issue.

    • Session: session_01F8q5J1MQyocgtNspb15fSn
    • Branch: claude/issue-6865-lint-required-context-pin

    Scope per the 2026-08-09 triage comment: pin all six required-context job names (ESLint, TypeScript Type Check, Build Core, Build Docs, Console Pin Gate, Temporal Conformance (live PG + MySQL)), following the existing check:shard-attestation idiom. Each name will be re-verified against origin/main before pinning.


    Generated by Claude Code

  3. os-project-manager commented on Aug 9, 2026

    @os-project-manager
    CollaboratorAuthor

    Scope update — the pinned name set is now FINAL, from the maintainer's saved configuration (2026-08-09), superseding both this card's original two and the #5617 closing comment's planned four-item second batch.

    The maintainer configured and saved the main ruleset's required set as exactly five contexts:

    ESLint
    TypeScript Type Check
    Test Core
    Dogfood Regression Gate
    Build Core
    

    The guard pins these five names — no more, no fewer. The rest of the #5617-approved second batch was deliberately narrowed at settings time (marginal-value review with the maintainer): Build Docs and Temporal Conformance (live PG + MySQL) stay advisory (the latter's live-DB flake exposure outweighs its solo-red catch rate), and Console Pin Gate is a v17-window option the maintainer may add temporarily — the guard should treat it as allowed-if-present, not required-to-exist, so adding or removing it around the release window never reds the guard.

    Implementation note carried over: the guard asserts that each pinned name exists as a job-level name: in the workflow files (ESLint / TypeScript Type Check in lint.yml; Test Core / Dogfood Regression Gate / Build Core in ci.yml), in the check:shard-attestation idiom — a rename or deletion reds CI naming the detached branch-protection contract.


    Generated by Claude Code

  4. os-project-manager commented on Aug 9, 2026

    @os-project-manager
    CollaboratorAuthor

    Scope update #2 (maintainer settings action, 2026-08-09 ~12:0xZ): the pinned set gains a sixth name.

    The maintainer added ADR maintainer approval (the job-level name from .github/workflows/adr-merge-approval.yml — NOT the workflow-level "ADR Merge Approval", which was explicitly checked and avoided) to the main ruleset's required set, closing the #7022 gap (two ADR files landed unapproved earlier today because the gate was advisory).

    Final pinned set for the guard — six required names plus one allowed-if-present:

    ESLint                                    (lint.yml)
    TypeScript Type Check                     (lint.yml)
    Test Core                                 (ci.yml)
    Dogfood Regression Gate                   (ci.yml)
    Build Core                                (ci.yml)
    ADR maintainer approval                   (adr-merge-approval.yml)
    

    plus Console Pin Gate as allowed-if-present (v17-window option, never required-to-exist).

    The adr-merge-approval workflow deliberately reports on every PR (no paths filter, merge_group trigger, review-triggered re-run), so it is deadlock-safe as a required context — the guard should also assert that the workflow keeps all three triggers, since losing any one of them re-opens either the #7022 bypass (merge_group) or a stuck-red-after-approval state (pull_request_review).


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions