fix(git-sync): address PR #119 review #4 — symlink guard, dead-code cull, changelog + warnings/suggestions

Blocking (review id 2514):
- [security] Forbid symlinks in vaults. ensureServable now sets
  core.symlinks=false in each vault's local git config (a pushed symlink is
  checked out as a plain file, never a real link), and the engine cycle wraps
  every read/write/mkdir in an lstat/realpath guard (new path-guard.ts) that
  refuses a path that is — or traverses — a symlink, or whose realpath escapes
  the vault root. Prevents a writer from publishing /etc/passwd or the server
  .env, or writing outside the vault. Adds unit tests (path-guard.test.ts) +
  a read-guard integration test (cycle.test.ts) + real lstat/realpath in the
  roundtrip integration test.
- [simplification] Delete dead lib/diff.ts + test/diff.test.ts and drop the
  now-unused @fellow/prosemirror-recreate-transform dependency.
- [documentation] Add a CHANGELOG [Unreleased] → Added entry for git-sync.

Warnings:
- [test-coverage] Cover the CREATE-branch conflict-markers guard (a new .md with
  markers and no gitmost_id is recorded as a create failure, never created).

Suggestions:
- [stability] Bound each `git config` in ensureServable with a timeout.
- [authz] Trigger endpoint resolves spaceId workspace-scoped and 404s a foreign
  space before any vault directory is created.
- [stability] Attribute git-initiated moves to the service account
  (lastUpdatedById), via an optional actor param on PageService.movePage.
- [documentation] Document the per-space autoMergeConflicts toggle in AGENTS.md.
- [test-coverage] Cover the unterminated `:::` callout fence fallback.
- [simplification] Move test-only roundtrip-helpers.ts out of src/ into test/.

Architecture:
- Move the Yjs/ProseMirror merge primitives (yjs-body-merge, three-way-merge,
  lcs + specs) into collaboration/merge/, breaking the collaboration →
  integrations/git-sync dependency cycle this PR introduced.
- Port the schema-surface drift gate to packages/mcp (the mcp schema mirror had
  none); pins 52 entries.

Deferred (with rationale in the review thread): the incremental-pull perf
warning (correctness-neutral; needs a high-water-mark design + its own tests on
the data-loss-critical path) and the redis-sync rolling-deploy mixed-version
edge (the deficient behavior is in already-released old-instance code; the new
code is correct on both sides; impact is a transient rollout-window artifact).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
claude code agent 227
2026-06-28 15:39:12 +03:00
parent f020739bfd
commit 906733b5c8
37 changed files with 675 additions and 731 deletions

View File

@@ -76,7 +76,7 @@ export class GitmostDataSourceService {
this.createPage(ctx, title, content, spaceId, parentPageId),
deletePage: (pageId) => this.deletePage(ctx, pageId),
movePage: (pageId, parentPageId, position) =>
this.movePage(pageId, parentPageId, position),
this.movePage(ctx, pageId, parentPageId, position),
renamePage: (pageId, title) => this.renamePage(ctx, pageId, title),
listRecentSince: (spaceId, sinceIso, hardPageCap) =>
this.listRecentSince(spaceId, sinceIso, hardPageCap),
@@ -252,6 +252,7 @@ export class GitmostDataSourceService {
* §3.2 / §14.4).
*/
private async movePage(
ctx: GitSyncBindContext,
pageId: string,
parentPageId: string | null,
position?: string,
@@ -268,6 +269,10 @@ export class GitmostDataSourceService {
{ pageId, parentPageId: parentPageId ?? null, position: resolvedPosition },
page,
GIT_SYNC_PROVENANCE,
// Attribute the git-initiated move to the service user (lastUpdatedById),
// matching create/delete/rename — the contract is "git-operations are
// attributed to the service account".
ctx.userId,
);
return { id: pageId };
}