Skip to content

Per-change resources and automatic conflict resolution - #90

Merged
jflavan merged 3 commits into
mainfrom
feat/conflict-resolution
Oct 28, 2026
Merged

jflavan merged 3 commits into
mainfrom
feat/conflict-resolution

Conversation

@jflavan

@jflavan jflavan commented Oct 28, 2026

Copy link
Copy Markdown
Owner

Changes that run at the same time (speed.max_parallel_changes) already had their own worktrees. This closes the two gaps that were left.

Per-change resources

Every claimed change's commands, and the sessions that may run commands, now get:

  • Own temp folder: TMPDIR/TMP/TEMP set to a short per-slot folder in the system temp dir. It is removed when the step ends, and leftovers are swept at the next tick.
  • Own ports: a block of ports checked free when handed out, in LOOPSTRA_PORT_BASE and LOOPSTRA_PORT_COUNT. Set with speed.ports_per_change (10 by default, 0 for none) and speed.ports_from (20000).
  • Optional per-slot tool caches (speed.own_caches, off): XDG, npm, bun, yarn, pip and go. They are kept warm between changes.

Automatic conflict resolution

A conflict with main used to block the change for an engineer. Now it is resolved in three places:

  • At review start, when main gained code since the build.
  • In the merge checks.
  • While the change waits for a person at the merge gate, as soon as it no longer merges cleanly (git merge-tree, git 2.38+). This covers both the local status gate and pull requests waiting for approval.

How it works:

  • Squash, then resolve: on a conflict, the branch is squashed onto main. A new resolve step (strong by default) gets both changes' intent and each conflict with both sides. The squash commit keeps people's credit with Co-authored-by.
  • Tested, then reviewed: every resolution is tested, then reviewed in one more round. The reviewer is shown the conflicts as they were and what the agent kept ({{resolution}}). These rounds don't use up max_rounds; stages.review.max_resolutions (3) caps them.
  • When it fails: the branch is left as it was, and conflict.md records the files, both sides and the reason. Binary conflicts go to a person.
  • Interruptions: a stop, an outage or a crash part-way is undone, and doesn't count against the cap.
  • Approvals: with merge.human: pr, only an approval given after the resolution was pushed counts, and the pull request's wait says so. Before resolving a waiting pull request, main is synced and a person's commits on it are taken in.

Design: docs/superpowers/specs/2026-10-27-conflict-free-concurrency-design.md. The calls made along the way are in docs/decisions.md.

Testing

  • New tests:
    • tests/unit/resources.test.ts
    • tests/unit/conflicts.test.ts: resolved, refused, markers left, cap, cap off, binary, waiting, review start, crash recovery, co-authors
    • approvedAt parsing
    • lanes integration: clash resolved, clash unresolvable, waiting change caught up
    • a remote end-to-end: a person's commit kept, stale approval ignored, fresh approval merges
  • Results: bun run check is green (1877 pass). A high-effort /code-review found 10 issues, all fixed in the second commit.

🤖 Generated with Claude Code

jflavan and others added 3 commits October 27, 2026 21:48
Changes running at the same time no longer share a temp folder or ports: every
claimed change's commands and command-running sessions get their own TMPDIR,
a block of ports checked free (LOOPSTRA_PORT_BASE/COUNT), and optionally
per-slot tool caches (speed.own_caches).

A conflict with main is now resolved instead of blocking: at review start, in
the merge checks, and while a change waits for a person at the merge gate. The
branch is squashed onto main, a new `resolve` step (strong) resolves the
conflicts, and the resolution is tested and reviewed (the reviewer sees both
sides and what was kept) before anything merges. Failures leave the branch as
it was and write conflict.md. Resolution reviews do not use up max_rounds;
stages.review.max_resolutions caps them. With merge.human: pr, only an approval
given after a resolution counts.

Design: docs/superpowers/specs/2026-10-27-conflict-free-concurrency-design.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A pull request waiting for a person is caught up from GitHub's copy first:
  main is synced and a person's commits on the pull request are taken in.
- An approval counts only when given after the resolution was pushed
  (resolved-at is pending until then), and the wait says why.
- Resolution commits may be empty; new files the resolve session's commands
  left are removed before saving; binary conflicts go to a person.
- TMPDIR is a short folder in the system temp dir (Unix socket path limits).
- merge-tree drops --quiet; the waiting check is cached by commit.
- max_resolutions 0 or used up still writes conflict.md, with its own note.
- catchUpTested replaces the duplicated catch-up-then-test blocks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jflavan
jflavan merged commit 2dba8d4 into main Oct 28, 2026
13 checks passed
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.

1 participant