Skip to content

GitHub /builder approves the newest plan on the issue, not the one the approver read #233

Description

@Julian-Genuario

Summary

A /builder comment on GitHub is not bound to a specific plan. githubPlanAcceptance (services/api/src/github/router.ts:641-674) picks the newest plan_acceptance proposal on the issue whose Architect run succeeded, and the approval is written against whatever came back:

.where(and(... eq(actionTypes.name, "plan_acceptance"), eq(runs.status, "succeeded"), ...))
.orderBy(desc(proposals.createdAt))
.limit(1)

COMMAND_RE (router.ts:60-61) carries nothing that could disambiguate, and nothing supersedes or expires an older open proposal when a newer Architect run succeeds on the same issue. The API lane is proposal-id-bound (POST /v1/proposals/:id/decide), so this is GitHub-only.

Scenario

A reviewer reads plan P1 (proposal A, published with the facility:architect-plan:<run>:<proposalA> marker). A teammate re-runs /architect; run 2 succeeds and opens proposal B seconds before the reviewer's /builder webhook is processed — or the reviewer simply never noticed the second publication comment. Facility approves B, dispatches the Builder on P2, and the audit trail records that the reviewer approved P2 with a valid plan hash and fresh evidence. Every downstream check in builder-plan-policy.ts passes, because they verify that the approved proposal is consistent, not that it is the one the human read.

Why it matters

Gate 1 is the product invariant ("a person accepts the plan"). Here the person accepted a plan; which one is decided by ordering.

Suggested fix

Either require the proposal id (or a plan-hash prefix) in the command — /builder <proposalA> — with the bare form accepted only when exactly one live proposal exists on the issue; or supersede older open plan_acceptance proposals when a new Architect run succeeds on the same issue, and refuse /builder while more than one is live. Both keep the current UX for the common case.

Adjacent but different from #222/#223 (comments invalidating an approved plan) and #147 (base commit not recorded).

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