Skip to content

maintain: a reconciled store is not a stale one - #573

Merged
linhdmn merged 1 commit into
mainfrom
fix/reconcile-refresh-inventory
Oct 21, 2026
Merged

linhdmn merged 1 commit into
mainfrom
fix/reconcile-refresh-inventory

Conversation

@linhdmn

@linhdmn linhdmn commented Oct 21, 2026

Copy link
Copy Markdown
Member

The v4.19 reconcile made the two projects it touched report possibly_stale. The signal was lying about a store the reconcile had just made more correct.

Cause. Freshness is derived, not asserted (internal/store/backend.go:319-336): the inventory's seq is compared with the write watermark, and a difference reads possibly_stale. A delete advances that watermark — DeleteByFile and DeleteFileRecord both call BumpWatermark() — so the reconcile moved the watermark and left the inventory behind. The fleet doctor's own hint was then "re-index the stale projects", for stores that were exactly as fresh as they had been a minute earlier.

Measured on the live fleet (the wave's first pass, from the serve log):

maintain: 1 pass(es) over 20 store(s): reclaimed 1228800 bytes, grew 0 bytes, pruned 189 metric row(s)
  and 19 embed-run row(s), closed 0 stuck run(s), compacted the FTS index of 0 store(s),
  reconciled 6 stale file(s) (67 element(s)) with HEAD

and the next doctor run reported agentloop: current, possibly_stale and x-trader: current, possibly_stale — the two projects the reconcile had just touched. Both were fresh before.

Fix. The reconcile refreshes the inventory when it has deleted content (store.RefreshInventory), exactly as the core layer does after an index: the summary of what the store holds must match the store.

Tests

internal/maintain/reconcile_inventory_test.go — both fail on origin/main:

--- FAIL: TestReconcileKeepsTheStoreFresh
    the store reports "possibly_stale" after the reconcile, want "fresh" — a reconciled store is not a stale one
--- FAIL: TestReconcileRefreshesTheInventoryCounts
    the inventory says 2 elements, the store holds 1

and both pass with the fix. go build -p=2 ./..., go vet, gofmt clean; go test ./internal/maintain/ ./internal/store/ ./internal/core/ green.

The v4.19 reconcile made the two projects it touched report "possibly_stale".
Freshness is derived: the inventory's seq is compared with the write
watermark, and a delete advances that watermark — DeleteByFile and
DeleteFileRecord both call BumpWatermark() — so the reconcile moved the
watermark and left the inventory behind. The signal then said "the tree may
have moved" about a store that had just been made to MATCH the tree, and the
fleet doctor's own hint was "re-index the stale projects".

The reconcile now refreshes the inventory when it has deleted content, the
same way the core layer does after an index: the summary of what the store
holds must match the store.

Tests: TestReconcileKeepsTheStoreFresh — FAIL on origin/main ("the store
reports \"possibly_stale\" after the reconcile, want \"fresh\""), PASS here.
TestReconcileRefreshesTheInventoryCounts — FAIL on origin/main ("the
inventory says 2 elements, the store holds 1"), PASS here.
@linhdmn
linhdmn merged commit 9061d69 into main Oct 21, 2026
5 checks passed
@linhdmn
linhdmn deleted the fix/reconcile-refresh-inventory branch October 21, 2026 18:27
linhdmn added a commit that referenced this pull request Oct 22, 2026
The two follow-up fixes (#573, #574) carry no conventional commit prefix, so
release-please reads them as unreleasable and no release was cut for them.
They are on main and in the shipped binary; they are simply not in a tagged
release. The trailer forces the cut.

Release-As: 0.35.3
linhdmn added a commit that referenced this pull request Oct 22, 2026
…ips the wave (#577)

The PRD named 0.35.2 while cmd/leankg/VERSION, the release stamp in
internal/mcp/server.go, the published release (v0.35.3) and the installed
binary were all 0.35.3. The reconciliation that stamped 0.35.2 ran before the
0.35.3 cut, and the cut moved the version past it again.

v0.35.3 carries the wave's seven fixes and the two follow-ups (#573, #574);
their subjects carry no conventional prefix, so release-please cannot classify
them and the cut was declared with a Release-As trailer (the pattern AGENTS.md
documents), with their changelog entries added by hand.
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.

1 participant