* fix(import): coerce non-string frontmatter title/slug/type (#1939)
YAML `title: 2024-06-01` parses to a Date and `title: 1458` to a number;
the old `(frontmatter.X as string)` cast was a compile-time lie, so
downstream `.toLowerCase()` threw and (via the importer failure gate)
could wedge sync indefinitely. parseMarkdown now coerces via
coerceFrontmatterString (Date -> UTC ISO date, deterministic), and the
pure assessContentSanity self-protects against a non-string title.
* feat(sync): bounded auto-skip failure ledger; poison file can't wedge indexing (#1939)
New src/core/sync-failure-ledger.ts owns the failure store + a crash-safe,
multi-source, concurrent bounded auto-skip valve. A file that fails N
consecutive syncs (GBRAIN_SYNC_AUTOSKIP_AFTER, default 3) auto-skips so it
can't freeze all indexing forever, while fresh failures still fail-closed
and a `<head>` history-rewrite sentinel hard-blocks even with --skip-failed.
- (source_id, path) keying — failures never merge across sources
- success clears a path so attempts are truly consecutive
- advance-before-ack ordering (a crash can't mark a file skipped while wedged)
- shared applySyncFailureGate used by BOTH the incremental and full-sync gates
- legacy-row normalization + duplicate collapse on load
- cross-process lock + atomic temp-rename, age-based stale-lock break
sync.ts re-exports the ledger for existing callers; import.ts records
source-scoped and defers the bookmark to the gate under managedBookmark.
* fix(doctor): sync_failures severity via one shared decision on both surfaces (#1939)
Local buildChecks and remote doctorReportRemote now both route through
decideSyncFailureSeverity, so a stuck bookmark escalates WARN -> FAIL
consistently (oldest-open age > fail cadence, or large unresolved count),
auto-skipped pages stay visible (WARN, not hidden), and the
acknowledged/acknowledged_at field-split that caused drift is gone. The
remote surface stays subprocess-free (file read + Date.parse only).
* chore(test): add trailing newline to e5-lease-cap-ab baseline fixture
* fix(sync): address adversarial review findings on the failure ledger (#1939)
- #1: a parse-failed file that is later deleted/renamed-away no longer leaves
a permanent open ledger row. Removed paths (filtered.deleted, renamed-from,
and the "gone from disk" forward-delete skip branch) are treated as resolved
so the ledger self-heals instead of aging doctor to a stuck FAIL.
- #3: decideSyncFailureSeverity escalates to FAIL on OPEN (blocking) failures
only — auto_skipped rows already advanced the bookmark, so they stay
WARN-visible regardless of count, matching the state-machine contract.
* chore: bump version and changelog (v0.42.30.0)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs: document sync-failure ledger + auto-skip valve for v0.42.30.0
KEY_FILES.md: new src/core/sync-failure-ledger.ts entry (bounded auto-skip
state machine, decideGateAction/decideSyncFailureSeverity/applySyncFailureGate,
GBRAIN_SYNC_AUTOSKIP_AFTER); update sync.ts (failure store moved to ledger,
re-exported), doctor.ts (sync_failures severity via shared rule on both
surfaces), markdown.ts (coerceFrontmatterString), import.ts (managedBookmark).
live-sync.md: poison-file auto-skip tricky-spot. Regenerated llms-full.txt.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: re-bump to v0.42.31.0 (queue collision on 0.42.30.0)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: re-bump to v0.42.32.0 (queue collision)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>