| name | parallel-session-coedit-via-source-mtime-and-idempotent-rebuild |
| description | Safely co-edit a deliverable WHILE another live session (a second Claude Code
session, or a colleague's agent) is actively editing the same file. Use when:
(1) the user warns "a parallel session is making active edits on the same file,
don't delete each other's work / maybe only write when the other is finished",
(2) the deliverable is BUILT from many per-item source files by an assembler
(e.g. sections/*.html -> _assemble.py -> one big HTML; docs, slides, reports),
(3) you must make edits without clobbering — or being clobbered by — the other
session's concurrent work. Covers: mapping hot/cool items via source-file mtime,
editing only disjoint sources, why an idempotent rebuild is NON-destructive
(so your edits survive the other session's rebuild), the false-negative
grep gotcha when verifying content landed in assembled HTML, how to CHECK
COVERAGE ("is card X present / missing?") against the sources rather than the
build, and the end-of-session HANDOFF/COMMIT discipline on the shared branch
(commit only your own docs, unpushed; leave the co-owned deliverable uncommitted) —
including the worktree-GLOBAL-index gotcha: staging then pausing lets the OTHER
session's `git commit` absorb your staged files into its commit (stage+commit
atomically; re-verify `git diff --cached` in the instant before committing).
|
| author | Claude Code |
| version | 1.2.0 |
| date | "2026-07-01T00:00:00.000Z" |
Co-editing a section-assembled deliverable alongside a live parallel session
Problem
The user says: "a parallel session is making active edits on the same file, so
please don't delete each other's work — maybe only write on it when the other one
is finished." The deliverable is large and shared. The naive readings both fail:
serialize (do nothing until they finish — wastes the session, ignores the
user's "fan out / go fast") or dive in blindly (and clobber their work, which
is exactly what they warned against).
There is a third path that is fast AND safe — but only once you notice the build
model: the artifact is assembled from per-item source files, and the assembler
is idempotent (reads all sources from disk, regenerates the one output). That
changes the collision math entirely.
Context / Trigger conditions
- User warns that another live session / agent is editing the same file; "don't
clobber each other"; possibly "only write when the other is finished."
- The deliverable is generated: a build script (
_assemble.py, a bundler, a
static-site generator) inlines per-item source files (sections/*.html,
partials, slides) into ONE output file. The output is a build artifact, not
the source of truth.
- You need to make real edits this session without losing concurrent work.
Solution
-
Find the build model FIRST. Never hand-edit the assembled OUTPUT. Locate the
assembler and confirm the big file is generated from per-item sources. Edit the
source files; the output is regenerated. (If you edit the 2 MB assembled
HTML directly, the next rebuild by either session silently overwrites you.)
-
Map hot/cool per item via SOURCE-FILE mtime — this is your real-time
ownership signal, no git needed:
stat -f '%Sm %N' -t '%H:%M:%S' sections/*.html
Files touched in the last few minutes = the other session's HOT set; old
mtimes = COOL. Re-stat right before each edit batch — the other session
moves between cards/items quickly (a broad pass can touch 6+ files in minutes).
-
Edit ONLY cool, disjoint source files. Two sessions on different source
files never clobber each other — last-writer-wins only bites the same file.
So staying on disjoint items satisfies "don't delete each other's work" without
serializing. Ask the user to confirm ownership at item granularity ("does the
other session own items 1–9 and I take 10 + the analysis?").
-
An idempotent rebuild is NON-destructive — this is the key insight. The
assembler reads ALL source files from disk and regenerates the output, so any
session's rebuild auto-merges everyone's saved source edits. Your edits
survive the other session's rebuild, and theirs survive yours — as long as all
edits live in the SOURCES, not the output. You don't even have to rebuild
yourself: the next rebuild by anyone picks up your saved sources. (Verified: my
edits to one card survived the other session's rebuild that happened minutes
later; that rebuild pulled my changes into the output untouched.)
-
The only two real collision surfaces — avoid both during the active window:
(a) both sessions editing the same source file; (b) editing the shared
assembler/config (e.g. reordering items in _assemble.py, which also
renumbers everything). If the ask is "position X near Y," prefer a
cross-reference note inside the (disjoint) source files over a physical
reorder of the shared assembler. Defer structural reorders until the other
session is done.
-
Verify your edits survived. After the other session bumps the mtime of a
file you edited, RE-READ it to confirm your changes are intact. (The Edit tool
also errors on stale state, so a blind edit fails safe rather than clobbering.)
-
Rebuild only when sanctioned ("rebuild now") — then verify the OUTPUT
contains every session's content.
Verification
- Rebuild cleanly: check the assembler's success line, the item/card count, and
that all referenced assets embedded (no "missing []").
- Confirm your content landed — strip tags AND normalize whitespace before
grepping. A line-based
grep "my exact phrase" on assembled HTML gives
false negatives when the phrase wraps across a source line break OR an inline
tag (<i>, <b>) sits mid-phrase. Use:
python3 -c "import re,sys; t=re.sub(r'\s+',' ', re.sub(r'<[^>]+>',' ', open(sys.argv[1]).read())); print(sys.argv[2] in t)" OUT.html "my exact phrase"
- To decide whether a card/consideration is ALREADY present (or "missing"),
grep + READ the SOURCE section files — NOT the assembled output. The
tag-stripped check above answers "does this phrase appear anywhere in the
build", which is the wrong question for coverage: the build can be stale
vs the sources, AND a tag-stripped scan over-matches when the term appears
in an unrelated card (checking if the ruled-out section covered "Distance from
campus" false-positived on card 09's "by distance" FA figure). So the build
grep fails both ways — false-negative raw (tags/wraps), false-positive
tag-stripped (cross-section collision). Before a handoff/plan declares a card
missing → "go build it", confirm against
sections/*.html. See
shipped-change-not-visible-deploy-vs-gate-vs-cache (Step 0) + lessons #181.
- Confirm the OTHER session's work is still present in the output (grep a
marker of their concurrent additions) — proves the rebuild merged, not lost, it.
Example (the client dossier, 2026-06-30)
Two live sessions co-editing decision_dossier_ENRICHED.html, assembled from
sections/*.html via _assemble.py. stat showed the other session sweeping
cards 01–09 (mtimes in the last ~5 min); my targets 07/08b/10 were cool (hours
old). I edited 08b first; the other session's later rebuild (16:12) pulled my 08b
edits into the output cleanly. When they moved onto 09, I held the 09 cross-ref
until the user gave me explicit ownership of all four cards. I used cross-reference
notes (07↔09) instead of physically reordering cards (which would have meant
editing the shared _assemble.py). Final rebuild verified all four of my cards
and the other session's card-09 enrichment present; two "missing" greps turned
out to be a line-wrap and an inline <i> tag (false negatives), confirmed by the
tag-stripped check.
Variant — end-of-session handoff/commit on the shared branch (session-handoff Phase 4)
When you wrap up (e.g. /session-handoff) while the deliverable is still co-edited
live on a shared branch, the handoff skill's default "commit ALL work → open a
PR → auto-merge docs" is wrong and will entangle or ship the other session's
in-progress work. git status on the shared worktree shows both sessions' edits
intermixed — there is no clean per-session diff. Invert Phase 4:
- Do NOT commit the co-owned deliverable. Your source edits are saved on disk
and the idempotent rebuild already merges them. Committing now (a) entangles the
other session's WIP in your commit and (b) ships a half-done, often gated
(
DO-NOT-MERGE) artifact. Leaving the deliverable saved-but-uncommitted for
the worktree owner / post-sign-off commit is the protocol, not a loose end.
- Commit ONLY your own doc artifacts (handoff doc, next-session prompt),
path-staged by exact filename — never
git add -A/-u, which sweeps the
other session's files + untracked build scripts
(git-add-all-sweeps-untracked-artifacts-into-commit).
- The index is worktree-GLOBAL — don't stage-then-pause.
git add writes to
the one shared index; if the other session runs its git commit (even a
plain commit, no -a) while your files sit staged, your staged files ship in
ITS commit — you never ran git commit. Observed 2026-07-01: path-staged
cards 08/10 were absorbed into the parallel session's f9e63216 ("dossier card
1B…"). Mitigate: run git add <paths> and git commit atomically in the same
step, and re-check git diff --cached --name-only shows exactly your paths
in the instant before committing (abort if a foreign path appears). If you'd
rather your content edits land in the other session's commit anyway (the owner
is actively rebuilding that file), just leave them unstaged and let it fold
them in. See lesson_shared_worktree_staging_absorbed_by_parallel_commit (the client
project memory).
- Never push. A shared branch usually carries the other session's unpushed
commits (
git log @{u}..HEAD shows them above origin); pushing publishes their
work. The other session owns the push.
- Don't amend a commit another session/issue may reference — add a new small
commit (preserves the SHA your handoff/issue cited).
- Memory index near its size cap? UPDATE an existing memory file (no new
MEMORY.md index line) instead of adding one.
- File the follow-up as a labeled issue so it's tracked independently of the
unpushed/gated branch (the next session may start from a fresh worktree off it).
- If docs genuinely must reach
main now, land them via a separate worktree off
origin/main (a "safe-docs PR" excluding the gated deliverable) — never
branch-switch the shared/active branch.
Net (verified the client dossier 2026-06-30c): 2 path-staged doc commits, unpushed, atop
the other session's commits; every deliverable edit (yours + theirs) left
uncommitted; one labeled follow-up issue; an existing memory file updated in place.
Notes
- The user's "maybe only write when the other is finished" is a safe default, not
a hard rule — with the source/assembler model you can co-edit disjoint sources
immediately and let the idempotent rebuild merge. Surface the coordination
question (who owns what) rather than stalling.
- This is filesystem coordination, not git — no branches. Distinct from
shared-file-redesign-parallel-author-serial-integrate (one orchestrator
authoring handoff prompts / in-session fan-out) and
large-redesign-parallel-branch-collision-audit (git-branch collisions).
- Worktree note: the deliverable may live in a different worktree than your cwd;
edit it by absolute path there, and remember the user's
file:// URL points at
that worktree's copy.
See also
shared-file-redesign-parallel-author-serial-integrate — single-orchestrator
parallel authoring & serial integration (git branches / handoff prompts).
large-redesign-parallel-branch-collision-audit — pre-existing branches that
collide with a redesign's files.
merge-conflict-generated-files — when the assembled output is tracked and
conflicts in git.
session-handoff — the Variant above inverts its Phase 4 (commit-all + PR +
auto-merge) for a shared, live-co-edited, gated branch.
shipped-change-not-visible-deploy-vs-gate-vs-cache (Step 0) — the general
"don't conclude from a shallow grep of a built artifact; read the source" rule
the coverage-check bullet specializes for section-assembled deliverables.