Ref-store damage (a deleted refs/heads/main, an interrupted ref update) can leave an
existing vault repo without a 'main' branch. The cycle's ensureBranch('docmost','main')
+ checkout then throw every poll ("pathspec 'main' did not match"), wedging the space
forever with no self-heal — ensureRepo only creates branches on a FRESH git init
(found via web-test corruption charter, reproduced deterministically).
Add VaultGit.ensureMainBranch() and call it in the cycle preflight (after
clearStaleGitLocks, before the branch setup): if 'main' is missing, re-create it from
the 'docmost' mirror branch (they track each other) else from HEAD. Same
wedge-forever family as D3-N3.
Verified on the stand: deleting refs/heads/main now self-heals (main restored, the
edit reaches the vault, 0 pathspec errors) — was wedged forever. Unit test (real temp
repo: delete main -> ensureMainBranch restores it from docmost). git-sync suite green (708).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
An interrupted git operation (a hard crash / OOM-kill / abrupt container stop mid
`git add`/`commit`/`checkout`) leaves a `.git/index.lock` (or a ref `*.lock`).
Git then refuses EVERY subsequent operation ("Unable to create '…/index.lock':
File exists"), so every poll cycle failed and the space's sync wedged INDEFINITELY
with no self-heal — the whole space stopped syncing until a human ran `rm` on the
lock (found via web-test restart/corruption charter, reproduced deterministically).
The daemon holds the per-space Redis lock and is the vault's ONLY writer, so any
`*.lock` reaching a fresh cycle is necessarily stale (no live git process holds it).
Add `VaultGit.clearStaleGitLocks()` and call it in the cycle preflight, right after
ensureRepo and before the mid-merge recovery — clearing index/HEAD/config/packed-refs/
MERGE_HEAD/ORIG_HEAD and the engine's ref locks (best-effort, missing = no-op).
Verified on the stand: a planted stale index.lock is now cleared and the space
recovers (edit reaches the vault, 0 "File exists" errors) — was wedged forever.
Unit test (real temp repo: index.lock blocks git add -> clear -> git add works);
git-sync suite green (707).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bug #1 (push 503 starvation): an external receive-pack that briefly overlapped
a poll cycle immediately 503'd because the per-space single-writer lock was
held. Add a BOUNDED retry-acquire on the PUSH path only (SpaceLockService
.withSpaceLock acquireRetry: capped exponential backoff up to ~5s); a transient
overlap now waits and succeeds, a genuinely stuck cycle still 503s after the
bound. The poll cycle passes no retry (immediate skip). Push result stays
deterministic: the receive-pack only runs once the lock is held, so a 503 never
leaves a half-applied ref.
Bug #2 (concurrent-edit marker leak + silent same-block loss):
- Marker leak (a): the push UPDATE path stripped markers for the body sent to
Docmost but left raw <<<<<<</>>>>>>> committed on the published `main` vault
forever (autoMergeConflicts ON). Now the cleaned body is written back to the
vault file + recorded in writtenBack so runPush commits it on `main` and the
vault converges to clean bytes.
- Marker leak (b): pin merge.conflictStyle=merge in ensureRepo and teach
stripConflictMarkers/hasConflictMarkers about the diff3 `|||||||` base section
(drop the marker AND the stale base region) so diff3/zdiff3 conflicts can
never leak `|||||||` + base content into a page. Also scrub the 3-way merge
BASE markdown.
- Silent same-block loss: the block 3-way merge still resolves same-block
conflicts deterministically to git, but it is no longer silent: diff3Plan now
reports a conflict count (mergeXmlFragments3WayWithStats), gitSyncWriteBody
logs it, and the persistence boundary-snapshot now fires for git-sync writes
over a non-git-sync baseline so the human's pre-merge content is preserved in
page history (recoverable). Full both-preserved persisted-conflict UI remains
the deferred redesign.
Tests: space-lock bounded-retry (success/stuck/poll-immediate); push vault-clean
+ diff3 ||||||| strip; ensureRepo conflictStyle pin; diff3Plan/3-way conflict
counts; persistence git-sync boundary snapshot. Server tsc clean; git-sync
vitest + server collaboration/git-sync jest all green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>