From dd172580edd4259a2560f9adb3ae795f3822143a Mon Sep 17 00:00:00 2001 From: claude code agent 227 Date: Fri, 26 Jun 2026 01:34:14 +0300 Subject: [PATCH] docs(git-sync): document GIT_SYNC_BACKEND_TIMEOUT_MS, drop dead consts, fix dangling plan refs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address the non-red-team documentation/cleanup items from review #1679: - Document the GIT_SYNC_BACKEND_TIMEOUT_MS watchdog (git http-backend) in .env.example and add it to the environment validation schema — it was used (getGitSyncBackendTimeoutMs, default 120000) but undocumented/unvalidated. - Remove the dead GIT_SYNC_DEBOUNCE_MS_DEFAULT / GIT_SYNC_POLL_INTERVAL_MS_DEFAULT exports (never imported; environment.service is the single source of defaults). - Redirect the dangling `plan §X.Y` comment references to issue #194 (the git-sync spec moved there when docs/git-sync-plan.md was deleted by this PR). Co-Authored-By: Claude Opus 4.8 (1M context) --- .env.example | 5 +++++ .../src/collaboration/git-sync-converter-gate.spec.ts | 6 +++--- .../common/decorators/auth-provenance.decorator.ts | 4 ++-- apps/server/src/core/auth/dto/jwt-payload.ts | 2 +- .../integrations/environment/environment.service.ts | 2 +- .../environment/environment.validation.ts | 11 +++++++++-- .../src/integrations/git-sync/git-sync.constants.ts | 6 ------ .../git-sync/services/gitmost-datasource.service.ts | 2 +- 8 files changed, 22 insertions(+), 16 deletions(-) diff --git a/.env.example b/.env.example index e2223a66..357dcac2 100644 --- a/.env.example +++ b/.env.example @@ -230,6 +230,11 @@ MCP_DOCMOST_PASSWORD= # (default: 2000). # GIT_SYNC_DEBOUNCE_MS=2000 # +# Watchdog timeout in ms for the spawned `git http-backend` process serving a +# git smart-HTTP push (default: 120000). A stalled/hung receive-pack is killed +# after this deadline so it cannot hold the per-space lock forever. +# GIT_SYNC_BACKEND_TIMEOUT_MS=120000 +# # Defense-in-depth absolute cap on soft-deletes applied per push cycle # (default: 5). A non-convergent / phantom-absence cycle can never trash more # than this many pages without an explicit override. diff --git a/apps/server/src/collaboration/git-sync-converter-gate.spec.ts b/apps/server/src/collaboration/git-sync-converter-gate.spec.ts index c15c70ca..a52262ff 100644 --- a/apps/server/src/collaboration/git-sync-converter-gate.spec.ts +++ b/apps/server/src/collaboration/git-sync-converter-gate.spec.ts @@ -16,7 +16,7 @@ * editor-ext ProseMirror documents must survive a full round trip through the * actual server write path without losing any node / mark / attribute. * - * Pipeline per document (plan §13.1): + * Pipeline per document (issue #194 §13.1): * 1. md = convertProseMirrorToMarkdown(content) // git-sync export * 2. doc = await markdownToProseMirror(md) // git-sync import * 3. push `doc` through the REAL editor-ext Yjs write path the server uses: @@ -26,12 +26,12 @@ * (apps/server/src/collaboration/extensions/persistence.extension.ts:96/115) * with the same `tiptapExtensions` (collaboration.util.ts) and the same * `@hocuspocus/transformer`, so the gate exercises the real schema - * validation that runs on a git-sync write (plan §3.3). + * validation that runs on a git-sync write (issue #194 §3.3). * 4. assert docsCanonicallyEqual(canon(original), canon(normalized)) === true * * Any node / mark / attr that editor-ext drops (because the git-sync * docmost-schema named it differently, or declares a different default) makes - * the gate FAIL for that document — exactly the schema-divergence plan §3.3 / + * the gate FAIL for that document — exactly the schema-divergence issue #194 §3.3 / * §13.1 warn about. Genuine, irreducible divergences are isolated into the * clearly-named `KNOWN DIVERGENCE` block at the bottom (never silently hidden). * diff --git a/apps/server/src/common/decorators/auth-provenance.decorator.ts b/apps/server/src/common/decorators/auth-provenance.decorator.ts index e7358dd8..2f061c98 100644 --- a/apps/server/src/common/decorators/auth-provenance.decorator.ts +++ b/apps/server/src/common/decorators/auth-provenance.decorator.ts @@ -10,7 +10,7 @@ import { ProvenanceSource } from '../../core/auth/dto/jwt-payload'; */ export interface AuthProvenanceData { // ProvenanceSource includes 'git-sync' — set by the in-process git-sync data - // plane (plan §8.1) when it drives PageService writes; never from a request token. + // plane (issue #194 §8.1) when it drives PageService writes; never from a request token. actor: ProvenanceSource; aiChatId: string | null; } @@ -62,7 +62,7 @@ export function agentSourceFields( sourceKey: S, chatKey: C, ): Partial & Record> { - // git-sync data-plane write (plan §8.1): stamp the source 'git-sync' with NO + // git-sync data-plane write (issue #194 §8.1): stamp the source 'git-sync' with NO // aiChatId (it has no internal ai_chats row). Mirrors the agent branch; each // write has a single actor, so precedence is irrelevant here. if (provenance?.actor === 'git-sync') { diff --git a/apps/server/src/core/auth/dto/jwt-payload.ts b/apps/server/src/core/auth/dto/jwt-payload.ts index 04055adc..511ec417 100644 --- a/apps/server/src/core/auth/dto/jwt-payload.ts +++ b/apps/server/src/core/auth/dto/jwt-payload.ts @@ -4,7 +4,7 @@ * Single source of truth so a typo like 'agnet' can't slip through as a bare * string (#143 review). Distinct from `ActorType` (auth principal kind). * - * 'git-sync' marks writes made by the git-sync data plane (plan §8.1). It NEVER + * 'git-sync' marks writes made by the git-sync data plane (issue #194 §8.1). It NEVER * travels in a user-facing token; it is set in-process on the collab connection * context by the native datasource, so it cannot be spoofed from a request. */ diff --git a/apps/server/src/integrations/environment/environment.service.ts b/apps/server/src/integrations/environment/environment.service.ts index b42cb677..975a6c72 100644 --- a/apps/server/src/integrations/environment/environment.service.ts +++ b/apps/server/src/integrations/environment/environment.service.ts @@ -321,7 +321,7 @@ export class EnvironmentService { .filter(Boolean); } - // --- git-sync (plan §7.2) ------------------------------------------------- + // --- git-sync (issue #194 §7.2) ------------------------------------------------- /** Global master switch for the git-sync control plane (default false). */ isGitSyncEnabled(): boolean { diff --git a/apps/server/src/integrations/environment/environment.validation.ts b/apps/server/src/integrations/environment/environment.validation.ts index 8a44d7bd..62539fc8 100644 --- a/apps/server/src/integrations/environment/environment.validation.ts +++ b/apps/server/src/integrations/environment/environment.validation.ts @@ -171,7 +171,7 @@ export class EnvironmentVariables { ) CLICKHOUSE_URL: string; - // --- git-sync (plan §7.2) — all OPTIONAL. The master switch defaults off; a + // --- git-sync (issue #194 §7.2) — all OPTIONAL. The master switch defaults off; a // required-if-enabled service user id is validated only when sync is on. --- @IsOptional() @@ -202,6 +202,13 @@ export class EnvironmentVariables { @IsString() GIT_SYNC_DEBOUNCE_MS: string; + // Watchdog timeout (ms) for the spawned `git http-backend` process (default + // 120000): a stalled receive-pack is killed so it cannot hold the per-space + // lock forever. Optional int (validated as a string env). + @IsOptional() + @IsString() + GIT_SYNC_BACKEND_TIMEOUT_MS: string; + // Defense-in-depth absolute cap on soft-deletes per push cycle (default 5): a // non-convergent / phantom-absence cycle can never trash more than this many // pages without an explicit override. Optional int (validated as a string env). @@ -210,7 +217,7 @@ export class EnvironmentVariables { GIT_SYNC_MAX_DELETES_PER_CYCLE: string; // Required when git-sync is enabled: the service user create/move/rename/delete - // are attributed to (plan §7.2). Optional otherwise. + // are attributed to (issue #194 §7.2). Optional otherwise. @ValidateIf((obj) => obj.GIT_SYNC_ENABLED === 'true') @IsNotEmpty() @IsString() diff --git a/apps/server/src/integrations/git-sync/git-sync.constants.ts b/apps/server/src/integrations/git-sync/git-sync.constants.ts index 9b42a497..75c4fd4f 100644 --- a/apps/server/src/integrations/git-sync/git-sync.constants.ts +++ b/apps/server/src/integrations/git-sync/git-sync.constants.ts @@ -39,9 +39,3 @@ export const GIT_SYNC_LOCK_PREFIX = 'git-sync:lock:'; * and the Redis lock prevents two instances racing the same space. */ export const GIT_SYNC_LOCK_TTL_MS = 5 * 60 * 1000; - -/** Default event-debounce window (ms), overridable via GIT_SYNC_DEBOUNCE_MS. */ -export const GIT_SYNC_DEBOUNCE_MS_DEFAULT = 2000; - -/** Default poll-safety interval (ms), overridable via GIT_SYNC_POLL_INTERVAL_MS. */ -export const GIT_SYNC_POLL_INTERVAL_MS_DEFAULT = 15000; diff --git a/apps/server/src/integrations/git-sync/services/gitmost-datasource.service.ts b/apps/server/src/integrations/git-sync/services/gitmost-datasource.service.ts index 9a22c831..0978be35 100644 --- a/apps/server/src/integrations/git-sync/services/gitmost-datasource.service.ts +++ b/apps/server/src/integrations/git-sync/services/gitmost-datasource.service.ts @@ -423,7 +423,7 @@ export class GitmostDataSourceService { actor: 'git-sync', // PersistenceExtension reads `context.user.id` for lastUpdatedById, so the // service user is required on the context (unlike the bare `{ actor }` - // sketch in the plan). + // sketch in issue #194). user: { id: userId }, }); try {