Skip to content

feat: Add new merge strategy option#589

Open
TadejPolajnar wants to merge 3 commits into
mattpocock:mainfrom
TadejPolajnar:feat/merge-strategy-pr-mode
Open

feat: Add new merge strategy option#589
TadejPolajnar wants to merge 3 commits into
mattpocock:mainfrom
TadejPolajnar:feat/merge-strategy-pr-mode

Conversation

@TadejPolajnar
Copy link
Copy Markdown

Summary

Adds a merge strategy selection to sandcastle init — a new step in the CLI wizard (between backlog manager and template) that controls what happens to completed branches:

  • Merge to HEAD (default) — existing behavior, branches merge directly into the host's current branch
  • Pull Request (GitHub) — pushes each completed branch to origin and opens a PR via gh pr create on the host

The PR-mode flow: host runs pre-flight checks (origin remote, gh >= 2.4 + auth, attached HEAD), resolves the repo's default branch, then for each completed branch: pushes, invokes a short-lived PR-author agent that emits title/body via structured tags, and runs gh pr create with shell-escaped args. Re-runs are idempotent (open PRs get updated, closed/merged ones are skipped, force-push is never attempted).

What changed

  • sandcastle init wizard gains a "Select a merge strategy" step (skipped for blank template)
  • --merge-strategy CLI flag for non-interactive use
  • All 4 merging templates (simple-loop, sequential-reviewer, parallel-planner, parallel-planner-with-review) ship .merge-to-head and .pull-request variant files
  • ADR 0010, README section, CONTEXT.md terms, changeset

Test plan

  • 216 unit tests passing (registry, scaffold matrix, variant selection, invariants)
  • Typecheck clean
  • Manual: sandcastle init → simple-loop → Pull Request → github-issues scaffolds correct files with PR-mode markers and no force-push patterns

@vercel
Copy link
Copy Markdown

vercel Bot commented May 7, 2026

@TadejPolajnar is attempting to deploy a commit to the Matt Pocock's projects Team on Vercel.

A member of the Team first needs to authorize it.

Phase 1 of the pull-request merge-strategy rollout. Adds the registry,
CLI flag/selector, and copyTemplateFiles variant-suffix support without
shipping any template variants yet. Existing template output is
unchanged on this commit.

- Add MergeStrategyEntry registry (merge-to-head, pull-request)
- Add --merge-strategy CLI flag and interactive clack selector
  (skipped for the blank template, which has no merge phase)
- copyTemplateFiles now accepts a mergeStrategy and selects between
  filename-suffixed variants (foo.merge-to-head / foo.pull-request)
- Add CLOSES_LINE to BacklogManagerEntry.templateArgs for use by
  Phase 2 PR-mode templates (Closes #N gated to github-issues)
- Defensive guard against malformed variant filenames
- 9 new InitService tests + 2 new cli.test.ts tests

Spec: ideas/merge-strategy-pull-request.md
Phase 2 of the merge-strategy rollout. Adds the four merging templates'
pull-request variants and the supporting host-side flow.

Per template (simple-loop, sequential-reviewer, parallel-planner,
parallel-planner-with-review):
- Rename existing main.mts -> main.mts.merge-to-head
- Add main.mts.pull-request: host pre-flight (origin, gh >= 2.4, gh
  auth, attached HEAD, default-branch resolution), per-issue push, PR
  state pre-check via gh pr list, structured-output PR-author agent,
  gh pr create on host with shellEscape, no force-push.
- Add pr-prompt.md.pull-request: agent emits <pr-title> + <pr-body>
  only; never runs gh or git push.

Templates that resolve issues on the host (simple-loop and
sequential-reviewer):
- Variant-ize prompt.md / implement-prompt.md so the pull-request
  variant locks the agent to the host-resolved {{TASK_ID}}, while the
  merge-to-head variant retains its agent-picks-the-issue behavior.
- Lock-until-merged loop control: each iteration first lists open
  Sandcastle PRs and continues working on the lowest-numbered one
  rather than picking a new backlog item.
- Backlog-manager runtime guard: clear error if {{LIST_TASKS_COMMAND}}
  returns a non-github-issues shape.

Parallel-planner family:
- main.mts.pull-request overrides planner-emitted branch names with
  deterministic 'sandcastle/issue-<id>-<slug>' (slug from issue title).

Supporting changes:
- TEXT_FILE_EXTENSIONS now includes .mts and .ts so PR-mode main.mts
  variants pick up the backlog-manager-specific {{LIST_TASKS_COMMAND}}
  and {{CLOSES_LINE}} at scaffold time.

Tests (198 passing, +20 from Phase 1):
- Variant selection per template (merge-to-head/pull-request vs
  pull-request main.mts content markers).
- pr-prompt.md presence + CLOSES_LINE substitution per backlog manager.
- Variant file pairing invariant catches missing .merge-to-head /
  .pull-request partners.
- Dockerfile and .env.example are byte-identical between merge
  strategies (proves §4.7 of the spec).
- 16-combo placeholder-leak invariant: caught the .mts substitution
  bug above before merge.

Spec: ideas/merge-strategy-pull-request.md
@TadejPolajnar TadejPolajnar force-pushed the feat/merge-strategy-pr-mode branch from 7adb308 to b5aa654 Compare May 7, 2026 06:14
@TadejPolajnar TadejPolajnar changed the title Add init-time merge strategy option (merge-to-head | pull-request) feat: Add new merge strategy option May 7, 2026
@Jamerrone
Copy link
Copy Markdown

Jamerrone commented May 12, 2026

@TadejPolajnar This is exactly what I was looking for, so I really appreciate that someone is working on it. Can I mention 2 things? It might be worth keeping the timestamp in the log file name, this in case something goes wrong, and you need to retry (API limit or whatever other reason). More importantly, I feel like this removes a rather good feature from the existing sequential-reviewer template. The issue selector is now "dumb" it tries to work on the first one (newest) but this gets weird when you use the to-issues skill because often the oldest one is the one that needs to be picked up first. Also, because to-prd also adds the ready-for-agent label, it now tries to pick up the PRD issue. The current template has the "pick the best next issue" logic which works very well around all of these issues, it never failed picking the correct one as it reads all of them, see what is blocked by what etc. This is all lost now.

Edit: Is it correct that once there is an open PR the loop just kinda stops? Because right now it keeps selecting the same issue over and over and each iteration just says Locked to open PR #26 (issue 5: feat: add Projectile EntityKind and flight system) even when there are 22+ issues open.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants