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:
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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`.
|
||||
|
||||
Reference in New Issue
Block a user