Skip to content

fix(#5915 J1): P0 segment-awareness — stats/reindex-detect/tier-reload (prevents data-loss at flip)#5917

Merged
cajasmota merged 3 commits into
mainfrom
worktree-agent-a349c5202f0b006aa
Jul 22, 2026
Merged

fix(#5915 J1): P0 segment-awareness — stats/reindex-detect/tier-reload (prevents data-loss at flip)#5917
cajasmota merged 3 commits into
mainfrom
worktree-agent-a349c5202f0b006aa

Conversation

@cajasmota

Copy link
Copy Markdown
Owner

Slice J1 of the #5915 register — the P0 segment-awareness gaps that would cause data-loss / silent-break once graphs can be segment-sets. #5901 made the graph LOADER segment-aware but left metadata probes on CurrentGraphPath (returns a flat path absent for a segment-set → "0 entities / never indexed / mtime-0"). Refs #5915.

  • PersistedStatsFromDir (graph/load.go) → routes through ReaderForDir: single-file byte-identical; segment-set opens a MultiReader summing counts → ok=true. Clears the 0-entity cascade (statuswriter, store, status_stats, daemon.go, index_commit) and the incremental "already-indexed?" gate that could force a full-reindex loop.
  • ReindexRequiredReason (graph/load.go) → ReaderForDir + MultiReader.Version(); current v4 segment-set → (false,""); old-version → (true,reason) so a segmented repo auto-reindexes after the fbversion bump (via #5890(g): graceful update/install/reinstall migration for the breaking on-disk format (force-reindex OK) #5907) instead of serving empty.
  • tierReloadCallback + boot cold-register (cmd/grafel/daemon_tier.go) → GetForRepoRef (segment-aware) instead of Get(CurrentGraphPath); freshness uses the manifest mtime; boot cold-register probes the descriptor so a segmented repo is warmable.

Independently reviewed, mutation-proven: FIX1 revert fails the stats + reindex-loop-prevention tests; FIX2 Version() is a real header read and the compare is load-bearing for flip auto-migration; FIX3 ErrUnknownRef-on-empty-ref traced safe (empty ref rejected before the reload hook — no cold-wake regression). Single-file behavior-preserving. 6600 tests pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_017quGgaqK7NRoxGT6BTqV2o

jcajasmc and others added 3 commits July 22, 2026 23:03
PersistedStatsFromDir opened CurrentGraphPath(dir)+fbreader.Open, so for a
segment-set (graph.<gen>/ dir + manifest) the flat .fb path is absent so
ok=false and callers read "0 entities / never indexed". This cascaded to the
status plane (statuswriter), the incremental.go:331 already-indexed gate
(a false !ok forces a FULL REINDEX LOOP), the dashboard store, status_stats,
daemon.go:747 and index_commit.

Route through the segment-aware ReaderForDir: single-file/legacy opens
fbreader.Open(desc.Path) (byte-identical), a segment-set opens a MultiReader
over the gen dir whose EntityCount/RelationshipCount sum across segments so
ok=true plus correct counts.

Tests: segment-set ok=true with summed 6 ents/3 rels; explicit
incremental.go:331 gate-cleared guard; single-file parity; absent ok=false.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017quGgaqK7NRoxGT6BTqV2o
ReindexRequiredReason (the cheap header-only stale-format detector feeding
the engine auto-reindex via statuswriter, doctor checkReindexRequired and the
statusline) opened CurrentGraphPath(dir)+fbreader.Open. For a segment-set the
flat .fb path is absent so it returned (false,"") — segment-blind: after a
future fbversion bump a below-min segment-set would NOT be detected stale, so
NOT auto-reindexed, and would serve empty (defeating #5907 for exactly the
large repos that segment).

Route through ReaderForDir: single-file/legacy reads the same version
(byte-identical, absent so (false,"")); a segment-set opens a MultiReader whose
Version() is segment 0. The same v < minSupportedFBFormatVersion comparison +
FormatVersionReason apply uniformly. fbversion is still 4, so a current v4
segment-set correctly returns false (a no-op today).

Tests: current-version segment-set → (false,""); segment-set with seg 0
patched below min → (true, reason naming both versions + reindex); single-file
parity retained by the existing reindex_required_test.go.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017quGgaqK7NRoxGT6BTqV2o
…J1 FIX-3)

tierReloadCallback (the cold-wake reload) called
daemonMCPCache.Get(CurrentGraphPath(stateDir)); for a segment-set (graph.<gen>/
dir + manifest) the flat .fb path is absent so Get errors and a cold-evicted
segmented repo could never be re-warmed (monolith serving break). Route the
prime through the segment-aware GetForRepoRef, which resolves the descriptor
and opens a MultiReader over the gen dir; single-file/legacy is unchanged.

Also make the stale-detection freshness reference segment-aware (manifest.json
mtime for a segment-set — else isRepoDirtyAfter reads the absent flat path as
"graph missing → never dirty" and silently skips the reactive reindex), and
fix the boot cold-register existence probe (registerKnownGroupsCold) to route
existence through CurrentGraphDescriptor rather than os.Stat(CurrentGraphPath),
so a segmented repo is cold-registered (the prerequisite for ever warming it).

Test: tierReloadCallback warms a hand-built segment-set repo without error and
the cache then holds a gen-dir-keyed MultiReader summing 4 entities. Windows-
safe (InvalidateDir drops the mmap before TempDir cleanup).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017quGgaqK7NRoxGT6BTqV2o
@cajasmota
cajasmota merged commit da40fa8 into main Jul 22, 2026
1 of 2 checks passed
@cajasmota
cajasmota deleted the worktree-agent-a349c5202f0b006aa branch July 22, 2026 15:31
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