Skip to content

feat: env-driven ob sync-config bootstrap (change 0011)#4

Merged
fx merged 7 commits into
mainfrom
feat/0011-sync-config-bootstrap
May 6, 2026
Merged

feat: env-driven ob sync-config bootstrap (change 0011)#4
fx merged 7 commits into
mainfrom
feat/0011-sync-config-bootstrap

Conversation

@fx

@fx fx commented May 6, 2026

Copy link
Copy Markdown
Owner

Summary

Implements change 0011: an env-var-driven ob sync-config step that runs once per vault between sync-setup and sync --continuous, so operators can configure file types, excluded folders, sync mode, conflict strategy, configs, and device name without kubectl exec'ing into the pod.

  • New OB_SYNC_* env vars (FILE_TYPES, EXCLUDED_FOLDERS, MODE, CONFLICT_STRATEGY, DEVICE_NAME, CONFIGS) validated at startup; invalid enums fail fast with ConfigError (exit 78).
  • unset → flag omitted (preserve on-disk value); "" → flag forwarded verbatim ("empty to clear" upstream semantic).
  • Per-vault retry envelope mirrors ensureVaultSetup: 5 attempts, 1s/×2/cap 60s, throws treated as transient. Permanent failure marks the vault failed and skips the ob sync spawn.
  • Drive-by refactor: extracted shared runWithBackoff + drain + Backoff types into src/obsidian/backoff.ts, eliminating ~50 LOC of duplication between setup and sync-config.

Spec / change references

Test plan

  • bun run test:cov passes locally with 100% line + function coverage on every src/ file (823 pass / 0 fail / 4 pre-existing env-gated skips, 55 src/ files at 100%)
  • bun run typecheck clean
  • bunx biome check src/ test/ clean
  • All scenarios from the change doc covered by tests:
    • All vars unset → buildSyncConfigArgs returns null; supervisor skips the spawn
    • Empty-string semantic: OB_SYNC_EXCLUDED_FOLDERS=""["sync-config", "--path", "/p", "--excluded-folders", ""]
    • Invalid OB_SYNC_MODEConfigError with exit 78, names the var and lists valid values
    • Invalid file-type token rejected before any ob child is spawned
    • Order of operations on fresh vault: sync-setupsync-configsync --continuous
    • Transient sync-config failure recovers (exit 1 then exit 0; both invocations use same argv; child only spawned after success)
    • Permanent sync-config failure (5 consecutive non-zero exits) marks vault failed; sync --continuous not spawned; other vaults unaffected
  • CI green (lint + typecheck + test) — pending
  • Codecov patch + project gates green — pending

Summary by CodeRabbit

  • New Features

    • Added OB_SYNC_* env vars to drive per-vault sync-config during bootstrap (file-type filtering, excluded folders, mode, conflict strategy, device name, configs).
  • Improvements

    • Integrated an env-driven sync-config step into vault init with capped exponential backoff, retry/cancellation, clearer failure handling that marks failed vaults and affects readiness; startup validation fails fast on invalid values.
  • Documentation

    • Updated README, .env.example and docs to describe OB_SYNC_* semantics (unset vs empty, validation, skip conditions).
  • Tests

    • Added comprehensive tests for parsing, retry/backoff, wiring, ordering, cancellation and failure paths.

fx added 5 commits May 6, 2026 22:21
Adds the SyncConfigEnv type and loadSyncConfigEnv validator that resolves
the OB_SYNC_FILE_TYPES, OB_SYNC_EXCLUDED_FOLDERS, OB_SYNC_MODE,
OB_SYNC_CONFLICT_STRATEGY, OB_SYNC_DEVICE_NAME, and OB_SYNC_CONFIGS
variables. Each field is `string | undefined` so the supervisor can later
distinguish "unset" (skip flag) from "empty" (forward verbatim as the
upstream "empty to clear" sentinel). Invalid enum values fail fast as
ConfigError (exit 78) before any vault directory is touched.

Plumbs the result through Config.syncConfigEnv and updates every existing
Config-literal test fixture so tsc stays clean. The supervisor wiring
that consumes these values lands in a follow-up commit.
New module `src/obsidian/syncconfig.ts` translates a validated
SyncConfigEnv into an `ob sync-config` argv (`buildSyncConfigArgs`) and
runs the spawn through the same retry/backoff envelope as
`ensureVaultSetup`: max 5 attempts, 1s/x2/cap 60s, throws-as-transient.

`buildSyncConfigArgs` returns null when every field is unset so the
supervisor can skip the spawn entirely (preserves on-disk values). Empty
strings are forwarded verbatim as the upstream "empty to clear" sentinel.
Flag order matches the spec table: file-types, excluded-folders, mode,
conflict-strategy, device-name, configs.

Reuses DEFAULT_BACKOFF and backoffDelay from setup.ts so the two
orchestrators stay in lockstep on retry tuning. SyncConfigPermanentError
mirrors SetupPermanentError's shape so the supervisor wiring (next
commit) can handle them identically.
…ange 0011)

The supervisor now invokes applyVaultSyncConfig between ensureVaultSetup
and the continuous `ob sync` child. When every OB_SYNC_* var is unset
the call is a logged no-op (preserves prior behavior). Otherwise the
spawn order on a fresh vault is sync-status → sync-setup → sync-config
→ sync, matching the change doc's order-of-operations scenario.

A SyncConfigPermanentError marks the vault `failed` with lastError and
prevents `child.start()` from being called for that vault — a sync-config
failure on one vault never blocks others (vaults are processed serially
but per-vault failure stays per-vault).

Re-exports SyncConfigPermanentError so callers can recognise the error
class. New tests cover: (1) the all-vars-unset no-op (default), (2) the
order-of-operations scenario with OB_SYNC_FILE_TYPES set, (3) permanent
sync-config failure marking the vault failed and blocking the sync child,
and (4) non-Error throws stringifying into lastError via the catch-handler.
Adds the six OB_SYNC_* variables to the README's Configuration table with
allowed values, "unset preserves" / "empty clears" semantics, and a
worked example explaining the typical .json/.txt/.docx fix
(OB_SYNC_FILE_TYPES=image,audio,pdf,video,unsupported).

Mirrors the documentation in .env.example as a commented OB_SYNC_* block
so operators can copy-paste the file as a starting point.

Marks the change-doc task checkboxes off; the PR-number row is left for
the parent agent to fill once the PR exists.
…nc-config API

Collapses ~50 LOC of duplication between sync-setup and sync-config
orchestrators onto a single retry primitive in src/obsidian/backoff.ts.

- New `runWithBackoff` owns the probe → spawn → exit-code → retry loop;
  throws inside an attempt are synthesised to -1 in one place. Both
  `ensureVaultSetup` and `applyVaultSyncConfig` now delegate.
- `Backoff` (renamed from `SetupBackoff`, alias kept) plus `drain` move
  to backoff.ts and are re-exported.
- `applyVaultSyncConfig(vault, deps, env)` now mirrors `ensureVaultSetup`'s
  signature; `log` lives inside `SyncConfigDeps` as `logger`.
- `SyncConfigEnv` enum constants get proper union types (`SyncMode`,
  `SyncConflictStrategy`, `SyncFileType`, `SyncConfigKey`); validators
  rewritten as `parseSync*` helpers shaped like `parseLevel`/`parseProvider`.
- Drop WHAT-narrating comments in the touched files; keep WHY only.
Copilot AI review requested due to automatic review settings May 6, 2026 22:38
@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds env-driven per-vault ob sync-config bootstrap: new OB_SYNC_* parsing/validation in config, shared backoff/retry primitives, a new sync-config orchestration module, setup refactor to use the backoff runner, wiring of sync-config into the supervisor startup sequence, and corresponding tests/docs/fixtures updates.

Changes

Sync Configuration Bootstrap

Layer / File(s) Summary
Configuration Types & Validation
src/config/index.ts
Adds exported SyncMode, SyncConflictStrategy, SyncFileType, SyncConfigKey, SyncConfigEnv; implements loadSyncConfigEnv(env) parsing/validating OB_SYNC_* vars and preserving unset vs empty-string semantics; exposes config.syncConfigEnv.
Validation Tests
test/config/syncConfig.test.ts, test/config.test.ts
Unit tests for loadSyncConfigEnv and loadConfig covering valid tokens, CSV parsing, empty-string clearing, and failure with ConfigError (exitCode 78) on invalid enums/tokens.
Backoff/Retry Primitives
src/obsidian/backoff.ts, test/obsidian/backoff.test.ts
Adds Backoff, DEFAULT_BACKOFF, backoffDelay(), drain(), and runWithBackoff() with capped exponential backoff, sliceable sleep for cancellation, thrown-error handling (mapped to lastExit: -1), and structured logging; tests cover delays, draining, retries, cancellation, and logs.
Setup Refactor
src/obsidian/setup.ts, test/obsidian/setup.test.ts
ensureVaultSetup() moved to use runWithBackoff; introduces SetupDeps with injectable backoff, obBin, shouldStop; probeStatus simplified; SetupPermanentError carries lastError; tests updated for sleep-chunking assertions.
Sync-config Orchestration
src/obsidian/syncconfig.ts, test/obsidian/syncconfig.test.ts
New module: buildSyncConfigArgs(env,vaultPath) (omits undefined, forwards empty strings, returns null if no flags) and applyVaultSyncConfig() that runs ob sync-config under runWithBackoff; adds SyncConfigPermanentError; tests cover arg ordering, no-op behavior, retries, cancellation, and obBin override.
Supervisor Integration
src/obsidian/index.ts, test/obsidian/index.test.ts
Supervisor invokes applyVaultSyncConfig(...) after ensureVaultSetup and before spawning continuous ob sync; permanent sync-config failure marks vault failed with lastError and prevents continuous sync spawn; SupervisorDeps.setupBackoff retyped to Backoff; re-exports SyncConfigPermanentError.
Test Fixtures & Updates
multiple test/* files (e.g., test/embeddings/index.test.ts, test/http/*, test/mcp/helpers.ts, test/relevance/eval.ts, test/server.test.ts)
Fixtures/config builders updated to include syncConfigEnv: {} so tests compile; many new tests added for backoff, sync-config orchestration, and supervisor wiring.
Documentation & Examples
.env.example, README.md, docs/changes/0011-sync-config-bootstrap.md, docs/index.md, docs/index.yml
Documented OB_SYNC_* env vars and ob sync-config flag mappings, unset vs empty-string semantics, skip behavior when all unset, retry/backoff contract, and marked change 0011 complete.

Sequence Diagram

sequenceDiagram
  participant Supervisor
  participant Setup
  participant SyncConfig
  participant Backoff
  participant ObCLI

  Supervisor->>Setup: ensureVaultSetup(vault)
  alt already configured
    Setup-->>Supervisor: configured
  else not configured
    Setup->>Backoff: runWithBackoff(runSetupOnce)
    Backoff->>ObCLI: run "ob sync-setup"
    ObCLI-->>Backoff: exit (0/!=0)
    Backoff-->>Setup: result (ok/cancelled/failure)
    Setup-->>Supervisor: setup completed / throws SetupPermanentError
  end
  Supervisor->>SyncConfig: buildSyncConfigArgs(cfg.syncConfigEnv, vaultPath)
  alt args === null
    SyncConfig-->>Supervisor: no-op (skip)
  else args present
    SyncConfig->>Backoff: runWithBackoff(runSyncConfig)
    Backoff->>ObCLI: run "ob sync-config" with args
    ObCLI-->>Backoff: exit (0/!=0) or throw
    Backoff-->>SyncConfig: ok / cancelled / final failure
    alt final failure
      SyncConfig-->>Supervisor: throw SyncConfigPermanentError
      Supervisor-->>Supervisor: mark vault failed, set lastError, skip spawn
    else ok
      SyncConfig-->>Supervisor: success
    end
  end
  Supervisor->>ObCLI: spawn "ob sync --continuous" (if not failed)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • fx/ob#3: Main PR implements the env-driven sync-config bootstrap and adds the syncconfig module, backoff primitives, supervisor wiring, tests, and docs — directly related.
  • fx/ob#2: Related edits to src/config/index.ts and env parsing/validation scaffolding that this change extends.

Poem

🐰 "I hopped through flags and backoff tunes,

env whispers set each vault's new run,
retries counted under moonlit loops,
one config call, then syncs begin, one-by-one."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the primary change: adding environment-driven sync-config bootstrap functionality. It references the feature type, the specific capability, and the change number for clear traceability.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/0011-sync-config-bootstrap

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented May 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.91%. Comparing base (8bf5a53) to head (f6bc3de).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff            @@
##             main       #4    +/-   ##
========================================
  Coverage   99.90%   99.91%            
========================================
  Files          59       61     +2     
  Lines        4404     4592   +188     
========================================
+ Hits         4400     4588   +188     
  Misses          4        4            

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an env-var-driven, per-vault ob sync-config bootstrap step (Change 0011) to let operators configure Obsidian Sync behavior via OB_SYNC_* at startup, without interactive pod access.

Changes:

  • Introduces src/obsidian/backoff.ts and refactors sync-setup retries to use the shared runWithBackoff/drain helpers.
  • Implements src/obsidian/syncconfig.ts (buildSyncConfigArgs + applyVaultSyncConfig) and wires it into the supervisor init sequence between sync-setup and sync --continuous.
  • Adds loadSyncConfigEnv validation/plumbing in config, plus comprehensive unit tests and operator docs (README.md, .env.example, change doc task updates).

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/server.test.ts Updates test config fixture to include new required syncConfigEnv.
test/relevance/eval.ts Updates test config fixture to include new required syncConfigEnv.
test/obsidian/syncconfig.test.ts New unit tests covering env→argv, spawn/no-op, retries, and cancellation for sync-config.
test/obsidian/index.test.ts Extends supervisor integration tests for sync-config ordering and permanent-failure behavior.
test/obsidian/backoff.test.ts New unit tests for shared backoff/retry primitive and drain.
test/mcp/helpers.ts Updates test config fixture to include new required syncConfigEnv.
test/indexer/index.test.ts Updates test config fixture to include new required syncConfigEnv.
test/http/routes.test.ts Updates test config fixture to include new required syncConfigEnv.
test/http/helpers.ts Updates test config fixture to include new required syncConfigEnv.
test/http.test.ts Updates test config fixture to include new required syncConfigEnv.
test/embeddings/index.test.ts Updates test config fixture to include new required syncConfigEnv.
test/config/syncConfig.test.ts New unit tests for loadSyncConfigEnv validation/normalization behavior.
test/config.test.ts Adds tests ensuring loadConfig plumbs and surfaces OB_SYNC_* validation.
src/obsidian/syncconfig.ts New ob sync-config orchestration: argv build + backoff-wrapped execution + permanent error.
src/obsidian/setup.ts Refactors setup to use shared backoff/drain/runWithBackoff; re-exports Backoff types.
src/obsidian/index.ts Wires applyVaultSyncConfig into per-vault init before spawning sync --continuous.
src/obsidian/backoff.ts New shared retry primitive (Backoff, DEFAULT_BACKOFF, runWithBackoff, drain).
src/config/index.ts Adds SyncConfig env types + loadSyncConfigEnv validation; plumbs into Config.
README.md Documents OB_SYNC_* env vars and their unset vs empty-string semantics.
docs/changes/0011-sync-config-bootstrap.md Marks core tasks complete for Change 0011 (but status/index tracking still pending).
.env.example Documents optional OB_SYNC_* variables and semantics.

Comment thread docs/changes/0011-sync-config-bootstrap.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/config/index.ts`:
- Around line 238-248: The parseCsvSubset function validates CSV tokens but
returns the original raw string (so "image, pdf" retains whitespace); change it
to return the normalized, validated CSV by joining the trimmed tokens (use the
existing tokens array) so callers receive a clean comma-separated string
(preserve the early-return for empty string). Update the return in
parseCsvSubset to return tokens.join(",") after validation.

In `@src/obsidian/backoff.ts`:
- Around line 82-92: In the catch block that sets code = -1 (when an exception
is treated as transient), capture and preserve the last thrown error (e) into a
variable like lastError so it survives retries instead of being lost; then, when
creating/throwing the permanent error after retry exhaustion, pass that
preserved lastError into the permanent-error constructors so callers can inspect
the original ENOENT/raw error (update the catch around opName/vaultSlug/attempt
i and the code-path that constructs permanent errors to include lastError).
- Around line 100-103: The backoff sleep currently awaits sleep(delay) and only
re-checks shouldStop() after the full delay, which can block stop() for the
entire backoff window; change the wait to be stop-aware by replacing the direct
await sleep(delay) with a cancellable/wakeable wait (e.g. Promise.race between
sleep(delay) and a stop signal) or a small-tick loop that checks shouldStop()
periodically and breaks early; locate the backoff loop using
backoffDelay(backoff, i - 1), shouldStop(), sleep(), and logger.info(`${opName}
backing off`, { vault: vaultSlug, delayMs: delay }) and ensure that if
shouldStop() becomes true during the backoff you abort the sleep and return
immediately so stop() latency is bounded.

In `@test/obsidian/index.test.ts`:
- Around line 690-730: Update the test "non-Error throw from a sync-config
sub-step stringifies into lastError" to assert that the actual non-Error thrown
value ("raw sync-config failure") is included in the supervisor's recorded
lastError (in addition to the existing "permanently" check); locate the spawner
mock (sp.run) that throws the literal string and the supervisor instance (sup)
whose vault state / lastError is inspected (sup.get("v")?.lastError), then add
an expectation that lastError contains the stringified thrown value so the test
fails if the raw error is lost.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 31938038-3f0c-42fc-9f51-9a079e57b00b

📥 Commits

Reviewing files that changed from the base of the PR and between 43e1e0f and 877f5c0.

📒 Files selected for processing (21)
  • .env.example
  • README.md
  • docs/changes/0011-sync-config-bootstrap.md
  • src/config/index.ts
  • src/obsidian/backoff.ts
  • src/obsidian/index.ts
  • src/obsidian/setup.ts
  • src/obsidian/syncconfig.ts
  • test/config.test.ts
  • test/config/syncConfig.test.ts
  • test/embeddings/index.test.ts
  • test/http.test.ts
  • test/http/helpers.ts
  • test/http/routes.test.ts
  • test/indexer/index.test.ts
  • test/mcp/helpers.ts
  • test/obsidian/backoff.test.ts
  • test/obsidian/index.test.ts
  • test/obsidian/syncconfig.test.ts
  • test/relevance/eval.ts
  • test/server.test.ts

Comment thread src/config/index.ts Outdated
Comment thread src/obsidian/backoff.ts
Comment thread src/obsidian/backoff.ts Outdated
Comment thread test/obsidian/index.test.ts
Per Copilot review on PR #4: change 0011's task list is fully
checked off (only the post-merge changelog flip remained), so
flip the change doc Status header and both docs/index.md /
docs/index.yml entries to `complete`, matching the convention
used for changes 0007–0010. Mark the final task as done with
the merged PR number.
@fx fx requested a review from Copilot May 6, 2026 22:44
- parseCsvSubset: return the trimmed/normalized CSV instead of the raw
  string so " image, pdf " no longer survives into `ob sync-config`
  argv as whitespace, turning a config typo into a runtime failure.

- runWithBackoff: preserve the most recent thrown error across retry
  exhaustion via a new optional `lastError` field on the failure
  result, and propagate it through both `SetupPermanentError` and
  `SyncConfigPermanentError` constructors so launch-time ENOENT/raw
  values surface in the supervisor's `lastError` instead of being
  flattened to a generic permanent-failure message.

- runWithBackoff: slice the inter-attempt sleep into ≤250ms chunks
  with a `shouldStop()` poll between each slice so a stop signal
  arriving mid-window aborts promptly instead of holding `stop()`
  open for up to `capMs`.

- Strengthen the supervisor's non-Error-throw test to assert the raw
  thrown value ("raw sync-config failure") survives the chain — the
  existing "permanently" check passed even when the original error
  was lost.

- Update existing backoff/setup/syncconfig retry tests to reflect the
  sliced-sleep timing (sum of slices unchanged; each slice ≤250ms).

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 24 changed files in this pull request and generated no new comments.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/config/index.ts (1)

41-47: ⚡ Quick win

Tighten SyncConfigEnv field types to preserve validation guarantees across modules.

After validation, mode and conflictStrategy can be typed as unions instead of generic string; this prevents accidental invalid values when constructing SyncConfigEnv outside loadSyncConfigEnv.

Proposed typing refinement
 export interface SyncConfigEnv {
   readonly fileTypes?: string;
   readonly excludedFolders?: string;
-  readonly mode?: string;
-  readonly conflictStrategy?: string;
+  readonly mode?: SyncMode | "";
+  readonly conflictStrategy?: SyncConflictStrategy | "";
   readonly deviceName?: string;
   readonly configs?: string;
 }

-function parseSyncMode(raw: string): string {
+function parseSyncMode(raw: string): SyncMode | "" {
   // Empty string is the upstream "empty to clear" sentinel — pass through.
   if (raw === "") return raw;
-  if ((VALID_SYNC_MODES as readonly string[]).includes(raw)) return raw;
+  if ((VALID_SYNC_MODES as readonly string[]).includes(raw)) return raw as SyncMode;
   throw new ConfigError(
     `OB_SYNC_MODE must be one of ${VALID_SYNC_MODES.join("|")} (or empty), got "${raw}"`,
   );
 }

-function parseSyncConflictStrategy(raw: string): string {
+function parseSyncConflictStrategy(raw: string): SyncConflictStrategy | "" {
   if (raw === "") return raw;
-  if ((VALID_SYNC_CONFLICT_STRATEGIES as readonly string[]).includes(raw)) return raw;
+  if ((VALID_SYNC_CONFLICT_STRATEGIES as readonly string[]).includes(raw)) {
+    return raw as SyncConflictStrategy;
+  }
   throw new ConfigError(
     `OB_SYNC_CONFLICT_STRATEGY must be one of ${VALID_SYNC_CONFLICT_STRATEGIES.join("|")} (or empty), got "${raw}"`,
   );
 }

Also applies to: 221-236

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/config/index.ts` around lines 41 - 47, The SyncConfigEnv interface
currently uses generic string types for mode and conflictStrategy which weakens
validation; change those fields to the appropriate string union types (e.g.,
replace mode: string and conflictStrategy: string with the exact allowed unions
or an existing enum type used elsewhere) so only valid values accepted, update
any other identical interface instance noted (the same interface block around
the other occurrence) and ensure loadSyncConfigEnv's returned values conform to
the new unions (adjust exports/imports/tests/types using SyncConfigEnv or
cast/validate accordingly).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/config/index.ts`:
- Around line 41-47: The SyncConfigEnv interface currently uses generic string
types for mode and conflictStrategy which weakens validation; change those
fields to the appropriate string union types (e.g., replace mode: string and
conflictStrategy: string with the exact allowed unions or an existing enum type
used elsewhere) so only valid values accepted, update any other identical
interface instance noted (the same interface block around the other occurrence)
and ensure loadSyncConfigEnv's returned values conform to the new unions (adjust
exports/imports/tests/types using SyncConfigEnv or cast/validate accordingly).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 23c2eb37-7135-4fee-b1af-f73dccd6d9c7

📥 Commits

Reviewing files that changed from the base of the PR and between 141059a and f6bc3de.

📒 Files selected for processing (9)
  • src/config/index.ts
  • src/obsidian/backoff.ts
  • src/obsidian/setup.ts
  • src/obsidian/syncconfig.ts
  • test/config/syncConfig.test.ts
  • test/obsidian/backoff.test.ts
  • test/obsidian/index.test.ts
  • test/obsidian/setup.test.ts
  • test/obsidian/syncconfig.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/obsidian/backoff.ts
  • test/obsidian/index.test.ts
  • test/obsidian/backoff.test.ts

@fx fx merged commit 09a9f21 into main May 6, 2026
5 checks passed
@fx fx deleted the feat/0011-sync-config-bootstrap branch May 6, 2026 22:55
@fx fx mentioned this pull request May 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants