LearnNewsExamplesServices
Frontmatter
id11942
titleSub-of-#11790: tenant-repo-sync residuals (jitter/backoff + concurrency-gate + KB_TENANT_REPO_SYNC_ error codes)
stateClosed
labels
enhancementai
assigneesneo-opus-ada
createdAtMay 25, 2026, 2:15 AM
updatedAtMay 25, 2026, 1:20 PM
githubUrlhttps://github.com/neomjs/neo/issues/11942
authorneo-opus-ada
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 25, 2026, 1:20 PM

Sub-of-#11790: tenant-repo-sync residuals (jitter/backoff + concurrency-gate + KB_TENANT_REPO_SYNC_ error codes)

neo-opus-ada
neo-opus-ada commented on May 25, 2026, 2:15 AM

Parent Issue

#11790 — TenantRepoSyncService scheduler lane MVP. PR #11940 shipped the load-bearing surface (service + scheduling trigger + Orchestrator.poll() wiring + deployment-aware cloud-toggle + CLI operator path). This sub-ticket tracks the Contract Ledger residuals that #11940 explicitly deferred for post-MVP delivery.

Scope (Contract Ledger residuals from #11790)

1. Per-repo cadence + deterministic jitter/backoff (Contract Ledger row 5)

Replace the current single global tenantRepoSyncIntervalMs with per-repo cadence + deterministic jitter:

  • Pure scheduling function computes next-due state from lastRunAttemptAt, per-repo configured cadence (default = global interval), deterministic repoSlug + tenantId salt jitter (to prevent thundering-herd on first bootstrap), and failure backoff (exponential growing on consecutive failures, resetting on success).
  • Per-repo failure backoff state persists alongside lastIngestedRev in the existing tenant-repo-sync-revisions.json.
  • Jitter must be deterministic per repoSlug/tenantId so test runs and operator diagnosis are reproducible.

2. Concurrency-limit gate (Contract Ledger row 7)

Reactive concurrencyLimit config on TenantRepoSyncService caps simultaneous git/ingest work:

  • Default conservative limit (e.g., 2) to avoid multi-tenant CPU/network exhaustion.
  • concurrencyLimit: 1 serializes (current MVP shape).
  • Per-repo work acquires a semaphore slot before cloneIfMissing/fetch/buildIngestEnvelope/ingestSourceFiles; releases after completion (success or failure).
  • Stable error code KB_TENANT_REPO_SYNC_CONCURRENCY_GATE_TIMEOUT for slot-acquisition timeout.

3. Stable KB_TENANT_REPO_SYNC_* error codes (Contract Ledger row 9)

Replace ad-hoc error throws with stable constants per the #11787/#11788 redaction-path discipline:

  • KB_TENANT_REPO_SYNC_TENANT_NOT_FOUND — manual CLI path with unknown tenantId
  • KB_TENANT_REPO_SYNC_REPO_NOT_CONFIGURED — manual CLI path with unknown repoSlug
  • KB_TENANT_REPO_SYNC_SYNC_FAILED — per-repo sync failure (wraps the underlying GitMirror/envelope/ingest error with redacted message)
  • KB_TENANT_REPO_SYNC_MANIFEST_UPDATE_FAILEDtenant-repo-sync-revisions.json write failure
  • KB_TENANT_REPO_SYNC_CONCURRENCY_GATE_TIMEOUT — (from sub-item 2 above)
  • All surfaced errors route via the #11787/#11788 redaction path for credential-bearing hints or git stderr before logging/returning.
  • Callers branch on error.code, not message prose.

Acceptance Criteria

  • AC1 — Per-repo cadence stored in tenant-repo-sync-revisions.json alongside lastIngestedRev; deterministic jitter via repoSlug + tenantId hash; jitter window ≤ 20% of cadence; backoff doubles on consecutive failures and resets on success.
  • AC2 — concurrencyLimit reactive config on TenantRepoSyncService; semaphore implementation; CONCURRENCY_GATE_TIMEOUT error code; tests covering serialized (1), bounded-parallel (2), and gate-timeout paths.
  • AC3 — All TenantRepoSyncService.runTask error paths return {status: 'failed', details: {code: 'KB_TENANT_REPO_SYNC_*'}} per the enumerated taxonomy. Manual CLI surface returns matching exit codes. Tests for each error path including secret-redaction.
  • AC4 — JSDoc updated for runTask to document the error-code taxonomy as part of the contract.

Dependencies

  • Parent: #11790 (MVP scheduler lane already shipped in PR #11940)
  • Substrate: #11787 (TenantRepoAccessContract redaction path), #11788 (GitMirror error surfaces)

Out of Scope

  • Webhook-driven triggering — deferred per Discussion #11782 OQ2 (different lane).
  • Cross-deployment KB-collection conflict resolution — out of scope; tenant/repo scoping handles isolation.
  • Per-repo health dashboard / operator-visible telemetry — owned by #11791.

Why deferred (PR #11940 cycle-1 scope decision)

Per @neo-gpt #11940 cycle-1 review: the MVP poll-wiring + cloud-toggle gap was the load-bearing blocker for the 40h cloud trial. Jitter/backoff and concurrency-gate are sophistication beyond MVP — useful for production scale but not gating trial start (small tenant count, low concurrent traffic). Stable error codes were similarly deferred to keep PR #11940 scoped to load-bearing integration. This ticket tracks the residuals so the original #11790 Contract Ledger commitments are honored as follow-up work.

Authored by: [Claude Opus 4.7] (Claude Code)

tobiu referenced in commit 41a93c4 - "feat(orchestrator): cloud-deployable tenant-repo-sync scheduler lane (#11790) (#11940) on May 25, 2026, 7:48 AM
tobiu referenced in commit 521f18d - "feat(cloud-deployment): operator docs + health/telemetry for tenant-repo ingestion (#11791) (#11951) on May 25, 2026, 8:37 AM
tobiu referenced in commit 3b2fd16 - "feat(orchestrator): stable KB_TENANT_REPO_SYNC_* error code taxonomy (#11942 AC3+AC4) (#11952) on May 25, 2026, 9:46 AM
tobiu referenced in commit d21b41b - "feat(orchestrator): concurrency-limit gate for tenant-repo-sync (#11942 AC2) (#11960) on May 25, 2026, 10:45 AM
tobiu referenced in commit 323a8d5 - "feat(orchestrator): per-repo jitter + exponential backoff for tenant-repo-sync (#11942 AC1) (#11962) on May 25, 2026, 1:20 PM
tobiu closed this issue on May 25, 2026, 1:20 PM