# Tool Event SQLite Continuation

P017 stores physical event 2+ of each new continuous tool semantic run in:

```text
<OYSTERUN_CONFIG_DIR>/routec-tool-events.sqlite
```

The first physical event remains a normal Matrix event and carries:

```text
tool_storage_generation: sqlite_continuation_v1
```

Absence of that exact marker means `legacy_p142_matrix_retained`. The frontend
must not request combined SQLite detail for a legacy event, and the Host repeats
the generation check before resolving a run.

## Tables

`tool_runs` stores one row per consecutive tool run. It owns the generation,
session/room, first retained Matrix event ID, provider/turn/grouping identity,
consecutive run index, state, last physical event index, and timestamps. A
single-event run therefore still has an explicit run row. The grouping identity
index is unique across generation, session, room, grouping-key kind/value, and
consecutive run index.

`tool_event_continuations` stores physical event 2+ with primary key
`(run_ref, tool_event_index)`. `(run_ref, event_key)` rejects duplicate provider
delivery. The foreign key to `tool_runs` uses `ON DELETE CASCADE`.

`tool_event_store_meta` owns schema identity/version, migration state, cutover
timestamps, active write generation, and recovery classification.

## Cutover

Schema creation and version cutover run in one SQLite transaction. New writes
are enabled only after meta reports a complete v2 schema and
`sqlite_continuation_v1` write generation. An interrupted transaction leaves
the prior schema unchanged and can be retried.

The superseded Stage 2 draft could have created a v1 continuation table from
legacy JSONL. During cutover that table is renamed to
`tool_event_continuations_recovery_v1`; it is never queried by the live
resolver. P142 `large_tool_calls` JSONL is not scanned, imported, or used as a
product fallback. It remains recovery evidence only.

An `active` row belongs to the current Host process. On Host restart, the store
closes any inherited `active` row before accepting new writes. Missing terminal
tool content remains visible as an incomplete logical invocation, and a later
run receives a different run identity instead of joining the interrupted run.

## Run And Write Boundary

The run contains immediately consecutive `tool.call`, `tool.update`,
`tool.output`, `tool.result`, and `tool.failure` events. Hidden thinking deltas
do not end it. A durably committed non-tool semantic event or explicit provider
turn terminal closes every active run for that session/room. This boundary does
not depend on the non-tool event repeating the provider turn grouping key.

The first event is committed to Matrix and registers `tool_runs`. Event 2+ is
inserted synchronously and transactionally in source order. A duplicate is
idempotent; an index gap fails closed. A failed continuation write cannot fall
back to JSONL or claim delivery. No lifecycle notice, timeline count, or badge
is created.

## Detail

`GET /session/tool-event-detail` requires authorized session/room/event
identity and the exact `sqlite_continuation_v1` request generation. Host checks
the Matrix marker, resolves the matching run, combines physical event 1 from
Matrix/P131 detail with event 2+ from SQLite, preserves physical order, then
groups by provider/target turn plus `tool_call_id`.

The logical view is Call, ordered Updates, Output, and Result/Failure. Missing
`tool_call_id` is an isolated incomplete invocation. Local paths inside tool
payload remain content; database and resolver paths are not response fields.
Search remains Matrix-only.

Historical P142 Matrix events remain individual tool boxes with their own
retained preview and single-event expansion. They never resolve into this
SQLite store.

## Client Completeness Validation

An HTTP 200 response is not sufficient proof that the complete P017 run was
resolved. During an installed-package update, static frontend files can be
replaced before the already-running Node Host exits. In that bounded window a
new frontend can call an older in-memory endpoint which knows only the retained
Matrix event. Rendering that response would silently present one row as the
complete run even though SQLite contains more events.

For a `sqlite_continuation_v1` request, the frontend therefore accepts the
response only when it proves all of the following:

```text
status: ok
tool_storage_generation: sqlite_continuation_v1
continuation_storage_kind: host_tool_event_continuation_sqlite
matrix_retained_plus_sqlite_continuation: true
requested page matches response page
physical/logical counts and invocations have valid shapes
```

A stale or malformed HTTP 200 response is not a legacy fallback. The detail
view reports that the Host is still running an older Tool Detail pipeline and
asks the Owner to restart the Oysterun Host. After restart, the endpoint reads
the Matrix anchor plus the SQLite continuation and the same detail view renders
all logical invocations. The browser must not manufacture continuation rows or
read Host-local storage directly.

## Cleanup

The store exposes one authoritative session-deletion operation. It deletes the
matching `tool_runs` rows; SQLite foreign-key cascade removes continuation rows,
and later resolution returns unavailable. Oysterun currently has no product
Delete Session API/UI. Stop, restart, and internal history-record replacement
are not deletion and must not call this operation because retained Matrix
anchors still require their detail. A future product deletion route must call
the store operation only after its own authoritative delete succeeds.

Run focused verification with:

```bash
node host-service/test-p017-cinny-raw-pagination-and-tool-sqlite.mjs
node host-service/test-unified-tool-lifecycle-detail.mjs
node host-service/test-p019-tool-detail-pipeline-completeness.mjs
```
