
Several Claude Code sessions, one repo: the file owner table, checked against 13 merges
After one commit carried off 50 files another session was still writing, we started splitting parallel work with a table that gives every shared file exactly one owner.
Commit 32fca47 was supposed to fix a launch crash in release 1.0.3. It changed 74 files, and 50 of them belonged to the next app, which a second Claude Code session was building in the same working tree. That was September 23, 2026, in SugarMount's utility app repository (sugar-utils). Sixteen days later the family calendar app AXFamily wrote a plan around a shared-file owner table, and the next day merged 13 feature branches in 2 minutes 11 seconds. Seven of those merges needed hand-merged hunks. None were in the per-feature translation files; they came from committed translation output (four merges), names and error codes missing from the stub list (M1), an index file in R1's row (A1, A2), and test files (F2).
Sources: git history of both repositories (32fca47, ca1ade4, merges 3e9d0cb to e5f9798 and 58920b9 to ff0c975, a0351cf, 5d51b5b), AXFamily's release plan of 2026-10-09, memory notes the sessions left at the time, and this site's AGENTS.md. Hand-merge counts were redone with git show --cc on 2026-10-28.
Contents (9)
- A release fix shipped 50 files from a session next door
- In a shared working tree, git cannot tell your change from theirs
- Separate worktrees still hand-merged AppContainer.kt in 3 of 4 merges
- AXFamily's plan gave each shared file an owner and a route for everyone else
- Seven of 13 merges needed hand merging; the per-feature translation files never did
- Six mismatches git never saw, and 12 review defects, sat outside the table
- One table before splitting, two commands after merging
- Still committed: the merged translation files, and no row for concepts
- FAQ
A release fix shipped 50 files from a session next door
On the evening of September 23 two sessions shared the sugar-utils working tree. One was finishing the 1.0.3 release of the image compressor (U01); the other was building the next app, Flip & Rotate Image (U02). At 22:24 the U01 session committed 32fca47. Its message lists a fix for a constructor R8 had stripped, a release launch script, and analytics defaults. The diff added 3,946 lines across 74 files, including 50 files (3,313 lines) under apps/sugarflip/ and docs/store/sugarflip/ and 5 files (428 lines) in the shared image engine core/images, all of it U02 work in progress. The U02 session's memory note calls it a sweep "in git add -A style". U02's engine and screens got a commit with a real description the next afternoon, in 7821dbd.
The same commit added scripts/qa/release_launch.sh, and line 13 of that script was ./gradlew --stop. Per the Gradle docs, --stop terminates every daemon started with the same Gradle version. A session on the same PC was building the Korean spacing app, also on Gradle 9.6.0, and its memory note records two clean builds that day dying with Gradle build daemon has been stopped: stop command received.
Each session blamed the neighbour. The U02 session wrote that the stops came from the spacing app's build; the spacing app's session pointed at sugar-utils. Across the full history of both repositories, the only --stop is that line in 32fca47's script. Nothing records who ran it or when. The line stayed for 15 days, until ca1ade4 replaced it with --no-daemon on October 8.

In a shared working tree, git cannot tell your change from theirs
In one working tree git has no conflict to flag: both sessions' edits sit in the same index, and whichever session commits first becomes the owner of every file in the tree at that moment. With no per-file owner written down, an agent has no basis for deciding which changes are its own. The Gradle daemon had the same shape: not a file, but something every session on the PC used, and any session could stop it.
The U02 session wrote rules into its memory note that night. Re-read a shared file (AGENTS.md, the backlog, CHANGELOG, templates) right before editing it, and use a targeted Edit, not a full Write. Never commit, stash, reset or run gradlew --stop. Expect another session to kill a build, so check the timestamps on the test-result XML and rerun instead of trusting an exit code. The same rules now sit in the operator's global instructions, which apply to every project, next to a line saying long Gradle builds run with --no-daemon.
Separate worktrees still hand-merged AppContainer.kt in 3 of 4 merges
We had already tried separate working trees. On September 20 the repository was created at 23:10, and four foundation tasks (file handling, image engine, ads and billing, analytics) went to subagents, each in its own git worktree. Four branches merged between 23:28 and 23:40, two more by 00:26, six in all. git show --cc shows AppContainer.kt hand-merged in 3 of the 4 foundation merges. The repo's first ADR says "No DI framework; a manual AppContainer per app", so every foundation task had to add its own lines to that one file.
Worktrees keep work-in-progress files apart. A wiring file that every task appends to is still shared, and the conflict moves to merge time. The game repository gave each workstream an "exclusive files" column, described in "Use hundreds of agents" in practice: 10 workstreams, 59 commits in a day, three running at once. AXFamily wrote a separate table for the shared wiring files that column left out.
AXFamily's plan gave each shared file an owner and a route for everyone else
The October 9 release plan put this first in its conclusions: one Wave 0 agent lays down every shared file up front, and nobody edits those files from Wave 1 on. Section 2, "rules for parallel work conflicts", is a three-column table. Condensed:
| Shared file | Owner | How other packages work |
|---|---|---|
| Contract schema and generated code | W0 contract lane | Don't touch; queue new contracts as follow-up PRs to W0 |
firestore.rules, index file | R1 | Put test JSON in rules-tests/fixtures/<pkg>/ only |
Functions index.ts, errors.ts, etc. | W0 | W0 registers every planned export as a stub (NOT_IMPLEMENTED); each package fills only its own handler file |
| App shell (pubspec, router, service registry, manifest, Gradle files) | W0 | Routes, ad rules and channels ship as files in the feature folder; W0's registries collect them |
Merged translations app_ko.arb | W0's merge tool | Each feature writes only parts/<f>_ko.arb; the tool merges |
| CI workflow | W0 | Add one check file; it is auto-discovered |
| Gradle builds, devices | none | One at a time regardless of worktree, --no-daemon |
W0 laid these files down serially (merged at 19:54 on October 9). Then the 12 packages in the Wave 1 table worked in parallel inside the folders listed in its "main files (exclusive)" column. Talking it out between sessions in one tree had produced 32fca47 and its 50 swept files; worktrees alone had hand-merged AppContainer.kt in 3 of 4 merges. One session doing all 12 packages in sequence would avoid conflicts; we never measured how long that would take.
Seven of 13 merges needed hand merging; the per-feature translation files never did
Between 01:51:06 and 01:53:17 on October 10, 13 feature branches went into the integration branch: the plan's 12 plus a design-system branch (UX0) that had no row in the plan. Listing, per merge, the files that still have combined hunks in git show --cc gives seven merges:
- Four app-screen packages (A2 to A5): the merged
app_ko.arbandapp_en.arbplus three Dart files generated from them. A CI check fails when the merged file differs from the parts and says to run the merge tool and commit, so every branch regenerated and committed it. Theparts/files appear in none of the 13 merges. - The media package (M1): functions
index.tsanderrors.ts. M1 addedconfirmAssetUpload, which was not among W0's 16 planned exports, and both the account package (F1) and M1 edited the same error-code line. - A1 and A2:
firestore.indexes.json, which R1 owns.
The rest were test files. Diffing each branch against its merge base with git diff --name-only and checking the table, 8 of 13 branches edited a file in someone else's row. That count leaves out the merged translations and the fixture files the plan assigned to each package; with B1's shared vector file it would be 9. Git merged two lines in the dependency file, two in a Gradle file and four in the manifest with no conflict; hand merges only exposed cases where two branches touched the same line. The plan's "nobody edits from Wave 1 on" did not hold; for some of the 8 (M1, F1), the reason was an endpoint the stub list did not name and error codes W0 had not put in errors.ts.

Six mismatches git never saw, and 12 review defects, sat outside the table
At 02:09, sixteen minutes after the merges, a0351cf ("minimal fix for Wave 1 integration breakage") repaired six package pairs. The app's paid-plan check (A4) disagreed with the billing server's (B1) subscription states, A4 and the settings package (A5) each declared their own ad-consent type, and the error copy didn't know the media package's new error codes. All six pairs lived in different files, so git raised no conflict. At 05:28, 5d51b5b fixed the 12 defects the integration review confirmed plus minor issues and a contract re-baseline, adding 2,088 lines across 115 files. Guardian checks were pulled into one function (isGuardianOf), and the three screen packages A2, A3 and A4 were made to read which family is being viewed from a single value, AppSession.currentHouseholdId. Two missing indexes went in with a new check that compares the app's orderBy queries with the index table.
The table named an owner for every shared file and none for concepts such as "guardian" or "current family". The double definitions showed up only in the integration review behind 5d51b5b. Use the owner table to cut text conflicts, and leave contract drift to an integration review plus the checks that review produces. We have not tried the table on work that cannot be split into rows, such as a refactor that cuts across many folders.
One table before splitting, two commands after merging
Write the shared-file list and its owners before you decide how many agents to run. Sessions that share a working tree get no commit rights.
- Find the files every task has to add a line to: DI container, router, endpoint list, dependency file, merged translations, CI config, database indexes.
- Give each one owner, and on the same row write the route others use instead (a parts file, a file of their own that a registry picks up). Shared things that are not files, like builds and devices, get a row too.
- The owner lays the wiring files down first and registers every planned name as a stub.
- Files a tool generates, like a merged bundle, are regenerated after the merge by whoever integrates. This site's AGENTS.md works the same way: content authors edit only
<main>, the title and the description, never runtools/build.py, and leave generated files and everything outside<main>to the one build owner who runs the integration build. The agents drafting these notes each get their own copy of the site since October 26, 2026, when three of them shared one copy and mixed up and deleted each other's drafts. - If sessions must share a working tree: re-read before editing, targeted Edits, no commit, stash, reset or
--stop, Gradle with--no-daemon, and judge success by the test-result XML timestamps. - After merging, count where the table leaked.
In AXFamily the first command found the 7 hand-merged merges; checking the second command's output against the table gave the 8 branches outside their rows.# files that still have combined hunks in a merge git show --cc --format= <merge> | grep '^diff --cc' # files a branch changed, to check against the table git diff --name-only $(git merge-base <base> <branch>) <branch>
How to phrase rules like these in an instruction file is covered in How to write AGENTS.md: only the rules an AI coding agent gets wrong.
Still committed: the merged translation files, and no row for concepts
AXFamily's develop branch still tracks the merged app_ko.arb and the three generated Dart files, and CI checks that they are current. Moving their regeneration to the integrator after each merge has not been done yet. Whether adding "concept" rows (guardian check, current family) to the owner table would cut defects like those in 5d51b5b is untested.
FAQ
Can two Claude Code sessions work in the same working tree?
They can, as long as neither commits unless the operator asks. In sugar-utils one session's commit, 32fca47, carried 50 files another session was still writing. Since then, sessions in a shared tree do not commit, stash, reset or run gradlew --stop.
Do git worktrees remove conflicts between parallel agents?
They stop work-in-progress files from mixing, but wiring files that several tasks edit still conflict at merge time. On September 20, AppContainer.kt was hand-merged in 3 of 4 merges.
Does gradlew --stop stop builds in other projects?
It does when their daemons run the same Gradle version. The Gradle docs say so, and two clean builds of another repository on the same PC, also on Gradle 9.6.0, died with "stop command received". Run long builds with --no-daemon.
This note is written from values SugarMount measured itself; an AI coding agent was used to draft it.
More AX notes
- Three agent designs, three judges: the market judge alone would have picked the one that crashed on every KakaoTalk file
- Commits without a Co-Authored-By trailer: the agent made 26 of the 29 we could check
- Running an AI marketer on a schedule four times a day: the permission table that leaves money, accounts and policy to a person
- 84 agents reviewed a release: three refuters per finding, and only the 20 survivors got fixed
- Pre-gzipping Android assets, measured: zero APK savings, and .pack.gz became .pack with a FileNotFoundException
- A market:// link opens a store chooser on SKT-sold Samsung phones: set the Play package first