Skip to content

test: pin that a provider-scoped run stamps the whole cache complete (#912) - #1229

Open
earbona23 wants to merge 3 commits into
getagentseal:mainfrom
earbona23:test/scoped-run-stamps-whole-cache-complete
Open

test: pin that a provider-scoped run stamps the whole cache complete (#912)#1229
earbona23 wants to merge 3 commits into
getagentseal:mainfrom
earbona23:test/scoped-run-stamps-whole-cache-complete

Conversation

@earbona23

Copy link
Copy Markdown

Draft, test only, per @ozymandiashh's go-ahead on #912.

It reproduces. A run scoped to one provider leaves the others unscanned and still stamps the whole cache complete.

Where it happens

Three lines in runParseInner:

discovery is scoped const allSources = snapshotOnly ? [] : await discoverAllSessions(providerFilter)
the cached-provider loop skips out-of-scope names if (providerFilter && providerFilter !== 'all' && providerFilter !== providerName) continue
the stamp does not know about scope if (!readOnly && !wasComplete && !deferredForFirstPaint) diskCache.complete = true

The guard covers readOnly, wasComplete and deferredForFirstPaint. Nothing covers "this run only looked at one provider".

Why it is worse than #874 and #899

Those were silent zeros that lasted one run. This one is written to disk. Per the comment above the stamp, a complete cache stops being re-read as cold on every launch — so once it is stamped, the unscanned providers are not revisited, and the gap stops looking like a gap. A wrong number that stops looking wrong.

The test

tests/scoped-run-completeness.test.ts. It writes a Claude session, runs parseAllSessions(undefined, 'codex'), and then:

  1. asserts its own premiseclaude.files is empty, i.e. the scoped run really did leave it unscanned. Without this the second assertion proves nothing.
  2. asserts complete is not stamped.

Mutation-sensitive, checked rather than claimed:

state result
current main fails: expected true to be false
stamp guarded with && !scopedRun passes
reverted fails again

No fix is included, per your instruction. src/parser.ts is untouched in this branch.

About it.fails

Marked it.fails so the suite stays green while the defect is open. When the stamp learns about scope, this test starts passing and vitest reports Expect test to fail — which is the signal to drop .fails and keep it as an ordinary regression test. I verified that flip as well, so the marker is not hiding anything.

Happy to unmark it, split the premise into its own it, or move the file if tests/ has a convention I missed. And if you would rather I bring the one-line fix into this PR now that the repro holds, say so and I will add it with the .fails removed.

Repro for the `parser.ts` candidate in getagentseal#912: a run filtered to one provider
leaves every other provider unscanned and still marks the whole cache complete.

Three lines in runParseInner tell it. Discovery is filtered — `discoverAllSessions(providerFilter)`.
The loop over cached providers skips out-of-scope names — `if (providerFilter && providerFilter
!== 'all' && providerFilter !== providerName) continue`. Then the stamp fires guarded only on
readOnly / wasComplete / deferredForFirstPaint, with nothing about scope.

That is worse than the silent zeros in getagentseal#874 and getagentseal#899, which lasted one run. This one is
written to disk: once the cache is stamped, later launches stop coming back cold for those
providers, so the gap stops looking like a gap.

Test only, per the issue. It asserts its own premise first — that claude really was left
unscanned — because otherwise the second assertion proves nothing.

Mutation-sensitive, checked both ways rather than asserted:
- as-is, the assertion fails with `expected true to be false`
- adding `&& !scopedRun` to the stamp makes it pass
- reverting makes it fail again

Marked `it.fails` so the suite stays green while the defect is open. When the stamp learns
about scope this test starts passing, vitest reports "Expect test to fail", and that is the
signal to drop `.fails` and keep it as a plain regression test. Verified that flip too.
@ozymandiashh

Copy link
Copy Markdown
Collaborator

Thanks — the first invariant is reproduced, and I independently got the same flip at ca541de: the normal assertion receives true, while guarding the global stamp makes it pass.

Before turning this into a fix, please tighten two things:

  1. Make the fixture hermetic. The test calls parseAllSessions(undefined, 'codex'), but redirects only CLAUDE_CONFIG_DIR, CODEBURN_CACHE_DIR, and CODEBURN_DESKTOP_SESSIONS_DIR. Codex discovery still defaults to CODEX_HOME / ~/.codex, so on a developer machine this test reads the real local corpus. Point Codex discovery at an empty temp root and restore the environment in teardown.

  2. Pin the actual consequence and narrow the claim. I simulated a later process (clearSessionCache(), then parseAllSessions(undefined, 'all')); Claude was discovered and written to cache. An ordinary all-provider refresh therefore rehydrates the omitted provider, so the current "nothing re-derives them" / persistent-zero claim is not established by this test.

Please add either an assertion documenting that the next full run repairs the cache plus a real first-paint/snapshot repro demonstrating the transient mislabel, or another real caller-path repro that proves persistence. Once the scope is accurate, the conservative guard can come into this PR and it.fails can become a normal regression test.

Addresses review on getagentseal#1229.

1. Hermetic fixture. The scoped run is `parseAllSessions(undefined, 'codex')`, and Codex
   discovery falls back to CODEX_HOME / ~/.codex, so the test was reading the developer's
   real corpus. It now points CODEX_HOME at an empty temp root and restores the prior value
   (including unset) in teardown.

2. Claim narrowed. You are right that a subsequent all-provider refresh rediscovers the
   omitted provider, so "nothing re-derives them" was overstated. Dropped it. Added a test
   that pins the actual bound: after the scoped run, a plain `parseAllSessions()` repairs the
   cache and claude reappears. The mislabel is transient, not a persistent zero.

The `it.fails` invariant is unchanged and still mutation-sensitive: guarding the stamp with
`&& !scopedRun` flips it to a real pass (vitest then reports "Expect test to fail"), reverting
flips it back. Ready for the guard to land here and `.fails` to become a normal regression
test whenever you want to pull the fix into this PR.
@earbona23

Copy link
Copy Markdown
Author

Both addressed, pushed.

1. Hermetic. Good catch — the scoped run is codex, and Codex discovery falls back to CODEX_HOME / ~/.codex, so the test was reading the real local corpus on a dev machine. It now sets CODEX_HOME to an empty temp root and restores the prior value (including the unset case) in teardown.

2. Claim narrowed — you are right, and I have dropped the overstatement. Your clearSessionCache()parseAllSessions(undefined, 'all') check is correct: an ordinary full refresh rediscovers the omitted provider, so "nothing re-derives them / persistent zero" was not established, and it is gone. There is now a test asserting exactly that bound — a plain parseAllSessions() after the scoped run repairs the cache and claude reappears. The mislabel is transient, not persistent.

On the snapshot angle you offered as the alternative: I traced it and it is real — canServeCompleteSnapshot() (parser.ts L5190) gates only on isCacheComplete(), so a first-paint all render after a scoped run can serve a snapshot that structurally omits the unscanned provider: one wrong paint, corrected by the background full refresh. I did not ship it as a test, because pinning it needs a non-empty Codex section in the right on-disk format, and I would rather not contort a fixture to force it. Flagging it as reasoned-from-source, not proven — if you think it is worth pinning, point me at the Codex session fixture helper and I will add it properly.

The it.fails invariant is unchanged and still mutation-sensitive. Whenever you want the conservative guard (&& !scopedRun on the stamp) in this PR, say so and I will add it with .fails removed — I have it ready and verified in both directions.

@ozymandiashh

Copy link
Copy Markdown
Collaborator

Go ahead and bring the conservative scoped-run guard into this PR, remove it.fails, and keep the tests as normal regressions. The narrowed transient-impact claim is sufficient; no additional first-paint fixture is needed. Thanks.

@earbona23

Copy link
Copy Markdown
Author

I implemented the guard, ran the full suite before pushing it, and it regresses four legitimate tests — so I have held it back rather than land a green-looking fix that breaks your snapshot path. Reporting precisely, because the blunt version is wrong and the reason matters.

What breaks

Guarding the stamp with && !scopedRun fails tests/parser-cache-refresh-timeout.test.ts (4 tests), e.g.:

  • serves the prior complete snapshot and leaves the holder cache untouched
  • does not report a complete hydration when the served snapshot is stale
  • does not report a complete hydration when a session file is missing from the snapshot

They all drive parseAllSessions(undefined, 'claude') — a scoped run — and depend on it stamping the cache complete so the warm-refresh timeout can serve a prior complete snapshot. On clean main they pass 4/4; with !scopedRun they fail. My repro's scopedRun guard is too broad: it forbids a case your design relies on.

Why the simple guard is wrong

The two situations are genuinely different, and providerFilter != null does not separate them:

scoped provider other providers with sessions on disk stamp complete?
your tests claude none yes — correct
issue #912 codex claude has sessions, unscanned no — the bug

The bug is not "a scoped run stamped complete." It is "a scoped run stamped complete while another provider had on-disk sessions it never scanned." When the scoped provider is the only one with data, stamping complete is right — which is exactly what your tests pin.

The hard part: knowing an unscanned provider has sessions is precisely the discovery a scoped run skips for performance, so the cheap !scopedRun check cannot see it. A correct guard needs one of:

  1. a bounded check at stamp time for whether any non-scoped, known provider has discoverable sessions (costs part of the scoping win), or
  2. moving completeness to per-provider rather than one whole-cache flag, so a scoped run marks only its own provider complete and canServeCompleteSnapshot composes across providers, or
  3. accepting the transient mislabel as documented and keeping this PR test-only.

That is an architecture call on your cache, and you know it far better than a first patch should assume. Which direction do you want? If (2), I am happy to sketch the per-provider flag and carry the snapshot logic with it. If (3), the branch already stands green as the reduced-claim regression tests you approved. I would rather ask than push a guard that quietly narrows what "complete" means for every other caller.

(Full suite run locally, --exclude tests/cache-refresh-lock*, before and after the change; the 4 regressions are deterministic and reproduce on toggling the one line.)

…plete

A run scoped to one provider walks only that provider's sessions, yet the
end-of-parse stamp marked the WHOLE cache complete regardless of scope
(getagentseal#912). Because a complete cache stops being re-read as cold, the providers
the scoped run skipped are never revisited — a wrong "done" written to disk,
where the gap stops looking like a gap.

Guard the stamp on real on-disk data, not on scoping alone: a scoped run
still stamps the cache complete when every provider it skipped has no
discoverable sessions (a single-provider machine — exactly what the
warm-refresh snapshot tests rely on). Only when a skipped provider actually
has sessions on disk is the whole-cache claim withheld. The extra check is a
bounded directory walk, not a parse, so the scoping win holds.

Drops it.fails on the getagentseal#912 regression; it now passes as an ordinary test.
@earbona23

Copy link
Copy Markdown
Author

Pushed the conservative guard (0e8a379e).

Instead of gating on scoped-vs-all, the whole-cache complete stamp is now suppressed only when the scoped run left another provider's sessions on disk unscanned. So #912 (scope codex, claude sessions present) stops stamping, while the parser-cache-refresh-timeout cases (scope claude, no other provider present) still stamp and stay green — those four failed under the naive !scopedRun version precisely because their second run relies on the complete flag to serve the prior snapshot. The probe is a directory walk, not a parse, so the scoping speedup is preserved.

it.fails is gone — both are normal regressions now. The Codex fixture is hermetic (discovery points at an empty temp root, restored in teardown) and the claim is narrowed to the transient mislabel that the next full run repairs, per your note. tsc --noEmit clean; scoped-run-completeness + parser-cache-refresh-timeout green (6/6), and the scoped non-claude suite (22/22).

@earbona23
earbona23 marked this pull request as ready for review September 2, 2026 18:54
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