Problem class: python-cli-subcommand-merge-conflicts
Written to ~/solution.md. Full solution below.
Problem class: python-cli-subcommand-merge-conflicts
Scope: one shared CLI module (e.g. lore/__main__.py) touched by N parallel workers, each adding a subcommand plus a new top-level import.
Observed: despite pinning disjoint parser anchors for every worker, merging branch B after branch A produced one CONFLICT (content) in the import block. Resolution was mechanical: keep both import sets in a single spliced edit, then re-run the full gate suite on the merged tree.
Git's 3-way merge is line/hunk based, and it reasons from the merge base, not from intent.
Each worker's diff is:
import argparse
+import json # worker A's additive import
Because every worker chooses the same anchor line (import argparse, followed by the same blank lines), their diffs land in the same hunk region. Git has no notion that import statements are order-independent and additive — it only sees that two branches modified the same context lines:
<<<<<<< HEAD
import json
=======
import pathlib
>>>>>>> taskB
Pinning function-body anchors (A after match's add_parser, C at the end of build_parser) makes those regions auto-merge cleanly, but it does not help the import edit. The import block is a second, shared edit point that the anchors never addressed. The result: 1 conflict per additional wave branch, always at the same lines.
Git will not auto-merge “add a line here” on both sides when the insertion point and its surrounding context are identical. That is why this is unavoidable given per-line imports in a shared file.
Save this guard-railed resolver as tools/resolve_import_conflicts.py. It unions conflict hunks only when every conflicted line on both sides is blank or an import statement; it aborts on anything else, so it can never silently splice real code.
#!/usr/bin/env python3
"""Union-resolve git conflicts confined to a Python import block.
Every conflicted line on both sides must be blank or an import statement.
If any hunk contains real code, abort and let a human resolve.
Usage: resolve_import_conflicts.py path/to/file.py
"""
import pathlib
import re
import sys
IMPORT_RE = re.compile(r"^\s*(import\s+\S|from\s+\S+\s+import\s+)")
def is_import_or_blank(line: str) -> bool:
return line.strip() == "" or bool(IMPORT_RE.match(line))
def main(path: str) -> int:
p = pathlib.Path(path)
lines = p.read_text().splitlines(keepends=True)
out, i, resolved = [], 0, 0
while i < len(lines):
if not lines[i].startswith("<<<<<<< "):
out.append(lines[i]); i += 1; continue
ours, theirs, i = [], [], i + 1
while not lines[i].startswith("======="):
ours.append(lines[i]); i += 1
i += 1
while not lines[i].startswith(">>>>>>> "):
theirs.append(lines[i]); i += 1
i += 1
for line in ours + theirs:
if not is_import_or_blank(line):
sys.stderr.write(f"refusing: non-import conflict line {line!r}\n")
return 2
seen, union = set(), []
for line in ours + theirs:
if line not in seen:
seen.add(line); union.append(line)
out.extend(union); resolved += 1
p.write_text("".join(out))
print(f"resolved {resolved} import hunk(s) in {path}")
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1]))
Merge recipe (run once per wave branch):
git checkout main
for b in taskB taskC; do
if ! git merge --no-edit "$b"; then
python tools/resolve_import_conflicts.py lore/__main__.py
git add lore/__main__.py
git commit --no-edit
fi
done
# MANDATORY: re-validate the merged tree, not the branches
bash gates.sh # or: make gates / pytest / CLI smoke, whichever the repo uses
The single spliced edit produces the correct combined import block:
import argparse
import json
import pathlib
import re
Commit a .gitattributes entry so git auto-unions future additive import edits:
# Import-only union. Safe because Python imports are order-independent/additive.
lore/__main__.py merge=union
union is a built-in driver (no .git/config setup needed), so once .gitattributes is committed it applies for everyone:
git merge -q --no-edit taskA taskB taskC # no manual resolution
Caveats — do not skip these: - Scope it to the one file, never repo-wide. - It concatenates both sides of any conflicting hunk, so it can duplicate or reorder lines and can mask a genuine semantic conflict. - It is only safe for append-only import blocks. Keep the post-merge gate suite as the real safety net. - A human should still review the merged import list for unused/duplicate modules.
Eliminate the shared edit point entirely — then no import conflict exists:
import json, import pathlib, import re in the base file. Workers then edit only their disjoint parser regions and no import hunk is ever created.build_parser never edits the top block:
python
def _run_scan(args):
from lore.scan import scan
return scan(args)__main__.py thin. Register subcommands from self-contained modules (lore/commands/scan.py, …) each owning its own imports.Rule of thumb: if a wave touches one CLI module, budget exactly one mechanical import resolution + one full gate re-run, or pre-reserve the imports to make that budget zero.
Reproduction performed on a fresh repo: base lore/__main__.py with three stable, disjoint anchors (match, init, and end-of-build_parser); three branches each add one top-level import at the shared anchor plus one subcommand at a distinct anchor.
Conflict reproduced (import block only, exactly as reported):
Auto-merging lore/__main__.py
CONFLICT (content): Merge conflict in lore/__main__.py
>>> conflict merging taskB (import block only):
lore/__main__.py:2:<<<<<<<
>>> conflict merging taskC (import block only):
lore/__main__.py:2:<<<<<<<
Both parser regions auto-merged; every conflict was confined to line 2 (the import block).
Immediate fix (tools/resolve_import_conflicts.py):
resolved 1 import hunk(s) in lore/__main__.py
resolved 1 import hunk(s) in lore/__main__.py
Final merged import block and parser:
import argparse
import json
import pathlib
import re
...
match / scan / init / index / show / grep all registered
Gates on the merged tree:
. [100%]
1 passed in 0.00s
GATES OK
Preventive union driver (2b):
Auto-merging lore/__main__.py
Auto-merging lore/__main__.py
auto-merged all three (no manual resolution)
no conflict markers
. [100%]
1 passed in 0.00s
GATES OK
Guard demonstrated: the resolver correctly refused a hunk containing non-import code (refusing: non-import conflict line ' scan = sub.add_parser("scan", ...)'), and a deliberately mis-placed subcommand (inserted after return parser) was caught by the test gate — confirming the “always re-run gates after a union resolution” requirement.
# after each merge resolution on the real lore tree
python -m pytest -q tests
python -m lore --help
python -m lore match x && python -m lore scan x && python -m lore index x && python -m lore grep x
git grep -n '<<<<<<<' -- ':!*.md' # must print nothing
All four checks must pass, and there must be zero conflict markers, before the wave is considered merged.
# Evidence - Problem class: python-cli-subcommand-merge-conflicts - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-24T17:05:35.455Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "lore (get-h3/lore) wave of 3 workers all adding CLI subcommands to one lore/__main__.py build_parser. Mitigated BEFORE dispatch: each brief pinned an anchor point (task A after the match add_parser, task C at the very END of build_parser) so edit regions were disjoint; still produced one mechanical 3-way import-block conflict at merge (both sides additive at the same import). Resolution: keep both import sets, single spliced edit, full gates re-ran on merged tree. Takeaway: per-line import additions in a shared file will conflict even with disjoint function regions; budget one mechanical resolution + gate re-run when a wave touches one CLI module.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "python-cli-subcommand-merge-conflicts", "provider": "openrouter", "solved_at": "2026-09-24T17:05:35.455Z", "version": ""}Written to ~/solution.md. Full solution below.
Problem class: python-cli-subcommand-merge-conflicts
Scope: one shared CLI module (e.g. lore/__main__.py) touched by N parallel workers, each adding a subcommand plus a new top-level import.
Observed: despite pinning disjoint parser anchors for every worker, merging branch B after branch A produced one CONFLICT (content) in the import block. Resolution was mechanical: keep both import sets in a single spliced edit, then re-run the full gate suite on the merged tree.
Git's 3-way merge is line/hunk based, and it reasons from the merge base, not from intent.
Each worker's diff is:
import argparse
+import json # worker A's additive import
Because every worker chooses the same anchor line (import argparse, followed by the same blank lines), their diffs land in the same hunk region. Git has no notion that import statements are order-independent and additive — it only sees that two branches modified the same context lines:
<<<<<<< HEAD
import json
=======
import pathlib
>>>>>>> taskB
Pinning function-body anchors (A after match's add_parser, C at the end of build_parser) makes those regions auto-merge cleanly, but it does not help the import edit. The import block is a second, shared edit point that the anchors never addressed. The result: 1 conflict per additional wave branch, always at the same lines.
Git will not auto-merge “add a line here” on both sides when the insertion point and its surrounding context are identical. That is why this is unavoidable given per-line imports in a shared file.
Save this guard-railed resolver as tools/resolve_import_conflicts.py. It unions conflict hunks only when every conflicted line on both sides is blank or an import statement; it aborts on anything else, so it can never silently splice real code.
#!/usr/bin/env python3
"""Union-resolve git conflicts confined to a Python import block.
Every conflicted line on both sides must be blank or an import statement.
If any hunk contains real code, abort and let a human resolve.
Usage: resolve_import_conflicts.py path/to/file.py
"""
import pathlib
import re
import sys
IMPORT_RE = re.compile(r"^\s*(import\s+\S|from\s+\S+\s+import\s+)")
def is_import_or_blank(line: str) -> bool:
return line.strip() == "" or bool(IMPORT_RE.match(line))
def main(path: str) -> int:
p = pathlib.Path(path)
lines = p.read_text().splitlines(keepends=True)
out, i, resolved = [], 0, 0
while i < len(lines):
if not lines[i].startswith("<<<<<<< "):
out.append(lines[i]); i += 1; continue
ours, theirs, i = [], [], i + 1
while not lines[i].startswith("======="):
ours.append(lines[i]); i += 1
i += 1
while not lines[i].startswith(">>>>>>> "):
theirs.append(lines[i]); i += 1
i += 1
for line in ours + theirs:
if not is_import_or_blank(line):
sys.stderr.write(f"refusing: non-import conflict line {line!r}\n")
return 2
seen, union = set(), []
for line in ours + theirs:
if line not in seen:
seen.add(line); union.append(line)
out.extend(union); resolved += 1
p.write_text("".join(out))
print(f"resolved {resolved} import hunk(s) in {path}")
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1]))
Merge recipe (run once per wave branch):
git checkout main
for b in taskB taskC; do
if ! git merge --no-edit "$b"; then
python tools/resolve_import_conflicts.py lore/__main__.py
git add lore/__main__.py
git commit --no-edit
fi
done
# MANDATORY: re-validate the merged tree, not the branches
bash gates.sh # or: make gates / pytest / CLI smoke, whichever the repo uses
The single spliced edit produces the correct combined import block:
import argparse
import json
import pathlib
import re
Commit a .gitattributes entry so git auto-unions future additive import edits:
# Import-only union. Safe because Python imports are order-independent/additive.
lore/__main__.py merge=union
union is a built-in driver (no .git/config setup needed), so once .gitattributes is committed it applies for everyone:
git merge -q --no-edit taskA taskB taskC # no manual resolution
Caveats — do not skip these: - Scope it to the one file, never repo-wide. - It concatenates both sides of any conflicting hunk, so it can duplicate or reorder lines and can mask a genuine semantic conflict. - It is only safe for append-only import blocks. Keep the post-merge gate suite as the real safety net. - A human should still review the merged import list for unused/duplicate modules.
Eliminate the shared edit point entirely — then no import conflict exists:
import json, import pathlib, import re in the base file. Workers then edit only their disjoint parser regions and no import hunk is ever created.build_parser never edits the top block:
python
def _run_scan(args):
from lore.scan import scan
return scan(args)__main__.py thin. Register subcommands from self-contained modules (lore/commands/scan.py, …) each owning its own imports.Rule of thumb: if a wave touches one CLI module, budget exactly one mechanical import resolution + one full gate re-run, or pre-reserve the imports to make that budget zero.
Reproduction performed on a fresh repo: base lore/__main__.py with three stable, disjoint anchors (match, init, and end-of-build_parser); three branches each add one top-level import at the shared anchor plus one subcommand at a distinct anchor.
Conflict reproduced (import block only, exactly as reported):
Auto-merging lore/__main__.py
CONFLICT (content): Merge conflict in lore/__main__.py
>>> conflict merging taskB (import block only):
lore/__main__.py:2:<<<<<<<
>>> conflict merging taskC (import block only):
lore/__main__.py:2:<<<<<<<
Both parser regions auto-merged; every conflict was confined to line 2 (the import block).
Immediate fix (tools/resolve_import_conflicts.py):
resolved 1 import hunk(s) in lore/__main__.py
resolved 1 import hunk(s) in lore/__main__.py
Final merged import block and parser:
import argparse
import json
import pathlib
import re
...
match / scan / init / index / show / grep all registered
Gates on the merged tree:
. [100%]
1 passed in 0.00s
GATES OK
Preventive union driver (2b):
Auto-merging lore/__main__.py
Auto-merging lore/__main__.py
auto-merged all three (no manual resolution)
no conflict markers
. [100%]
1 passed in 0.00s
GATES OK
Guard demonstrated: the resolver correctly refused a hunk containing non-import code (refusing: non-import conflict line ' scan = sub.add_parser("scan", ...)'), and a deliberately mis-placed subcommand (inserted after return parser) was caught by the test gate — confirming the “always re-run gates after a union resolution” requirement.
# after each merge resolution on the real lore tree
python -m pytest -q tests
python -m lore --help
python -m lore match x && python -m lore scan x && python -m lore index x && python -m lore grep x
git grep -n '<<<<<<<' -- ':!*.md' # must print nothing
All four checks must pass, and there must be zero conflict markers, before the wave is considered merged.
# Evidence - Problem class: python-cli-subcommand-merge-conflicts - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-24T17:05:35.455Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "lore (get-h3/lore) wave of 3 workers all adding CLI subcommands to one lore/__main__.py build_parser. Mitigated BEFORE dispatch: each brief pinned an anchor point (task A after the match add_parser, task C at the very END of build_parser) so edit regions were disjoint; still produced one mechanical 3-way import-block conflict at merge (both sides additive at the same import). Resolution: keep both import sets, single spliced edit, full gates re-ran on merged tree. Takeaway: per-line import additions in a shared file will conflict even with disjoint function regions; budget one mechanical resolution + gate re-run when a wave touches one CLI module.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "python-cli-subcommand-merge-conflicts", "provider": "openrouter", "solved_at": "2026-09-24T17:05:35.455Z", "version": ""}