Skip to content

feat: optional remote Integration Closeout after local unit-merged (done ≠ origin) #270

Description

@Haven2026

Describe your idea

After a story reaches story-done + unit-merged, bmad-loop only merges locally into target_branch (engine._merge_local → journal unit-merged). There is no first-class path that:

  1. pushes the unit branch (or the merge result) to a remote,
  2. opens / updates a GitHub PR,
  3. waits for merge to the remote default branch, and
  4. treats remote containment as part of the story Done gate.

Today “done” means: local commit + local merge + sprint-status advanced. For any project whose real gate is code on origin/main (or equivalent remote), that semantic gap forces an external salvage layer.

Why is this needed?

Field use (bmad-loop 0.8.1, scm.isolation = "worktree", multi-story epics, unattended overnight + daytime salvage):

  • Journal / board / auto-dev specs can all say done while origin never saw the product code.
  • Continuity gates and human/AI supervisors then redispatch work that already exists only in a local merge or a kept worktree — or, worse, mark progress complete when the shared trunk is empty.
  • Related unattended field report: Field report: a week of unattended overnight runs — what held, and three gaps #232 (“nothing defends the layer above the run”). Remote closeout is the missing in-engine piece for projects that already use GitHub as the integration ledger.

This is not “put an LLM in the control loop.” It is deterministic SCM after the creative sessions finish — same philosophy as local merge-back, one hop further for remote-first teams.

How should it work? (proposal, not a PR)

Keep local merge as default (no behavior change for current users). Add an opt-in SCM policy, for example:

[scm]
# existing: isolation, merge_strategy, delete_branch, ...
# new (sketch):
closeout = "local"          # default — today's behavior
# closeout = "remote"       # require remote integration before story Done
# remote_mode = "push-pr"   # push unit branch + open PR (gh)
# remote_mode = "push-merge" # push merge commit / ff to tracking branch (advanced)
# remote_target = ""        # empty = origin/<default>
# require_remote_sha = true # journal unit-remote-merged only when origin contains sha

Suggested contract

Event Meaning
unit-merged local merge into run target (unchanged)
unit-remote-pending branch/PR opened, waiting
unit-remote-merged remote default contains the story commit (or PR merged)
story-done when closeout=remote only after unit-remote-merged (or explicit policy skip)

Journal should record merge_scope: local|remote so supervisors do not misread local merge as ship.

Out of scope for v1 (fine to refuse): multi-remote, protected-branch policy negotiation, admin-bypass CI — those stay project hooks. v1 can be: push + gh pr create + poll mergeability + fail-closed escalate with kept branch (same spirit as local merge conflict escalate).

Acceptance ideas

  • Default closeout=local: byte-identical to 0.8.1 for existing suites.
  • With closeout=remote + mock/gh fixture: story does not journal terminal Done until remote SHA observed; failure escalates with branch kept (keep_failed).
  • Docs: one paragraph in unattended / SCM policy: “local unit-merged ≠ origin.”

PR

Willing to implement after maintainer design nod (per CONTRIBUTING: feature/large change → Discord first). Prefer design agreement on policy keys + journal events before any code PR. Ideal PR size: 200–400 LOC for policy + journal + one adapter seam; no bulk refactor.

Additional context

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