Skip to content

pass-label: match a PASS writer against the roster the way the gates do #2544

Open

Description

@HugoXK

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.

Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: a91e62dd206541bbab9c6e2898d04642
Branch: dev/4-pass-label-writer-case
Base: d400edb
At: 2026-11-05T14:32:45Z

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.

Written-by: FlyBot Lead

What happens

pass-label matches the PASS writer case-sensitively, the gates do not, so a PASS the gates accept can leave no trace at all.

.github/scripts/pass-label.mjs:45:

if (!report.writer || !writers.includes(report.writer)) return null

writers is the plain array from .github/agent/review-policy.json, and includes compares exact strings. review-gate.mjs:77-85 builds new Set(policy.reviewers.map(l => l.toLowerCase())) and compares name.toLowerCase(), and merge-gate imports the same decide. So Written-by: reviewer (or dev4) against a roster entry Reviewer is a valid conclusion for the gates but verdictOf returns null — no pass label, no comment, no notification.

The stranger path does not rescue it either: strangerPassOf (:74-75) lowercases both sides, so the same writer counts as listed and returns null — the PR is silently unlabelled where #739 promises a pass record.

Reproduction

Clean d400edb74798b71e233eccd46f221eaef782ff5d, only a new case added to .github/scripts/pass-label.test.mjs:

not ok 3 - #739: a writer matches the way the gates match it, whatever its case
  error: 'reviewer is a listed name in another case'
  expected: true
  actual: ~
# tests 12 / # pass 11 / # fail 1

Run: node --test .github/scripts/pass-label.test.mjs.

Expected

One comparison rule, used in both places: a name is a listed writer when it matches a roster entry case-insensitively (isListedWriter(writers, name)), so verdictOf labels the PASS that review-gate already accepts and strangerPassOf stays quiet for the same name. Nothing widens: a name that is not in the roster in any case still yields null (negative controls: reviewer3, Reviewer Extra).

Scope

  • .github/scripts/pass-label.mjs — one exported helper (≈+3/−2), verdictOf and strangerPassOf both use it so the two cannot drift again.
  • .github/scripts/pass-label.test.mjs — the case case plus the two negative controls (≈+16/−0).
  • changelog.d/<this issue>-pass-label-writer-case.fixed.md.
  • No generated artifact: core/client.js, patches/ds-bot/** and their mirrors are untouched.

Tier: T2 (.github/scripts/**, changelog.d/**).

Duplicate check

Searched before filing: pass-label → #739 (closed, the feature), #797/#760 (closed, its implementation — lowercased only the stranger path), #801 (closed, review-body PASS); pass label case in:title → 0; no open issue or PR touches the verdictOf writer comparison.

Done when

The new case is red on the base and green with the fix, the .github/scripts suite is 669/669, the negative controls still yield null, and arch-docs reports no changed files for this PR.

Activity

  1. HugoXK commented on Nov 5, 2026

    @HugoXK
    ContributorAuthor

    Claim-Id: a91e62dd206541bbab9c6e2898d04642
    Branch: dev/4-pass-label-writer-case
    Base: main (d400edb74798b71e233eccd46f221eaef782ff5d)

    Flight plan: one PR (Fixes #2544) from dev/4-pass-label-writer-case, ready, maintainer_can_modify: true, red-first evidence in the body. Bug, T2 (.github/scripts/** + changelog.d/**).

  2. HugoXK commented on Nov 5, 2026

    @HugoXK
    ContributorAuthor

    Written-by: FlyBot Lead

    Evidence for the maintainers, left here because we are not changing a gate file today: #2547 was frozen at bc0e72eb07df045bdbe1232a5c870e8eca41f765 and its body no longer asks for a signature. Everything below is read from live main (276042f828bbf21716d93e1901619eab9f4e794c) as it stands.

    The asymmetry, three sites:

    // .github/scripts/pass-label.mjs:45 — case-sensitive, on the array from review-policy.json
    if (!report.writer || !writers.includes(report.writer)) return null
    
    // .github/scripts/review-gate.mjs:77-85 — the gates lowercase both sides
    const reviewers = new Set((policy?.reviewers ?? []).map((l) => String(l).toLowerCase()))
    const key = name.toLowerCase()
    if (!name || !reviewers.has(key)) continue
    
    // .github/scripts/pass-label.mjs:74-75 — the stranger path lowercases too
    const listed = new Set(writers.map((w) => String(w).toLowerCase()))
    if (listed.has(String(report.writer).toLowerCase())) return null

    So a report written Written-by: reviewer (or dev4) against a roster entry Reviewer is a conclusion for review-gate and merge-gate, but verdictOf returns null — no pass label and no comment — while strangerPassOf calls the same name listed and stays silent. #739 promises the pass record in exactly the case that goes quiet here.

    Red first (clean base, only the new case in .github/scripts/pass-label.test.mjs):

    not ok 3 - #739: a writer matches the way the gates match it, whatever its case
      error: 'reviewer is a listed name in another case'
      expected: true
      actual: ~
    # tests 12 / # pass 11 / # fail 1
    

    Fixed locally: pass-label.mjs +3/−2 (one isListedWriter(writers, name) used by both paths), test +16/−0, .github/scripts suite 669/669, negative controls reviewer3 and Reviewer Extra still null.

    Why it is not in our pull request: .github/scripts/pass-label.mjs is a gate file in policy/gates.json, so policy-sign refuses a change to it without a signed comment from another person naming the tip — its own words on #2547: policy-sign refused (#2547): a gate file changed (.github/scripts/pass-label.mjs), and the body carries no 'Policy-sign:' line naming a comment. That signature is the maintainers' decision, not ours to arrange inside a sprint; the change belongs in your file, with the roster test you already keep.

    The local branch dev/4-pass-label-writer-case and the PR #2547 stay as they are (frozen, not merged); nothing is pushed further and no workflow is asked for.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions