Skip to content

fix(source-control): the verdict reader's full artifact listing skips live artifacts at page boundaries, FAILs included #10486

Closed

Description

@kyle-sexton

Problem

The pr-verify verdict reader's full artifact listing (babysit_verify_evidence.py Reader._read_listing, GET repos/{repo}/actions/artifacts?per_page=100 paged through babysit_gh._run_pages) can skip a live artifact, a FAIL verdict included. It reads 69 pages over 50 to 58 s. When an artifact on an earlier page expires while later pages are read, every later artifact moves up one place, and the artifact at the next page boundary is never returned. An upload in the same window pushes artifacts down instead, and shows up as a duplicate.

A FAIL the listing skips does not count. The gate can then allow a pull request that verify:fail, verify:earlier-fail or the repeated-fail cap would refuse.

Evidence (2026-10-27, read-only probes against this repository)

Each full listing was taken right after an incremental listing whose size equalled the first page's live total_count:

Probe (UTC) Full listing rows / unique Live artifacts the full listing lacked Index each should have been at
15:49 6,860 / 6,855 (5 duplicates, each at a page boundary) pr-verify-8172-31a67e97…-fail (12247626604) 6100
15:53 6,862 / 6,862 none n/a
16:02 6,863 / 6,863 pr-verify-9476-56282b92…-fail-i7152 (12525406048), pr-verify-10024-e471ea68…-pass (12601608922), patch-work-resolve-issue-resolve-issue-1 (12606360050) 4600, 3500, 3400

GET repos/{repo}/actions/artifacts/{id} answered each missing id with expired: false after the probe. Every missing index is a multiple of 100, which is a page boundary.

Scope

#10488 closes this. The reader keeps no listing, whole or incremental, whose unique artifact ids are not exactly the live total_count. A whole listing is deduplicated by id, added to the whole listings read before it, and checked by a top-up read from page 1. After three whole listings that never hold the count, the pull request refuses verify:unreadable; it never allows. The refresh before an allow reads the same way.

Options

  1. Check the full listing against total_count too: re-read when the unique count differs. Under steady churn this may need several reads (UNVERIFIED how often).
  2. Read the next page from an id cursor rather than a page number, if the API offers one. The docs list no such parameter (UNVERIFIED).
  3. Overlap each page with the previous one, and check that the boundary artifacts match.

Activity

  1. added
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 27, 2026
  2. github-actions commented on Oct 27, 2026

    @github-actions
    Contributor

    Proposed work class: work-class: scoped
    Basis: automated intake triage; the lanes App applied this class.

    Size: Small
    Complexity: High
    Uncertainty: Knowable
    Flags: none
    Blocked by: none

    Understood: the pr-verify verdict reader's full artifact listing pages through actions/artifacts over about a minute. Artifacts that expire or are uploaded mid-read shift the pages, so an artifact at a page boundary can be skipped, including a FAIL verdict. The gate may then miss a FAIL. Your evidence shows the missing artifacts sit at multiples of 100. The three options you list are approaches to check, not open intent questions. Which one works depends on what the API supports, and reading or testing would settle that. I found no open duplicate.

    Automated triage, applied by the lanes App.

  3. added
    priority: mediumReal value, no hard deadline; normal backlog flow.
    and removed
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 27, 2026
  4. added
    status: readyTriaged, unblocked, and fully specified; eligible to pick up.
    work-class: scopedA briefed fix or small feature; blast radius bounded by the brief, tests exist.
    status: confirmedA maintainer confirmed the item is real and in scope; ready to be worked.
    and removed
    status: readyTriaged, unblocked, and fully specified; eligible to pick up.
    on Oct 27, 2026
  5. melodic-automation-lanes commented on Oct 27, 2026

    @melodic-automation-lanes
    Contributor

    Not admitted to the issue lane: no trusted-human acceptance criteria: a listed human must put the confirmed - [ ] criteria in the body or post them as their own comment, or, on an issue a listed human filed, intake's newest refinement comment must hold them unedited. Admission takes the issue once each of those is met, when intake triages it again (.github/actions/resolve-config/admission-rules.json).

  6. melodic-automation-lanes commented on Oct 27, 2026

    @melodic-automation-lanes
    Contributor

    The latest triage verdict holds nothing: admission moved the issue to status: confirmed; its comment says why.

  7. melodic-automation-lanes commented on Oct 27, 2026

    @melodic-automation-lanes
    Contributor

    Refinement. A read-only run explored this repository, searched its issues and pull requests, and read the allowed docs to draft what follows. On an issue a listed human filed, it counts as the issue's spec once posted, while unedited; it is still model text, so check it.

    Intended outcome: The full artifact listing is checked against the live total_count, and is re-read or refused as unreadable when it is incomplete, so a skipped FAIL cannot pass the gate.

    Related work

    Open questions

    1. If the full listing still disagrees with total_count after the allowed re-reads, should the reader refuse as unreadable (fail closed) or accept the listing?
      RECOMMENDED: Fail closed: raise _Unreadable so the gate refuses, as it does for other unreadable listings. Basis: plugins/source-control/skills/babysit-prs/scripts/babysit_verify_evidence.py:805
    2. Which option and how many re-reads: option 1 (compare the unique count to total_count, re-read up to N times) or option 3 (overlap pages)?
      RECOMMENDED: Option 1 with a small fixed cap of 2 re-reads. Dedup by artifact id first, then compare to the first page's total_count. Keep the whole read inside LISTING_TIMEOUT_SECONDS, because each read takes about 55 s. Basis: judgment
    3. Is the id cursor of option 2 available?
      RECOMMENDED: No. The docs list no cursor parameter for actions/artifacts, so drop option 2 unless a docs page shows one. Basis: judgment

    Sources read

    • plugins/source-control/skills/babysit-prs/scripts/babysit_verify_evidence.py:866: _read_listing runs gh api actions/artifacts?per_page=100 --paginate --slurp inside a retry loop bounded by LISTING_TIMEOUT_SECONDS, with no completeness check
    • plugins/source-control/skills/babysit-prs/scripts/babysit_verify_evidence.py:802: the paged result is only flattened by flatten_paginated_items
    • plugins/source-control/skills/babysit-prs/scripts/babysit_postmerge.py:447: an existing helper that checks total_count against the items returned and raises on a short answer, reusable as a pattern

    To answer, post a comment that starts with this line:

    **Decision recorded.** <answer>
    

    Automated refinement by the lanes App. Answer with a comment whose first line is **Decision recorded.**.

  8. added a commit that references this issue on Oct 27, 2026
    fb1ca60
  9. added
    priority: highSignificant impact, or blocks an imminent release; staff this cycle.
    and removed
    priority: mediumReal value, no hard deadline; normal backlog flow.
    on Oct 27, 2026
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

    priority: highSignificant impact, or blocks an imminent release; staff this cycle.status: confirmedA maintainer confirmed the item is real and in scope; ready to be worked.status: needs-decisionAwaiting a human or maintainer judgment call.work-class: scopedA briefed fix or small feature; blast radius bounded by the brief, tests exist.

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions