fix(git-sync): unwedge per-page conflicts, preserve callout types, flush collab on disconnect
Addresses QA findings on PR #119 (issues #235/#236). SYNC-WEDGE (HIGH): one same-line conflict on one page froze sync for the WHOLE space in both directions forever. The pull's docmost->main merge left the vault mid-merge, so every later cycle's isMergeInProgress() check returned skipped:"merge-in-progress" and skipped the entire space with no recovery. - pull.ts now COMMITS a conflicting merge with markers in place (commitMerge): cleanly-merged pages land, the conflicted page carries its markers on main and is isolated by the existing push-side conflict-marker skip (markers never reach Docmost), and the next cycle is no longer wedged. conflictedPaths is surfaced. - cycle.ts now RECOVERS a vault left mid-merge by a prior/pre-fix cycle: it aborts the stale merge (merge --abort, hard-reset fallback) and continues, instead of skipping the space forever. - git.ts: listUnmergedPaths / commitMerge / abortMerge / resetHardToHead. CALLOUT TYPE FIDELITY: git-sync's CALLOUT_TYPES was missing "note" and "default" (editor-canonical types), so [!note]/[!default] callouts flattened to [!info] on every round-trip. Aligned the list with @docmost/editor-ext getValidCalloutType. LOSS-ON-FAST-CLOSE: editing a page then closing the tab inside the collab debounce window (~3-18s) lost the edit, because with unloadImmediately:false Hocuspocus does not flush the debounced onStoreDocument on the last-client disconnect. PersistenceExtension.onDisconnect now flushes the pending store (debouncer.executeNow) on the last disconnect only, with no redundant write. DUPLICATION re-verify (#1): the schema-default merge-key normalization is intact; faithful toYdoc-based reproduction shows callout + rich content resync with 0 ops and no growth/strip across cycles -> the re-report was leftover vault data, not a live regression. Locked with a callout regression spec. Tests: git-sync 688 pass (incl. real-VaultGit wedge-recovery integration); server git-sync+collaboration 285 pass; new callout merge/fidelity + onDisconnect-flush specs. tsc --noEmit clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -66,6 +66,10 @@ function makeGit(merge: { ok: boolean; conflict: boolean; output?: string } = {
|
||||
order.push('merge');
|
||||
return { ok: merge.ok, conflict: merge.conflict, output: merge.output ?? '' };
|
||||
}),
|
||||
listUnmergedPaths: vi.fn(async () => ['Conflicted.md']),
|
||||
commitMerge: vi.fn(async (subject: string) => {
|
||||
order.push(`commitMerge:${subject}`);
|
||||
}),
|
||||
};
|
||||
return {
|
||||
git,
|
||||
@@ -403,7 +407,11 @@ describe('applyPullActions — commit subject reflects ACTUAL counts', () => {
|
||||
});
|
||||
|
||||
describe('applyPullActions — merge result is surfaced, not swallowed', () => {
|
||||
it('returns conflict:true on a conflicting merge (no auto-resolve)', async () => {
|
||||
it('COMMITS a conflicting merge with markers (no wedge) and surfaces conflictedPaths', async () => {
|
||||
// Regression for the WEDGE bug (QA #119): a conflicting docmost -> main merge
|
||||
// must NOT be left mid-merge (which wedged the whole space). It is committed
|
||||
// WITH markers so the rest of the space keeps syncing; the conflicted page is
|
||||
// surfaced in `conflictedPaths` and isolated by the push side.
|
||||
const { client } = makeClient();
|
||||
const g = makeGit({ ok: false, conflict: true, output: 'CONFLICT' });
|
||||
const fs = makeFs();
|
||||
@@ -415,6 +423,10 @@ describe('applyPullActions — merge result is surfaced, not swallowed', () => {
|
||||
);
|
||||
expect(res.merge.conflict).toBe(true);
|
||||
expect(res.merge.ok).toBe(false);
|
||||
// The merge was COMMITTED (vault no longer mid-merge) and the bad page named.
|
||||
expect(g.git.commitMerge).toHaveBeenCalledTimes(1);
|
||||
expect(res.conflictedPaths).toEqual(['Conflicted.md']);
|
||||
expect(g.order.some((o) => o.startsWith('commitMerge:'))).toBe(true);
|
||||
});
|
||||
|
||||
it('returns ok:false conflict:false on a non-conflict merge failure', async () => {
|
||||
|
||||
Reference in New Issue
Block a user