Skip to content

fix(source-control): map upkeep can adopt a fork PR that shares the upkeep branch name #9937

Closed

Description

@kyle-sexton

Problem

The map-upkeep flow (/testing:refresh-feature-map on main, ported to /app-control:map refresh in #9934) finds its existing upkeep PR with /source-control:pull-request list --head "<branch>". That lookup filters on the head branch name only: gh pr list --help says Filter by head branch ("<owner>:<branch>" syntax not supported), and read-pr.sh's LIST_FIELDS returns no isCrossRepository or headRepositoryOwner. On a public repository, a fork PR whose head branch has the upkeep branch's name is therefore taken as the upkeep PR. An unattended pass then pushes to origin, opens no PR, and reports the fork PR's URL as the upkeep PR.

Fix

  • list returns isCrossRepository and headRepositoryOwner.
  • The upkeep flow counts a match only when isCrossRepository is false. A same-named fork PR records blocked and is reported.

Evidence: plugins/testing/skills/refresh-feature-map/SKILL.md:177; plugins/source-control/skills/pull-request/scripts/read-pr.sh:40,96-99. Found by the security review of #9934 (finding 1). It exists on main, so it is deferred from that PR.

Activity

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

    @github-actions
    Contributor

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

    Size: Small
    Complexity: Low
    Uncertainty: Known
    Flags: none
    Blocked by: none

    Decision recorded. The owner answered in the Refinement and Issue Triage sessions (2026-10-25), deciding the open items below on the facts:
    [quote truncated; the full decision is at the link]
    #9937 (comment)

    The decision settles the open questions: the bug still stands in /app-control:map refresh, the fix is the one in the body, and it is not a security stop. The work is to add isCrossRepository and headRepositoryOwner to read-pr.sh list, have the refresh flow count a match only when isCrossRepository is false (a same-named fork PR makes the outcome blocked and is named in the report), and add a test for the two fields.

    Automated triage, applied by the lanes App.

  3. added
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    and removed
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 25, 2026
  4. melodic-automation-lanes commented on Oct 25, 2026

    @melodic-automation-lanes
    Contributor

    The latest triage verdict holds nothing: the issue is at status: ready.

  5. removed
    status: needs-decisionAwaiting a human or maintainer judgment call.
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    on Oct 26, 2026
  6. kyle-sexton commented on Oct 26, 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.

  7. added
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    and removed
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 26, 2026
  8. 1 remaining item

  9. kyle-sexton commented on Oct 26, 2026

    @kyle-sexton
    MemberAuthor

    Decision recorded. The owner answered in the Refinement and Issue Triage sessions (2026-10-25), deciding the open items below on the facts:

    go with whatever the facts/data/metrics + /discovery:research says and ensure we're aligned with SOLID, DRY/SSOT principles

    I should only ever be in the loop ONLY when necessary (intent/vision changed, something came up during implementation that impacts trajectory/alignment, OR a high security risk) - I've covered this all before

    No owner ledger (.work/*/interview-checklist.md, .work/gh-monitor-owner-decisions/) names this issue.

    The body's evidence paths moved: plugins/testing/skills/refresh-feature-map is gone from origin/main, and the upkeep flow now lives in /app-control:map refresh. The bug stands there.

    1. Is the bug still on main?
      Decided: Yes. plugins/app-control/skills/map/reference/refresh.md:214-215 finds the upkeep PR with /source-control:pull-request list --head "<branch>". read-pr.sh lists with LIST_FIELDS=number,url,state,isDraft,title,headRefName,baseRefName,mergeCommit (plugins/source-control/skills/pull-request/scripts/read-pr.sh:40) and filters on the branch name only (:94-104). gh pr list documents --head as "Filter by head branch (":" syntax not supported)" and offers isCrossRepository and headRepositoryOwner as JSON fields (https://cli.github.com/manual/gh_pr_list, read 2026-10-26).
    2. Which fix?
      Decided: The body's fix as written. list adds isCrossRepository and headRepositoryOwner to its fields; the upkeep flow counts a match only when isCrossRepository is false, and a same-named fork PR makes the outcome blocked with that PR named in the report. Basis: the same rule already guards the pr-unblock dispatchers, which skip any PR that is not "a ready same-repository PR" (.github/scripts/pr-unblock/fix-docs-dispatch.mjs:101-103).
    3. Is this an owner security stop?
      Decided: No. The harm is a wrong PR reported and a push to the repository's own upkeep branch; no token, grant or gate widens. The fix only tightens. Basis: refresh.md:214-219.

    Acceptance criteria:

    • /source-control:pull-request list returns isCrossRepository and headRepositoryOwner for each PR.
    • /app-control:map refresh treats a listed PR as the upkeep PR only when isCrossRepository is false; a same-named fork PR makes the outcome blocked and the report names it.
    • A test of read-pr.sh list asserts both fields are in the output.
  10. added
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    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 26, 2026
  11. 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.
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 26, 2026
  12. melodic-automation-lanes commented on Oct 26, 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: /source-control:pull-request list returns isCrossRepository and headRepositoryOwner, and /app-control:map refresh adopts a listed PR only when it is same-repository, recording a same-named fork PR as blocked.

    Acceptance criteria

    • plugins/source-control/skills/pull-request/scripts/read-pr.sh LIST_FIELDS (line 40) includes isCrossRepository and headRepositoryOwner, and read-pr.sh list output carries both for each PR.
    • plugins/source-control/skills/pull-request/scripts/read-pr.test.sh has a list case that asserts both fields appear in the output, and the test script passes.
    • plugins/app-control/skills/map/reference/refresh.md step 4 (lines 214-219) says a listed PR counts as the upkeep PR only when isCrossRepository is false.
    • refresh.md says a same-named PR with isCrossRepository true makes the outcome blocked, and the report names that PR.
    • refresh.md does not push to the branch or report the fork PR URL as the upkeep PR in the fork case.
    • Any other doc in plugins/source-control/skills/pull-request that lists the list fields is updated to match.

    Related work

    Sources read

    • plugins/source-control/skills/pull-request/scripts/read-pr.sh:40: LIST_FIELDS=number,url,state,isDraft,title,headRefName,baseRefName,mergeCommit, with no isCrossRepository or headRepositoryOwner
    • plugins/source-control/skills/pull-request/scripts/read-pr.sh:99: gh pr list is called with --json "$LIST_FIELDS"
    • plugins/app-control/skills/map/reference/refresh.md:214-219: upkeep PR lookup uses pull-request list --head and treats any match as the upkeep PR
    • plugins/source-control/skills/pull-request/scripts/read-pr.test.sh: exists, the place for the new list-fields test
    • issue comment 2026-10-26T20:08:28Z: owner decision confirming the bug, the fix and the three acceptance criteria

    Automated refinement by the lanes App. Intake triages the issue again with it.

  13. removed
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 26, 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

    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.priority: mediumReal value, no hard deadline; normal backlog flow.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.

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions