Skip to content

fix(controller-delivery): make GitHub PR delivery capability explicit, preflighted, and recoverable #115

Description

@James3014

Problem

DevSpace can now carry a controller far enough to plan, delegate implementation, independently verify a Candidate, commit it, and push a non-default branch — but the controller can still discover only at the very end that it has no usable GitHub PR-delivery channel.

This is a control-plane reliability defect, not a repository-specific implementation defect.

Fresh observed incident — DevSpace #112

During #112 (durable owner task ledger + queue-safe targeted dispatch):

  • GPT-5.6 Sol xhigh acted as controller.
  • Luna performed the implementation work.
  • Sol independently reviewed the resulting Candidate.
  • Focused verification passed for the fix(chat-swarm): add durable owner task ledger and queue-safe targeted dispatch #112 source/test surface.
  • The accepted Candidate was rebound to fresh main and pushed as:
    • branch: codex/issue-112-chat-swarm-ledger
    • candidate head: d10784e79eb08671cfd03bdbd9fbe04f17805c71
    • parent/fresh main at delivery: 2d1aff574306740bd3d261a7d90fa383dbb6d375
    • exact Candidate delta: 12 files, +100/-14
  • At PR-delivery time the Codex controller environment could not complete gh pr create because its GitHub write path was unavailable (observed auth/network/browser-channel failure modes).
  • A different pre-existing PR (fix(chat-swarm): add durable owner task ledger and queue-safe targeted dispatch (#112) #113) existed, but its provenance and Candidate identity did not match the accepted Sol/Luna Candidate and therefore could not be substituted.

This left the task in an undesirable partial state:

IMPLEMENTED
→ independently accepted Candidate
→ branch pushed
→ PR not deliverable by controller
→ CI / MERGE_READY not reachable without another actor manually taking over

The controller did the hard engineering work, but delivery still depended on ad-hoc human/operator intervention.

Goal

Make GitHub PR delivery a first-class, explicit, preflighted and recoverable DevSpace capability so a controller can safely complete:

accepted Candidate
→ exact branch push
→ create-or-recover exact PR
→ read exact PR identity
→ observe CI
→ report MERGE_READY / BLOCKED

without gaining broad or ambiguous GitHub authority.

Design principles

  1. Delivery capability is separate from model capability.
    A controller being able to reason, edit, test, commit, or push does not prove it can create/read a PR.

  2. Preflight before expensive engineering.
    If PR delivery is required by the task contract, DevSpace should know before implementation whether a valid delivery lane exists.

  3. Exact Candidate identity.
    PR creation must bind at least:

    • repository
    • base branch / exact base SHA
    • candidate branch
    • exact candidate head SHA
    • optional Issue binding
  4. No arbitrary GitHub write authority.
    A fallback delivery seam must be bounded to the accepted Candidate and requested PR operation, not expose general GitHub mutation.

  5. Remote effect reconciliation.
    timeout / 502 / lost acknowledgement != PR was not created.
    Reconcile the same logical delivery effect before retrying.

  6. No duplicate PR creation.
    An identical replay must resolve to the existing matching PR; changed material must fail closed.

  7. Delivery state is not merge authority.
    PR_DELIVERED, CI_GREEN, MERGE_READY, and MERGED remain distinct states.

Required work

G0 — GitHub delivery preflight

Add an effect-free preflight for the exact intended repository/delivery path.

It should distinguish at least:

  • GITHUB_PR_READY
  • GITHUB_AUTH_UNAVAILABLE
  • GITHUB_NETWORK_UNAVAILABLE
  • PR_WRITE_UNAVAILABLE
  • PR_READ_UNAVAILABLE
  • REPOSITORY_NOT_AUTHORIZED
  • CANDIDATE_IDENTITY_INVALID

Preflight must not claim push permission merely because read access works; if proof requires mutation, report that dimension separately/unknown.

G1 — Bounded PR-delivery operation

Provide one typed, durable operation for an already accepted and already pushed Candidate.

Illustrative contract:

create_pr_delivery(
  repository,
  candidateBaseSha,
  candidateHeadSha,
  candidateBranch,
  baseBranch,
  title,
  body,
  issueNumber?,
  attemptKey
)

The operation must re-read remote branch/base identities before any write and refuse on drift.

G2 — Durable operation identity / reconcile-before-retry

Persist a stable operation identity before the first possible GitHub write.

After timeout/disconnect/502:

same delivery operation
→ search/read remote PR state using exact candidate head + repository/base binding
→ classify:
   COMPLETED
   CONFIRMED_NO_EFFECT
   OUTCOME_UNKNOWN
→ only retry when safe

Do not create a new attempt just because the local tool response was lost.

G3 — Exact PR identity and idempotency

Acceptance requirements:

  • same exact Candidate replay resolves to the same PR;
  • no duplicate open PR for the same exact delivery identity;
  • changed head/base/title/body material under the same attempt key fails closed;
  • a PR for a different Candidate cannot be substituted merely because it references the same Issue;
  • pre-existing PRs are matched by physical Candidate identity, not title similarity.

G4 — Controller-visible delivery lifecycle

Expose structured states such as:

CANDIDATE_READY
BRANCH_PUSHED
PR_DELIVERY_PREPARED
PR_DELIVERY_IN_FLIGHT
PR_DELIVERED
CI_PENDING
CI_GREEN
CI_FAILED
MERGE_READY
PR_DELIVERY_RECONCILE_REQUIRED

A controller must not report delivery complete before an exact remote PR readback exists.

G5 — CI/readback seam

After PR creation, provide a bounded read path for:

  • exact PR number/url
  • exact base/head SHA
  • mergeability / draft state
  • required/current checks
  • terminal CI result

This is readback only; it does not grant merge.

Preferred architecture

If the Codex/worker sandbox already has a valid GitHub PR channel, use it.

If it does not, DevSpace may provide a narrow host-side delivery seam bound to the exact accepted Candidate. That seam must not become a generic shell/GitHub escape hatch.

Recommended separation:

semantic/controller lane
  plans / accepts Candidate
        ↓
DevSpace exact Candidate receipt
        ↓
bounded GitHub delivery seam
        ↓
GitHub PR
        ↓
read-only CI / merge-readiness evidence

Required hostile / failure tests

At minimum:

  1. auth unavailable at preflight;
  2. network unavailable at preflight;
  3. exact branch missing;
  4. remote branch head drift after local acceptance;
  5. base branch drift;
  6. PR create succeeds remotely but local response is lost;
  7. identical replay after lost response finds same PR;
  8. conflicting replay fails closed;
  9. an unrelated/pre-existing PR for the same Issue is not accepted as the target PR;
  10. duplicate PR prevention under concurrent/replayed delivery;
  11. read access available but write unavailable;
  12. CI readback after successful delivery;
  13. controller restart/reconnect can recover the durable delivery operation;
  14. no merge/deploy authority is gained by this feature.

Live acceptance

Use a real Sol controller on macOS with one bounded engineering task and prove:

fresh GitHub delivery preflight
→ Sol controller delegates implementation
→ independent Candidate acceptance
→ exact commit/push
→ controller invokes bounded PR delivery
→ remote PR exact-head readback
→ CI terminal readback
→ MERGE_READY or typed BLOCKED result

Acceptance must require:

  • 0 manual gh pr create intervention by James/coordinator;
  • 0 duplicate PRs;
  • 0 wrong-Candidate PR attribution;
  • 0 blind retry after ambiguous GitHub write;
  • exact base/head/branch evidence;
  • exact PR readback;
  • CI state readback;
  • no merge, deployment, release, or public-claim authority gained.

Relationship to existing issues

Claim ceiling

Until this closes, the strongest safe statement is:

DevSpace can produce and push accepted Candidates on some controller paths, but end-to-end controller-owned PR delivery / CI closure is not reliably proven and may require manual operator intervention.

Activity

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions