OpenHands stores each conversation as a directory with metadata and event files, so the provider needs a directory source facade rather than a JSONL file wrapper. This keeps the legacy discovery and dashed/undashed ID lookup behavior while making the composite snapshot fingerprint explicit at the provider boundary.
The provider uses the existing OpenHands parser and snapshot helpers so freshness, shallow watch planning, changed-path classification, and normalized parse output stay aligned with the legacy sync path.
test(parser): opt openhands into provider shadow
OpenHands now has a concrete facade provider on this branch, so its migration mode should enter shadow comparison instead of remaining legacy-only and additive.
Earlier provider opt-ins stay inherited and later provider branches own their modes.
Validation: go test -tags "fts5" ./internal/parser -run TestProviderMigrationModes -count=1; go test -tags "fts5" ./internal/parser -count=1; go vet ./...; git diff --check
test(sync): compare openhands shadow parity
OpenHands is shadow-compared on this branch, so add source-level migration coverage that compares provider observation with ParseOpenHandsSession.
The test uses the directory snapshot source shape so the provider fingerprint path and planned data-version behavior stay visible while the branch migrates away from legacy dispatch.
Validation: go test -tags "fts5" ./internal/parser ./internal/sync -run 'TestObserveProviderSourceMatchesOpenHandsLegacyParser|TestOpenHandsProvider|TestParseOpenHands|TestDiscoverAndFindOpenHands|TestClassifyOnePath_OpenHands|TestProcessFileOpenHandsUsesSnapshotMtimeForRetryCache' -count=1; go test -tags "fts5" ./internal/parser ./internal/sync -count=1; go fmt ./...; go vet ./...; git diff --check; ./custom-gcl run --config .golangci.nilaway.yml ./internal/parser/... ./internal/sync/...
refactor(parser): fold openhands into provider
Move OpenHands discovery, source lookup, and parse ownership onto the
concrete provider and delete the package-level DiscoverOpenHandsSessions,
FindOpenHandsSourceFile, and ParseOpenHandsSession free functions.
Discovery now walks conversation roots directly in the provider source
set, raw-session-ID lookup folds the literal/dash-stripped/normalized
matching into sessionDirForID, and parsing runs on a provider receiver
method. The provider-neutral snapshot, session-dir predicate, and event
parse helpers stay as shared free functions.
Make OpenHands provider-authoritative and remove its legacy sync
dispatch: the classifyOnePath block, the processFile case arm, the
OpenHands snapshot-mtime branch, and processOpenHands are gone. Sync now
classifies and processes OpenHands through provider changed-path
handling, which preserves the base_state.json/TASKS.json/events companion
remap to the session directory and keeps the snapshot mtime driving the
skip-retry cache via the provider fingerprint.
Drop the OpenHands AgentDef DiscoverFunc/FindSourceFunc hooks, remove the
shadow baseline test, exempt the provider file from the shim scan, and
add a guard asserting the legacy entrypoints stay deleted.
OpenHands now has a concrete provider facade for conversation directories under the configured conversations root.
The provider preserves legacy direct-child discovery, shallow watch planning, dashed and undashed raw-ID lookup, component-file changed-path classification, composite snapshot fingerprinting over base_state.json/TASKS.json/events/*.json, and normalized parse output from the existing OpenHands parser.