Repository navigation
Description
Problem
A real OpenCode install exposed a schema shape the Phase 3 adapter does not
recognise. The adapter was validated against upstream anomalyco/opencode
v2.2.x, whose message parts live in a separate session_part table. An
intermediate v2.0.x install instead stores message content inline in
session_message.data and has no session_part table at all.
voyager/adapters/opencode.py declares two shapes in SCHEMAS (v2 and
legacy), both of which require a parts table. detect_schema() therefore
matches neither, probe() returns ('unknown-schema', None), discover()
returns an empty source set, and no session from that install is indexed.
The failure itself is honest (unusable_sources() names the database instead of
silently reporting zero sessions or marking history SOURCE_MISSING), but the
provider is unusable on that real install.
The synthetic suite (27 tests) cannot catch this: its fixtures are built from
reviewable DDL modelled on the v2.2.x shape. This is exactly why
UNIT_VERIFIED on synthetic data must not be reported as LIVE_VERIFIED.
Goal
Support both real shapes, distinguished by structural schema features, never
by a version string or a hard-coded product version number:
- v2.0.x (inline) —
session_v2+session_message(content inline in
data) +project, with nosession_part. - v2.2.x (parts) — the shape Phase 3 already implements
(session_partwithmessage_id,session_id,ordinal,type,
call_id,data).
Detection must be based on actual tables and required columns (as
detect_schema() already does), plus the shape of session_message.data where
needed. Do not make probe() pass by simply dropping the session_part
requirement — the subsequent read path must actually parse the inline shape
correctly.
Required for the inline shape
- Session discovery and stable identity (
session_v2.id, title, directory,
project association). - Message reading and role mapping from the
typecolumn (user,
assistant, and provider-generated types such asmodel-switched/idle). - Content extraction from the inline
dataJSON (text plus any structured
fields actually present); tool calls / tool results where the record carries
them, andNOT_FOUND-style honest absence where it does not. - Timestamps (ms → s) and a stable per-session ordering (
seq, with a
deterministic tiebreak), so rescans are idempotent. - Provenance from structural fields only — the message
typecolumn and
explicit source flags; never infer a human turn from text. Provider-generated
messages must not be promoted touser. - Incremental sync and WAL:
-wal/-shmremain in the discovered source
set so a running agent's new turns trigger a rescan. - Honest degradation for unknown message types: record them without
inventing content, roles or exit codes; do not fabricate atool_resultfor a
call that never completed.
Tests and isolation
- Fully isolated
HOME,USERPROFILEandXDG_DATA_HOMEin every test that
discovers databases. - A real database is used only to construct a consistent, isolated snapshot;
all SQLite verification connections target the snapshot or a copy. No test
connects to a developer's real profile, and no user message text or database
path is committed into a fixture or report. - Keep all existing tests: legacy shape, v2.2.x parts shape, unknown schema,
corrupt / absent databases, WAL discovery,SOURCE_MISSINGretention,
streaming equivalence. - Add an anonymised v2.0.x fixture (table/column names and JSON key shapes
only, no real content) covering: schema discrimination, message extraction,
tool calls, CJK text, empty fields, WAL-driven incremental rescan. - Address the discovery-path finding:
_candidate_dbs()currently appends the
default data-dir database even when an explicitVOYAGER_OPENCODE_DB/
OPENCODE_DBoverride is set. Define and test a clear path priority so an
operator-named database does not cause an unrequested probe of the default
HOME database. At minimum, test isolation must be airtight; never silently
access a real database the operator did not specify.
Acceptance (real-install Gate)
- Gate A: adapter results compared row-for-row (structure and counts) against an
independent read-onlysqlite3oracle on the isolated snapshot. - Gate B: before/after SHA-256 of the real
.db/-wal/-shm, row counts,
and file-set side effects. - Gate C: capability matrix declarations agree with real evidence.
- Directed OpenCode tests, full Core suite,
test_docs.py, and static checks. - Capabilities not observed on a real install stay explicitly
NOT_VERIFIED—
in particular native resume (the adapter denies it) and cross-agent
continuation. Session discovery succeeding must not be reported as either. LIVE_VERIFIEDonly for capabilities with real-install evidence and a
recorded reason. Synthetic results stayUNIT_VERIFIED.
Scope / boundaries
- Phase 3 design input:
docs/audits/2026-11-opencode-adapter-design.md;
implementation report:docs/audits/2026-11-opencode-phase3-report.md
(both frozen — this issue does not rewrite their verification claims). - No change to
Store, continuity, the bridge schema, any existing CLI
contract, or any other provider adapter. - No database migration, no new runtime dependency (stdlib
sqlite3only). - Out of scope: Evidence Verification (
#22/ dsh-sessionflow#9), native
resume, channel-specific behaviour beyond the existing probe, release or tag
changes. - Work happens on the integration branch;
phase3-opencodeand its backup ref
are preserved. Merge requires all three Gates passing and maintainer review.
Verification level
Design/scope only: NOT_VERIFIED. Real-install evidence will be attached to
the PR, never to this issue as a claim.
Activity
HarryHeYu commentedon Nov 12, 2026
Implemented and opened for review: PR #25 —
feat(opencode): read-only SQLite adapter with v2.0.x inline schema support.Shape detection is structural (
SCHEMAS+SCHEMA_FORBID):v2-inlinerequiressession_v2+session_messageand forbidssession_part, so a v2.2.x database still resolves tov2. No version string is consulted.Real-install gate on one v2.0.29 install (isolated copy;
HOME/USERPROFILE/XDG_DATA_HOMEredirected; every connection on the copy):probe -> ok/v2-inline, 8/8 sessions, per-kind counts exactly match an independent read-onlysqlite3oracle (user 68, text 206, reasoning 2053, tool 2617, tool_result 2617), zero mismatches..db/-wal/-shmSHA-256 and all row counts unchanged.NOT_FOUND, cross-agent continuationNOT_VERIFIED.Also fixed here: an explicit
VOYAGER_OPENCODE_DB/OPENCODE_DBis now authoritative, so an operator-named database no longer causes an unrequested probe of the default HOME database.Directed: 36 opencode tests; with docs/isolation/consistency 105 passed / 3 skipped;
check_consistency.py --strictclean. Report:docs/audits/2026-11-opencode-v20x-real-gate.md(no user text, paths or hashes). Not merged — left for maintainer review.HarryHeYu commentedon Nov 12, 2026
Final review passed — no blocking issues, CI 10/10 green at head
9603164(PRMERGEABLE/CLEAN).Independent checks (27/27) covered: three-shape detection and priority (
v2wins whensession_partexists), inlinedataparsing, event order/counts, tool call↔result pairing, WAL incremental rescan + idempotence, read-only connection and the documented-shmside effect, override path isolation, and unknown-schema / corrupt-JSON / unknown-type handling.One P1 found in review and fixed in
9603164: the capability-matrix comment still claimed "no real install was observed" and described only v2/legacy — corrected to three shapes plus one real v2.0.29 observation, states kept atUNIT_VERIFIED(noLIVE_VERIFIED).Gate scope is narrow and stated as such: one install, one version (v2.0.29), one platform (Windows). Native resume stays
NOT_FOUND; cross-agent continuationNOT_VERIFIED. Not merged — merge is a separate explicit step, no release in this round.HarryHeYu commentedon Nov 12, 2026
Resolved by PR #25, squash-merged as
ee9fd9eon 2026-11-12.mainCI on that commit: success, 10/10 jobs (py3.10–3.13 × ubuntu/windows + core-install no-extras).Verified present on
mainafter the merge:voyager/adapters/opencode.py—v2-inlineshape,SCHEMA_FORBID(a database carryingsession_partstill resolves tov2),_inline_parts(),_tool_exit()(readsstate.metadata.exit, keeps0), and the authoritativeVOYAGER_OPENCODE_DB/OPENCODE_DBdiscovery priority.voyager/capability_matrix.py— corrected wording: three shapes exercised synthetically, one real v2.0.29 install observed; all states stayUNIT_VERIFIED.tests/test_opencode.py(36 tests) andtests/opencode_fixtures.pywith the anonymisedmake_v20_inline/V20_INLINE_DDLfixture.Real-install gate on the one observed install (isolated copy): probe
ok/v2-inline, 8/8 sessions, per-kind counts exactly matching an independent read-onlysqlite3oracle (user 68, text 206, reasoning 2053, tool 2617, tool_result 2617), zero mismatches; real.db/-wal/-shmhashes and all 20 table row counts unchanged. Report:docs/audits/2026-11-opencode-v20x-real-gate.md.Not promoted: native resume stays
NOT_FOUND(adapter denies resume), cross-agent continuationNOT_VERIFIED, inlineuser.filesNOT_VERIFIED(empty in observed data), multi-channel and non-WindowsNOT_VERIFIED. Evidence covers one install, one version (v2.0.29), one platform (Windows).Closing as completed. No release or tag change was made.