Zed and Shelley both store multiple conversations in a shared SQLite database, so their provider boundary needs to model the physical database source separately from per-session virtual paths. Keeping that source shape explicit makes lookup, watch classification, and force-replace parse behavior available through the shared facade instead of the legacy adapter path.
The providers preserve physical DB discovery, WAL/SHM change classification, raw and full ID lookup, virtual source fingerprints, multi-session parse fan-out, per-session Shelley content fingerprints, and parser output normalization.
fix(parser): define sqlite provider deletion semantics
Shared SQLite providers need to treat deletion events as source-level state, not as unclassifiable paths. Without that, a deleted Zed or Shelley database can disappear before the provider facade reports a complete empty source, leaving future cleanup behavior under-specified.
Classifying syntactically valid DB, WAL, and SHM paths even after the main DB is gone preserves the watcher-to-parse path. A missing backing DB now produces a complete force-replace SkipNoSession outcome, and the Zed fingerprint comment records the intentional legacy whole-DB hash tradeoff.
fix(parser): align zed shelley capabilities
Provider capabilities are used as a contract for what normalized content a provider can actually emit. Shelley currently records token totals from messages but does not emit aggregate usage events, and Zed filters child threads before relationship fields can surface.
Keep the declarations conservative so the facade does not advertise unsupported content features while the providers continue to preserve the parser behavior they actually expose today.
fix(parser): tighten sqlite sidecar classification
Deleted DB sidecar events need to remain classifiable, but basename-only matching is too broad once missing DB parses become complete force-replace outcomes. Unrelated files under the same provider root should not synthesize the canonical shared DB source.
Restrict Zed sidecars to the watched threads directory and Shelley sidecars to the provider root, with regression tests for unrelated matching basenames.
fix(parser): shadow zed shelley providers
The Zed and Shelley branch had concrete providers and passing provider coverage, but still left both agents marked legacy-only. That kept the stack additive and prevented provider changed-path classification from participating while both shapes run.
Move both agents into shadow compare on their migration branch so the runtime bridge can exercise the concrete providers before the legacy path is removed later in the stack.
Validation: go test -tags "fts5" ./internal/parser -run 'Test(ZedProvider|ShelleyProvider|ProviderMigrationModes)' -count=1; go test -tags "fts5" ./internal/sync -run 'TestSync.*(Zed|Shelley)' -count=1; go test -tags "fts5" ./internal/parser -count=1; go test -tags "fts5" ./internal/sync -count=1; go vet ./...; git diff --check
fix(sync): tolerate deleted sqlite provider sources
Zed and Shelley providers can classify removed physical database paths while shadow comparison is enabled. Legacy sync still owns writes on this branch, so those forced remove events must not fail at the pre-parse stat step.
Treat provider-classified deleted physical SQLite sources as an OK no-result parse. This keeps the archive-preserving legacy behavior while avoiding watcher failures until provider-authoritative deletion semantics are implemented at the stack tip.
Validation: go test -tags "fts5" ./internal/parser ./internal/sync -run 'TestEngine_(ClassifyPathsProviderRemoveKeepsDeletedSQLiteSources|ProcessFileProviderDeletedSQLiteSourcesDoNotFail)|Test(Zed|Shelley)ProviderClassifiesDeletedPhysicalDB|Test(Zed|Shelley)Provider' -count=1; go test -tags "fts5" ./internal/parser ./internal/sync -count=1; go fmt ./...; go vet ./...; ./custom-gcl run --config .golangci.nilaway.yml ./internal/parser/... ./internal/sync/...; git diff --check
test(sync): compare zed shelley shadow parity
Zed and Shelley are shadow-compared database-backed providers, so provider method tests are not enough to prove the sync migration preserves legacy parser output.
Cover physical DB source observation for both providers and compare sessions, messages, force-replace intent, and data-version planning against the legacy DB parsers.
Validation: go test -tags "fts5" ./internal/parser ./internal/sync -run 'TestObserveProviderSourceMatches(Zed|Shelley)LegacyParser|Test(Zed|Shelley)Provider' -count=1; go test -tags "fts5" ./internal/parser ./internal/sync -count=1; go fmt ./...; go vet ./...; ./custom-gcl run --config .golangci.nilaway.yml ./internal/parser/... ./internal/sync/...; git diff --check
test(parser): cover sqlite provider stored hints
SQLite fan-out providers need to treat stored virtual source paths differently depending on caller intent. Fresh lookups should reject deleted rows or malformed/stale virtual paths, while non-fresh lookup still needs to preserve the virtual source identity so changed-path cleanup can observe a SkipNoSession tombstone.
This closes part of the stored-hint compatibility gap for the Zed/Shelley branch without making provider writes authoritative. The tests document row deletion, invalid virtual paths, stale DB-path hints, and tombstone parse behavior while legacy sync remains the write path.
Validation: go test -tags "fts5" ./internal/parser -run 'Test(Zed|Shelley)Provider' -count=1 -v; go test -tags "fts5" ./internal/sync -run 'TestObserveProviderSourceMatches(Zed|Shelley)LegacyParser|TestEngine_ClassifyPathsProviderRemoveKeepsDeletedSQLiteSources|TestEngine_ProcessFileProviderDeletedSQLiteSourcesDoNotFail' -count=1 -v; go test -tags "fts5" ./internal/parser ./internal/sync -count=1 (sync fails only known TestSyncPathsCodexIndexEventRefreshesStoredDuplicate on this branch); go fmt ./...; go vet ./...; manual ./custom-gcl package loop with GOMAXPROCS=1 GOGC=5 GOMEMLIMIT=128MiB; git diff --check
Generated with Codex
Co-authored-by: Codex <codex@openai.com>
refactor(parser): fold zed and shelley providers
Move Zed and Shelley source ownership onto their concrete providers and
delete the ten package-level legacy entrypoints
(DiscoverZedSessions, FindZedSourceFile, ParseZedSQLiteVirtualPath,
ParseZedThreadDirect, ParseZedThreadFromDB, DiscoverShelleySessions,
FindShelleySourceFile, ParseShelleyConversationDirect,
ParseShelleyConversationFromDB, ParseShelleyVirtualPath) plus the
now-orphaned FindShelleyDBPath helper. Both agents become
provider-authoritative so runtime sync routes through provider
discovery, changed-path classification, and processProviderFile instead
of the removed processZed/processShelley methods and syncSingleZed.
Both providers keep multiple conversations in one shared SQLite
database addressed by a "<dbPath>#<id>" virtual path. The fold preserves
that shape:
- Discovery surfaces the single physical threads.db / shelley.db as one
source; Parse fans it out to one session per thread/conversation.
- Virtual-path resolution flows through the provider-neutral
ParseVirtualSourcePathForBase helper. parseZedVirtualPath restores the
legacy IsValidSessionID guard the bespoke parser enforced; parseShelley
VirtualPath maps directly onto the shared helper. Every engine call
site that split a virtual path now uses the neutral helper too, and the
surviving ZedSQLiteSourceMtime / ShelleySourceMtime watchers were
repointed at it.
- The Zed and Shelley single-conversation direct parses move onto the
providers as parseThreadDirect / parseConversationDirect over the
unexported parseZedThreadFromDB / parseShelleyConversationFromDB.
Because a provider has no database handle, the engine reproduces the
per-session skip the legacy fan-out loops performed in
dropUnchangedSharedSQLiteResults: the provider re-parses every session on
any database change, and the engine drops results whose stored file_mtime
(plus the content fingerprint in file_hash for Shelley's second-precision
timestamps) and data_version already match, applying the path rewriter so
remote stored paths resolve. Force-parse runs keep every result. A forced
parse on a deleted shared database now completes as an empty force-replace
in processProviderFile so the engine retires the removed sessions instead
of failing. ParseDiff synthesizes the Zed/Shelley database source the way
it already does for Kiro/OpenCode/Kilo so --agent zed/shelley keeps
working without a DiscoverFunc.
Tests move from the deleted free functions to provider API coverage, add
a guard asserting the legacy entrypoints stay gone, drop both provider
files from the pending shim scan list, and remove the shadow comparison
test. The shared writeProviderShadowSourceFile helper is rehomed into a
dedicated support file so the sync package keeps compiling after the
shadow test is deleted.
refactor(parser): delete zed legacy whole-database parser
ParseZedSessions parsed every top-level thread in a Zed threads.db, but
the provider routes per-thread through parseZedThreadFromDB, so the
free function survived only as test-exercised dead production code.
Delete ParseZedSessions; the retained parse tests reproduce the
whole-database walk with the provider's own primitives (ListZedThreadMetas
+ parseZedThreadFromDB), which share the top-level parent_id filter and
ordering, so they exercise the production path without the deleted shim.
fix(sync): preserve zed shelley force replaces
Zed and Shelley share one physical SQLite database across many virtual session paths. Provider-authoritative sync needs source-level force-replace behavior to retire those virtual sessions when a physical database disappears or when a provider reports a complete empty outcome.
SyncSingleSession also marks the discovered source with ForceParse, so unchanged-result filtering must respect the per-file flag instead of only the engine-wide flag. Otherwise a targeted resync can silently keep corrupted stored rows because the shared SQLite fingerprint has not changed.
Validation: go test -tags "fts5" ./internal/sync -run 'TestSync(SingleSessionZedForce|PathsZedDeleted|SingleSessionShelleyForce|PathsShelleyDeleted)' -count=1; go test -tags "fts5" ./internal/parser -run 'Test(Zed|Shelley)Provider' -count=1; go test -tags "fts5" ./internal/sync -run 'Test.*(Zed|Shelley).*' -count=1; go vet ./...; git diff --check
Zed and Shelley now have concrete parser providers for their shared SQLite database layouts. The providers model physical DB sources separately from per-session virtual paths, preserving discovery, watch planning, WAL/SHM changed-path classification, raw and full ID lookup, source fingerprinting, multi-session parse fan-out, force-replace parse semantics, and parser output normalization.
Zed uses DB-backed thread discovery with per-thread virtual sources, while Shelley keeps its per-conversation content fingerprint so same-second metadata and message rewrites remain visible through the provider facade.