Skip to content

pr-pipeline: bind the read runner's run head to a trusted head job for pr-refine and fix-ci #8954

Open

Description

@kyle-sexton

Problem

pr-refine's fix-docs lane (Phase 7.1) has no trusted job that sets the PR head. Its apply-patch job takes patch-head-sha from the read runner's run-job output, which a hostile run can forge. fix-ci instead takes the head from its trusted fix-ci-context job, but the read runner's run job never checks that the head it worked on matches inputs.head-sha, so fix-ci's binding is indirect too.

The impact is low. apply-patch applies onto the write runner's own gated head, and its refusals are measured from there, so a forged value only switches between applying the fix with a three-way merge and skipping it as superseded.

Proposed fix

  • Add a trusted head job to pr-refine.yml, shaped like fix-ci-context, and pass its head as head-sha to both fix-docs calls and as patch-head-sha to apply-patch.
  • In .github/workflows/pr-run-activity-read.yml, fail the run job when the head it checks out differs from a non-empty inputs.head-sha. This also hardens fix-ci.
  • Add caller tests for both.

Context

The owner accepted this as a documented residual for Phase 7.1, with this hardening to follow (lowest-friction rule).

🤖 Generated with Claude Code

Activity

  1. github-actions commented on Oct 22, 2026

    @github-actions
    Contributor

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

    Size: Medium
    Complexity: Medium
    Uncertainty: Known
    Flags: none
    Blocked by: none

    Understood: add a trusted head job to pr-refine.yml (shaped like fix-ci-context) and feed its head to both fix-docs calls and to apply-patch. Also make the read runner's run job fail when its checked-out head differs from a non-empty inputs.head-sha, and add caller tests for both. The approach and the owner's acceptance of the residual are stated, so nothing is missing. I found no clear duplicate among the open issues.

    Automated triage, applied by the lanes App.

  2. added
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    on Oct 22, 2026
  3. removed
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    on Oct 25, 2026
  4. kyle-sexton commented on Oct 25, 2026

    @kyle-sexton
    MemberAuthor

    Released for re-triage under the owner's 2026-10-24 rule: an issue stops for a person only for an intent or vision question, a security call, a host or attended run, or spend outside prepaid credit. The hold label came off and intake triage runs again now; it re-applies a hold, with a numbered question, when one of those applies.

    To hold this issue again, add needs-human (or status: needs-decision) and say why in a comment.

  5. added
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    and removed
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 25, 2026
  6. 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: needs-decisionAwaiting a human or maintainer judgment call.
    and removed
    status: readyTriaged, unblocked, and fully specified; eligible to pick up.
    on Oct 25, 2026
  7. melodic-automation-lanes commented on Oct 25, 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).

  8. melodic-automation-lanes commented on Oct 25, 2026

    @melodic-automation-lanes
    Contributor

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

  9. melodic-automation-lanes commented on Oct 25, 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: Close the issue as already implemented, or narrow it to any gap a person confirms remains.

    Related work

    Open questions

    1. The checkout already has every proposed item, so is this issue complete and should it be closed?
      RECOMMENDED: Yes, close it as completed. If a remaining gap exists, such as the fix-ci side or a missing test, rewrite the issue to name only that gap. Basis: .github/workflows/pr-refine.yml:58
    2. Does the check in the read runner's run job meet the requirement that the job fail when the head it checks out differs from a non-empty inputs.head-sha?
      RECOMMENDED: Yes. The step compares the gate's head with inputs.head-sha before checkout, and callers.test.mjs:5690 tests it. Close unless the owner wants the comparison made against the commit actually checked out. Basis: .github/workflows/pr-run-activity-read.yml:641

    Sources read

    • .github/workflows/pr-refine.yml:58: the fix-docs-head job reads the PR head SHA in a job that runs no head code and outputs head-sha
    • /.github/workflows/pr-refine.yml:134: fix-docs passes head-sha from needs.fix-docs-head.outputs.head-sha (the same input is passed at line 209 for fix-docs-subscription)
    • .github/workflows/pr-refine.yml:251: apply-docs-patch passes patch-head-sha from needs.fix-docs-head.outputs.head-sha
    • .github/workflows/pr-run-activity-read.yml:641: the step 'Assert the gated head is the caller's' fails the run when the gate's head differs from a non-empty inputs.head-sha
    • .github/actions/resolve-config/callers.test.mjs:3953: asserts both fix-docs calls pass head-sha from fix-docs-head
    • .github/actions/resolve-config/callers.test.mjs:5508: asserts apply-patch's patch-head-sha is fix-docs-head's head
    • .github/actions/resolve-config/callers.test.mjs:5690: tests the 'Assert the gated head is the caller's' step

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

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: lowNice-to-have, cosmetic, or speculative; opportunistic.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 Task.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions