docs(git-sync): remove dangling references to the deleted git-sync-plan doc (PR #119 review)

The implementation spec docs/git-sync-plan.md was removed as completed, but ~44
code comments still cited it as "plan §N". Strip those citations (comments only),
keeping each comment grammatical. The vendored engine's own "SPEC §N" references
point at a different, still-present spec and are left untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
claude code agent 227
2026-06-24 01:11:04 +03:00
parent f923accc3d
commit 0318a148dc
20 changed files with 51 additions and 52 deletions

View File

@@ -1,5 +1,5 @@
/**
* Git-sync control-plane constants (plan §6/§9/§10).
* Git-sync control-plane constants.
*
* Event/job names are REUSED from the shared event contract (event.contants.ts)
* so the listener subscribes to the exact names the rest of the server emits —
@@ -10,11 +10,11 @@
import { EventName } from '../../common/events/event.contants';
/**
* The page lifecycle events the git-sync listener reacts to (plan §10). A change
* The page lifecycle events the git-sync listener reacts to. A change
* to any of these in an enabled space schedules a debounced sync cycle.
* - PAGE_CREATED / PAGE_UPDATED / PAGE_MOVED — structural + content edits;
* - PAGE_SOFT_DELETED / PAGE_RESTORED — Trash transitions (deletes are soft);
* - PAGE_MOVED_TO_SPACE — cross-space move (cross-repo, plan §5).
* - PAGE_MOVED_TO_SPACE — cross-space move (cross-repo).
*
* NOTE: body edits arrive via PAGE_UPDATED (emitted from persistence.extension),
* NOT via EventName.PAGE_CONTENT_UPDATED — that name is a BullMQ queue-job name,
@@ -29,12 +29,12 @@ export const GIT_SYNC_PAGE_EVENTS = [
EventName.PAGE_RESTORED,
] as const;
/** Redis key prefix for the per-space leader lock (plan §9). */
/** Redis key prefix for the per-space leader lock. */
export const GIT_SYNC_LOCK_PREFIX = 'git-sync:lock:';
/**
* Leader-lock TTL (ms). Must exceed the maximum expected cycle duration so the
* lock is not lost mid-cycle; on a crash it expires on its own (plan §9). The
* lock is not lost mid-cycle; on a crash it expires on its own. The
* in-process mutex (orchestrator) prevents overlapping cycles on one instance,
* and the Redis lock prevents two instances racing the same space.
*/

View File

@@ -1,4 +1,4 @@
// Unit tests for the ops/testing controller (plan §6). The orchestrator, env,
// Unit tests for the ops/testing controller. The orchestrator, env,
// and the workspace-ability factory are hand-built mocks. We assert the admin
// guard (non-admin -> ForbiddenException, no orchestrator call), that trigger
// uses the workspace from request context (never the body), and that status

View File

@@ -34,7 +34,7 @@ class TriggerGitSyncDto {
}
/**
* Ops/testing endpoints for the git-sync control plane (plan §6). Admin-guarded
* Ops/testing endpoints for the git-sync control plane. Admin-guarded
* (workspace Manage/Settings, mirroring WorkspaceController) so only workspace
* admins can force a cycle. Mounted under the global `/api` prefix:
* - POST /api/git-sync/trigger { spaceId } — run one cycle now (await result),

View File

@@ -14,7 +14,7 @@ import { GitHttpBackendService } from './http/git-http-backend.service';
import { GitHttpService } from './http/git-http.service';
/**
* The git-sync control plane (plan §6). Wires the native datasource, the
* The git-sync control plane. Wires the native datasource, the
* orchestrator (poll + leader-lock), the per-space vault registry, the
* event-driven listener, and the admin trigger controller.
*
@@ -27,7 +27,7 @@ import { GitHttpService } from './http/git-http.service';
* - ScheduleModule (NOT forRoot) — so SchedulerRegistry is injectable (the
* orchestrator registers a DYNAMIC poll interval in onModuleInit). forRoot()
* is already registered globally by TelemetryModule; importing the plain
* module here avoids a duplicate scheduler registration (plan §6 note).
* module here avoids a duplicate scheduler registration.
*
* RedisService is provided by the global RedisModule (app.module) and CASL's
* WorkspaceAbilityFactory by the global CaslModule — both resolve without an

View File

@@ -1,4 +1,4 @@
// Unit tests for the event-driven git-sync trigger (plan §10). The orchestrator
// Unit tests for the event-driven git-sync trigger. The orchestrator
// and page repo are hand-built mocks; the debounce coalescing is exercised with
// jest fake timers. We assert the gate, the loop-guard (anti-echo), the
// missing-page short-circuit, the heterogeneous event-shape id resolution, the

View File

@@ -22,18 +22,17 @@ interface PageEventLike {
}
/**
* Event-driven trigger for the git-sync control plane (plan §10). Subscribes to
* Event-driven trigger for the git-sync control plane. Subscribes to
* the page lifecycle events and, for an enabled space, schedules a DEBOUNCED
* `orchestrator.runOnce(spaceId, workspaceId)` — coalescing a burst of edits into
* a single cycle per space.
*
* Loop-guard (best-effort, plan §10/§8.2): an event whose page row already reads
* Loop-guard (best-effort): an event whose page row already reads
* `lastUpdatedSource === 'git-sync'` is the orchestrator's OWN write, so we skip
* it to avoid a write -> event -> sync echo. The guard ALWAYS runs (the page row
* is fetched for every event, structural ones included). This is the cheap first
* guard; the
* full bodyHash + updatedAt loop-guard (consuming the push side's
* `PushedPageRecord`) is a later hardening step (plan §8.2) — noted, not built
* guard; the full bodyHash + updatedAt loop-guard (consuming the push side's
* `PushedPageRecord`) is a later hardening step — noted, not built
* here. The poll-safety interval still converges anything this guard drops.
*/
@Injectable()
@@ -74,7 +73,7 @@ export class PageChangeListener {
if (!page) return;
// Loop-guard: skip our own writes to avoid a write -> event -> sync echo
// (best-effort, plan §8.2). Applies unconditionally now.
// (best-effort). Applies unconditionally now.
if (page.lastUpdatedSource === 'git-sync') return;
// Prefer ids carried on the event; fall back to the row we already fetched.

View File

@@ -1,4 +1,4 @@
// Unit tests for the git-sync control plane (plan §9/§10/§11). The vendored
// Unit tests for the git-sync control plane. The vendored
// engine (@docmost/git-sync) is fully mocked so we exercise ONLY the
// orchestrator's wiring: gating, the Redis leader lock + in-process mutex,
// the pull/push call order, the delete-cap anti-data-loss guard, the remote

View File

@@ -64,12 +64,12 @@ export interface GitSyncRunStatus {
}
/**
* The git-sync control plane (plan §9/§10/§11). Drives the vendored engine in
* The git-sync control plane. Drives the vendored engine in
* process: under a Redis leader lock (single-writer across replicas) plus an
* in-process per-space mutex (no overlapping cycles on one instance), it runs a
* PULL (Docmost -> vault) then a PUSH (vault -> Docmost) for a space.
*
* Enumeration of enabled spaces (plan §10): STRICT opt-in. Only spaces whose
* Enumeration of enabled spaces: STRICT opt-in. Only spaces whose
* per-space flag `space.settings.gitSync.enabled === true` (written by the Phase-C
* UI) are reconciled. There is intentionally NO all-spaces fallback: when no space
* carries the flag, git-sync does NOTHING (an empty list) — flagging every space
@@ -99,7 +99,7 @@ export class GitSyncOrchestrator implements OnModuleInit, OnModuleDestroy {
this.redis = redisService.getOrThrow();
}
// --- Redis leader lock (plan §9) -----------------------------------------
// --- Redis leader lock -----------------------------------------
/**
* Acquire per-space leadership: `SET <key> <instanceId> PX <ttl> NX` returns
@@ -165,7 +165,7 @@ export class GitSyncOrchestrator implements OnModuleInit, OnModuleDestroy {
}
}
// --- enabled-space enumeration (plan §10) --------------------------------
// --- enabled-space enumeration --------------------------------
/**
* Enumerate the spaces the poll loop should reconcile. STRICT opt-in: ONLY
@@ -184,7 +184,7 @@ export class GitSyncOrchestrator implements OnModuleInit, OnModuleDestroy {
.execute();
}
// --- one sync cycle for a space (plan §11) -------------------------------
// --- one sync cycle for a space -------------------------------
/**
* Build the engine `Settings` for a space. The engine's REST-era fields
@@ -361,7 +361,7 @@ export class GitSyncOrchestrator implements OnModuleInit, OnModuleDestroy {
}
/**
* The actual engine wiring (plan §11). Mirrors the engine's own `main`:
* The actual engine wiring. Mirrors the engine's own `main`:
* PULL — readExisting -> computePullActions -> applyPullActions,
* PUSH — runPush (dry-run disabled: a real apply).
* The dependency-object shapes match pull.ts/push.ts exactly (see comments).
@@ -381,7 +381,7 @@ export class GitSyncOrchestrator implements OnModuleInit, OnModuleDestroy {
await vault.assertGitAvailable();
await vault.ensureRepo();
// Refuse to run on top of an unresolved merge (SPEC §9 / plan §11.2): a prior
// Refuse to run on top of an unresolved merge (SPEC §9): a prior
// conflicting pull leaves the vault mid-merge; the next checkout would fail.
if (await vault.isMergeInProgress()) {
this.logger.warn(
@@ -399,7 +399,7 @@ export class GitSyncOrchestrator implements OnModuleInit, OnModuleDestroy {
// before push can diff them.
await vault.checkout('docmost');
// --- PULL (plan §11.1/§11.2) --------------------------------------------
// --- PULL --------------------------------------------
// readExisting deps (ReadExistingDeps): list tracked *.md + read by relPath.
const existing = await readExisting({
listTracked: () => vault.listTrackedFiles('*.md'),
@@ -427,7 +427,7 @@ export class GitSyncOrchestrator implements OnModuleInit, OnModuleDestroy {
vaultRoot,
);
// --- PUSH (plan §11.3) --------------------------------------------------
// --- PUSH --------------------------------------------------
// runPush deps (PushDeps): settings, the full vault git object (method `this`
// binding must be preserved — pass the object, not bound method refs), a
// makeClient factory returning the push client subset, vault-relative fs
@@ -442,7 +442,7 @@ export class GitSyncOrchestrator implements OnModuleInit, OnModuleDestroy {
log: (line: string) => this.logger.log(`git-sync[${spaceId}] ${line}`),
};
// DEFENSE-IN-DEPTH delete cap (plan §11.3 step 6). A non-convergent vault
// DEFENSE-IN-DEPTH delete cap. A non-convergent vault
// (e.g. empty/duplicate titles -> colliding paths) can compute PHANTOM
// absence-deletions that slip under the engine's mass-delete FRACTION guard
// and soft-delete real pages. So plan the push as a DRY-RUN FIRST to read the
@@ -515,7 +515,7 @@ export class GitSyncOrchestrator implements OnModuleInit, OnModuleDestroy {
};
}
// --- poll-safety interval (plan §10) -------------------------------------
// --- poll-safety interval -------------------------------------
/** Registered interval name (shared by registration + teardown). */
private static readonly POLL_INTERVAL_NAME = 'git-sync-poll';
@@ -528,7 +528,7 @@ export class GitSyncOrchestrator implements OnModuleInit, OnModuleDestroy {
*
* ScheduleModule: forRoot() is registered ONCE globally by TelemetryModule;
* GitSyncModule imports the plain ScheduleModule so SchedulerRegistry is
* injectable without a duplicate forRoot (plan §6 note).
* injectable without a duplicate forRoot.
*/
onModuleInit(): void {
if (!this.environmentService.isGitSyncEnabled()) return;

View File

@@ -34,7 +34,7 @@ jest.mock('@docmost/editor-ext', () => ({
import * as Y from 'yjs';
import { GitmostDataSourceService } from './gitmost-datasource.service';
// Focused unit/contract test for the native GitSyncClient adapter (plan §3).
// Focused unit/contract test for the native GitSyncClient adapter.
// No DB, no real collab server: the repos/services/gateway are mocked and we
// assert the mapping logic + the provenance/soft-delete/position contracts.

View File

@@ -20,9 +20,9 @@ import { AuthProvenanceData } from '../../../common/decorators/auth-provenance.d
/**
* The acting context the orchestrator binds the datasource to. The datasource is
* NOT a fixed-identity singleton: it operates on behalf of a (workspaceId,
* userId) pair the orchestrator supplies per space (plan §3.2). `userId` is the
* userId) pair the orchestrator supplies per space. `userId` is the
* git-sync service user — it stays the responsible author (creatorId /
* lastUpdatedById) while the `'git-sync'` actor marks provenance (plan §8.1).
* lastUpdatedById) while the `'git-sync'` actor marks provenance.
*/
export interface GitSyncBindContext {
workspaceId: string;
@@ -44,7 +44,7 @@ const GIT_SYNC_PROVENANCE: AuthProvenanceData = {
/**
* Native, in-process implementation of the engine's `GitSyncClient` seam
* (plan §3). Reads go through repositories (PageRepo/SpaceRepo); body writes go
* Reads go through repositories (PageRepo/SpaceRepo); body writes go
* through collab `openDirectConnection` (§3.3); structural mutations
* (create/move/delete/rename) go through PageService.
*
@@ -94,7 +94,7 @@ export class GitmostDataSourceService {
/**
* Full page tree of a space mapped to the engine's `PageNode` shape. We read
* the DB directly, so `complete` is ALWAYS `true` — the incomplete-fetch
* suppression (SPEC §8) never fires natively (plan §3.2).
* suppression (SPEC §8) never fires natively.
*/
private async listSpaceTree(
ctx: GitSyncBindContext,
@@ -270,7 +270,7 @@ export class GitmostDataSourceService {
/**
* Compute a fractional-index position AFTER the last sibling under
* `parentPageId` (root pages when null) in the space, ordered by `position`
* with the "C" collation Docmost uses (plan §14.4). Falls back to a fresh key
* with the "C" collation Docmost uses. Falls back to a fresh key
* when there are no siblings.
*/
private async computeMovePosition(
@@ -394,7 +394,7 @@ export class GitmostDataSourceService {
* PersistenceExtension.onStoreDocument, which persists ydoc+content+textContent,
* stamps `lastUpdatedSource = 'git-sync'`, and broadcasts `page.updated`. The
* service user (`user.id`) stays the responsible `lastUpdatedById`; the actor
* marks provenance (plan §8.1).
* marks provenance.
*/
private async writeBody(
pageId: string,

View File

@@ -1,5 +1,5 @@
// Unit tests for the per-space vault path resolver + lazy VaultGit cache
// (plan §3/§5). `mkdir` and `VaultGit` are mocked so construction is cheap and
// `mkdir` and `VaultGit` are mocked so construction is cheap and
// no real filesystem / git work happens. We assert the path normalization
// (trailing slash) and the one-VaultGit-per-space caching contract.
import { mkdir } from 'node:fs/promises';

View File

@@ -9,9 +9,9 @@ const execFileAsync = promisify(execFile);
/**
* Resolves the on-disk vault location per space and owns the (lazily created,
* cached) `VaultGit` instance for each one (plan §3/§5).
* cached) `VaultGit` instance for each one.
*
* Topology (plan §5): one git repo per enabled space, rooted at
* Topology: one git repo per enabled space, rooted at
* `<GIT_SYNC_DATA_DIR>/<spaceId>`. A `VaultGit` is constructed at most once per
* space and reused across cycles — it is a thin, stateless shell-out wrapper, so
* caching it just avoids re-resolving the path and re-running `mkdir`.