OpenCode, Kilo, and MiMoCode share the same storage/session, message, part, and legacy SQLite source model. Moving them behind one concrete provider keeps that shared source contract explicit instead of spreading it across sync-only classifier paths.
The provider preserves storage-first discovery, hybrid SQLite fallback, duplicate filtering, child-file changed-path classification, SQLite virtual paths, composite source mtimes, storage fingerprints, and fork-specific ID relabeling.
fix(parser): classify removed opencode storage sessions
OpenCode-family storage sessions are watched recursively, so delete and rename-style events for the primary session JSON need to map back to the same provider source even after the file no longer exists. Without that syntactic fallback, provider-path sync can miss stale storage sources until a broader resync.
Move OpenCode, Kilo, and MiMoCode into shadow comparison on this branch so the stack continues as a real migration rather than an additive provider implementation.
Validation: go test -tags "fts5" ./internal/parser -run 'Test(OpenCodeProvider|OpenCodeFamilyProvider|ProviderMigration)' -count=1; go test -tags "fts5" ./internal/parser -count=1; go vet ./...; git diff --check
fix(sync): classify removed opencode session files
The provider path now handles deleted OpenCode-family storage session JSONs, but the legacy SyncPaths classifier is still active during the migration. It needs the same syntactic fallback so watcher-driven sync remains behaviorally equivalent while both forms run.
Validation: go test -tags "fts5" ./internal/sync -run 'TestEngine_ClassifyPathsOpenCodeFamilyRemovedSessionFile' -count=1; go test -tags "fts5" ./internal/parser -run 'Test(OpenCodeProvider|OpenCodeFamilyProvider|ProviderMigration)' -count=1; go test -tags "fts5" ./internal/sync -count=1; go vet ./...; git diff --check
test(sync): compare opencode family shadow parity
OpenCode, Kilo, and MiMoCode share the OpenCode-format provider implementation on this branch, so add source-level migration coverage for all three storage-mode source shapes.
The table test compares provider observation with each legacy parser and verifies session/message/data-version parity while preserving provider-computed storage fingerprints.
Validation: go test -tags "fts5" ./internal/parser ./internal/sync -run 'TestObserveProviderSourceMatchesOpenCodeFamilyLegacyParsers|TestOpenCode|TestParseOpenCode|TestParseKilo|TestParseMiMoCode|TestDiscoverKilo|TestDiscoverMiMoCode|TestProviderMigrationModes' -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
refactor(parser): fold opencode family into providers
OpenCode, Kilo, and MiMoCode share one on-disk format (storage/session
JSON plus message/part files, with a legacy SQLite fallback exposed as
<db>#<sessionID> virtual paths). They were still shims: the concrete
provider delegated to package-level free functions and the agents stayed
LegacyOnly, which violated the migration manifest invariant (a concrete
provider must not remain legacy-only) and left the runtime on the legacy
sync dispatch.
Make the three providers authoritative and own their behavior. One
shared openCodeFormatProvider implementation is parameterized per agent
by a format struct (SQLite filename, storage session subdir, ID prefix);
Kilo and MiMoCode reuse the OpenCode storage and SQLite readers and only
relabel the parsed session onto their own agent and ID prefix, so the
parse/discover/find logic is not duplicated three times.
The 15 legacy free functions (Discover/Find/ParseFile/ParseSession/
ParseSQLiteVirtualPath for each of opencode/kilo/mimocode) are deleted.
ParseOpenCodeFile/ParseOpenCodeSession move to unexported helpers
(parseOpenCodeStorageFile/parseOpenCodeDBSession) that the provider spec
drives. SQLite virtual-path resolution now goes through the
provider-neutral ParseVirtualSourcePathForBase, so engine, parsediff, and
resume callers no longer reference deleted parsers.
Remove the opencode-family legacy engine dispatch: the classify blocks
and classifyOpenCodeFormatPath, the processFile arm and
processOpenCodeFormat, the DB-backed sync pass, single-session resync,
and orphaned helpers. Runtime now routes through provider changed-path
classification and processProviderFile. Because these agents now flow
through file discovery, the resync empty-discovery guard tracks a
non-container discovered count so a self-preserving storage store cannot
mask plain file-backed sessions whose directories went empty, and
parse-diff discovers them through the provider facade.
fix(sync): skip provider-authoritative agents in parse-diff db synthesis
parseDiffDatabaseSources synthesized a raw opencode.db/kilo.db source so
the legacy processOpenCode fan-out re-parsed every DB session. Once those
agents became provider-authoritative, parseDiffProviderSources already
enumerates their DB sessions through the provider, which applies the
storage-ID filter that drops a file-backed storage session's stale db
row. Re-adding the raw db then double-counted those sessions and parsed
the filtered storage row, surfacing a spurious ParseError and an extra
examined file. Skip agents that have dropped their DiscoverFunc; the Kiro
data.sqlite3 synthesis still runs because Kiro keeps its legacy
DiscoverFunc until its own fold.
refactor(parser): delete opencode legacy whole-database parser
ParseOpenCodeDB parsed every session in an OpenCode SQLite database, but
the provider (and the Kilo/MiMoCode reuse) routes per-session through
parseOpenCodeDBSession, so the free function survived only as
test-exercised dead production code.
Delete ParseOpenCodeDB along with the orphaned loadOpenCodeSessions and
the OpenCodeSession bundle type; loadOpenCodeProjects stays since the
per-session path also resolves worktrees through it. The retained parse
tests reproduce the whole-database walk with the provider's own
primitives (ListOpenCodeSessionMeta + parseOpenCodeDBSession).
fix(sync): preserve parse-diff virtual sqlite identity
OpenCode-family providers expose SQLite sessions as per-session virtual sources. Parse-diff was still collapsing those db#session paths to the shared database path before error attribution, presence sweep, and limit accounting, which could apply one session's parse failure or omitted sample to every sibling in the DB.
Keep exact source keys for OpenCode, Kilo, and MiMoCode provider virtual SQLite paths while retaining shared-base grouping for true physical multi-session jobs. Source existence checks still stat the physical DB path so virtual identities do not look missing.
Validation: go test -tags "fts5" ./internal/sync -run 'TestParseDiffProviderVirtualSQLite(ErrorUsesExactSource|PresenceUsesExactSource|LimitUsesExactSource)|TestStripVirtualSourceSuffixVisualStudioCopilot' -count=1; go test -tags "fts5" ./internal/sync -run 'TestParseDiff(CoversMixedOpenCodeRoot|CoversMixedKiloRoot|ProviderVirtualSQLite|PresenceSweep|LimitNewestFirst|ReportHasFailures)' -count=1; go test -tags "fts5" ./internal/parser -run 'Test(OpenCodeProvider|OpenCodeFamilyProvider|Kilo|MiMoCode)' -count=1; go vet ./...; git diff --check
OpenCode, Kilo, and MiMoCode now share a concrete parser provider for their common storage format. The provider owns storage session files, message and part child-file classification, legacy SQLite virtual paths, hybrid storage-plus-SQLite discovery, duplicate filtering, and storage fingerprinting.\n\nThis keeps the existing parser helpers and fork-specific relabeling behavior while exposing discovery, watch planning, changed-path classification, lookup, fingerprinting, and parse output through the shared provider interface.