@hraness/oh 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hraness/oh",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "open-source tools for agentic research",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,8 +30,8 @@ oh --help
30
30
  oh version
31
31
  ```
32
32
 
33
- The supported CLI is the exact npm release `@hraness/oh@0.3.0`. Its identical
34
- tarball and checksum are mirrored by the immutable GitHub Release `v0.3.0`.
33
+ The supported CLI is the exact npm release `@hraness/oh@0.3.2`. Its identical
34
+ tarball and checksum are mirrored by the immutable GitHub Release `v0.3.2`.
35
35
  It requires Bun 1.3.14 or newer. The versioned contract is published at
36
36
  <https://oh.computer/spec/>.
37
37
 
package/spec/README.md CHANGED
@@ -18,7 +18,8 @@ binds these versions:
18
18
  | SQLite schema | `2` |
19
19
  | Sync protocol | `oh.sync.v1` |
20
20
  | Local embedding profile | `1` |
21
- | Hosted semantic cache | `oh.cloudflare.embeddinggemma.v1` |
21
+ | Hosted semantic cache V1 | `oh.cloudflare.embeddinggemma.v1` |
22
+ | Hosted semantic cache V2 | `oh.semantic-cloud.v2` |
22
23
  | Projection semantics | `oh.projection.positive-datalog.v1` |
23
24
  | Composite memory | `experimental v1` |
24
25
  | Memory page | `oh.memory-page.v1` |
@@ -34,6 +35,7 @@ binds these versions:
34
35
  - [Sync protocol](v1/sync.md)
35
36
  - [Local embedding profile](v1/embedding.md)
36
37
  - [Hosted semantic cache](v1/semantic-cloud.md)
38
+ - [Isolated hosted semantic cache V2](v2/semantic-cloud.md)
37
39
  - [Derived projections](v1/projection.md)
38
40
  - [Experimental composite agent memory](v1/memory.md)
39
41
  - [Memory pages and `.oh.md` interchange](v1/memory-page.md)
@@ -46,6 +48,11 @@ Machine-readable V1 artifacts:
46
48
  - [`embedding-profile.json`](v1/embedding-profile.json)
47
49
  - [`cloudflare-embedding-profile.json`](v1/cloudflare-embedding-profile.json)
48
50
  - [`cloudflare-embedding-renderer.json`](v1/cloudflare-embedding-renderer.json)
51
+ - [`libsql-semantic-cache-schema-v1.sql`](v1/libsql-semantic-cache-schema-v1.sql)
52
+ - [`libsql-semantic-digest-fixture-v1.json`](v1/libsql-semantic-digest-fixture-v1.json)
53
+ - [`semantic-cloud V2 manifest`](v2/manifest.json)
54
+ - [`libsql-semantic-cache-schema-v2.sql`](v2/libsql-semantic-cache-schema-v2.sql)
55
+ - [`libsql-semantic-digest-fixture-v2.json`](v2/libsql-semantic-digest-fixture-v2.json)
49
56
  - [`contract.schema.json`](v1/contract.schema.json)
50
57
  - [`record.schema.json`](v1/record.schema.json)
51
58
  - [`schema-revision.schema.json`](v1/schema-revision.schema.json)
@@ -25,9 +25,13 @@
25
25
  "specification": "./v1/projection.md"
26
26
  },
27
27
  "semanticCloud": {
28
+ "cacheSchema": "./v1/libsql-semantic-cache-schema-v1.sql",
29
+ "cacheSchemaSha256": "d9903735e900e08a8c49c9ad36ea8f277b1689117ef88b9a2c6b906456dfbace",
30
+ "digestFixture": "./v1/libsql-semantic-digest-fixture-v1.json",
28
31
  "profile": "./v1/cloudflare-embedding-profile.json",
29
32
  "renderer": "./v1/cloudflare-embedding-renderer.json",
30
- "specification": "./v1/semantic-cloud.md"
33
+ "specification": "./v1/semantic-cloud.md",
34
+ "successor": "./v2/manifest.json"
31
35
  },
32
36
  "schemas": [
33
37
  "./v1/contract.schema.json",
@@ -0,0 +1,114 @@
1
+ CREATE TABLE IF NOT EXISTS oh_semantic_schemas (
2
+ version INTEGER PRIMARY KEY,
3
+ name TEXT NOT NULL UNIQUE,
4
+ schema_sha256 TEXT NOT NULL,
5
+ applied_at TEXT NOT NULL
6
+ ) STRICT;
7
+
8
+ CREATE TABLE IF NOT EXISTS oh_semantic_vectors (
9
+ profile_sha256 TEXT NOT NULL,
10
+ renderer_sha256 TEXT NOT NULL,
11
+ input_sha256 TEXT NOT NULL,
12
+ vector_sha256 TEXT NOT NULL,
13
+ vector BLOB NOT NULL,
14
+ created_at TEXT NOT NULL,
15
+ PRIMARY KEY(profile_sha256, renderer_sha256, input_sha256)
16
+ ) STRICT;
17
+
18
+ CREATE TABLE IF NOT EXISTS oh_semantic_generations (
19
+ authority_id TEXT NOT NULL,
20
+ generation INTEGER NOT NULL CHECK(generation >= 0),
21
+ authority_sha256 TEXT NOT NULL,
22
+ profile_sha256 TEXT NOT NULL,
23
+ renderer_sha256 TEXT NOT NULL,
24
+ membership_sha256 TEXT NOT NULL,
25
+ generation_sha256 TEXT NOT NULL UNIQUE,
26
+ document_count INTEGER NOT NULL CHECK(document_count >= 0),
27
+ chunk_count INTEGER NOT NULL CHECK(chunk_count >= 0),
28
+ created_at TEXT NOT NULL,
29
+ PRIMARY KEY(authority_id, generation)
30
+ ) STRICT;
31
+
32
+ CREATE TABLE IF NOT EXISTS oh_semantic_memberships (
33
+ authority_id TEXT NOT NULL,
34
+ generation INTEGER NOT NULL CHECK(generation >= 0),
35
+ generation_sha256 TEXT NOT NULL,
36
+ record_key TEXT NOT NULL,
37
+ record_sha256 TEXT NOT NULL,
38
+ ordinal INTEGER NOT NULL CHECK(ordinal >= 0),
39
+ input_sha256 TEXT NOT NULL,
40
+ PRIMARY KEY(authority_id, generation, record_key, ordinal)
41
+ ) STRICT;
42
+
43
+ CREATE TABLE IF NOT EXISTS oh_semantic_heads (
44
+ authority_id TEXT PRIMARY KEY,
45
+ generation INTEGER NOT NULL CHECK(generation >= 0),
46
+ authority_sha256 TEXT NOT NULL,
47
+ profile_sha256 TEXT NOT NULL,
48
+ renderer_sha256 TEXT NOT NULL,
49
+ membership_sha256 TEXT NOT NULL,
50
+ generation_sha256 TEXT NOT NULL,
51
+ published_at TEXT NOT NULL
52
+ ) STRICT;
53
+
54
+ CREATE TABLE IF NOT EXISTS oh_semantic_purges (
55
+ authority_id TEXT PRIMARY KEY,
56
+ purged_at TEXT NOT NULL
57
+ ) STRICT;
58
+
59
+ CREATE INDEX IF NOT EXISTS oh_semantic_memberships_generation
60
+ ON oh_semantic_memberships(authority_id, generation, record_key, ordinal);
61
+
62
+ CREATE INDEX IF NOT EXISTS oh_semantic_memberships_input
63
+ ON oh_semantic_memberships(input_sha256);
64
+
65
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_vectors_no_update
66
+ BEFORE UPDATE ON oh_semantic_vectors
67
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic vectors are immutable'); END;
68
+
69
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_generations_no_update
70
+ BEFORE UPDATE ON oh_semantic_generations
71
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic generations are immutable'); END;
72
+
73
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_memberships_no_update
74
+ BEFORE UPDATE ON oh_semantic_memberships
75
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic memberships are immutable'); END;
76
+
77
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_generations_purge_guard
78
+ BEFORE INSERT ON oh_semantic_generations
79
+ WHEN EXISTS (SELECT 1 FROM oh_semantic_purges WHERE authority_id = NEW.authority_id)
80
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic authority was purged'); END;
81
+
82
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_memberships_purge_guard
83
+ BEFORE INSERT ON oh_semantic_memberships
84
+ WHEN EXISTS (SELECT 1 FROM oh_semantic_purges WHERE authority_id = NEW.authority_id)
85
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic authority was purged'); END;
86
+
87
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_memberships_published_guard
88
+ BEFORE INSERT ON oh_semantic_memberships
89
+ WHEN EXISTS (SELECT 1 FROM oh_semantic_heads
90
+ WHERE authority_id = NEW.authority_id AND generation = NEW.generation)
91
+ AND NOT EXISTS (SELECT 1 FROM oh_semantic_memberships
92
+ WHERE authority_id = NEW.authority_id AND generation = NEW.generation
93
+ AND generation_sha256 = NEW.generation_sha256
94
+ AND record_key = NEW.record_key AND record_sha256 = NEW.record_sha256
95
+ AND ordinal = NEW.ordinal AND input_sha256 = NEW.input_sha256)
96
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic generation is published'); END;
97
+
98
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_heads_insert_purge_guard
99
+ BEFORE INSERT ON oh_semantic_heads
100
+ WHEN EXISTS (SELECT 1 FROM oh_semantic_purges WHERE authority_id = NEW.authority_id)
101
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic authority was purged'); END;
102
+
103
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_heads_update_purge_guard
104
+ BEFORE UPDATE ON oh_semantic_heads
105
+ WHEN EXISTS (SELECT 1 FROM oh_semantic_purges WHERE authority_id = NEW.authority_id)
106
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic authority was purged'); END;
107
+
108
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_purges_no_update
109
+ BEFORE UPDATE ON oh_semantic_purges
110
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic purge markers are immutable'); END;
111
+
112
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_purges_no_delete
113
+ BEFORE DELETE ON oh_semantic_purges
114
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic purge markers are immutable'); END;
@@ -0,0 +1,13 @@
1
+ {
2
+ "authorityId": "fixture:semantic-authority",
3
+ "authoritySha256": "d1eac8dd93ce6e04cb1316c670008250b98ec32cb0555f21d10b782ddffe03a7",
4
+ "content": "fixture body",
5
+ "generation": 7,
6
+ "generationSha256": "d2beeb0082da8ae3545ade90642a6366ecb209a673dbbbed350a6fec9b1157e3",
7
+ "inputSha256": "b2309e89dd3f903934c7b5f4a4c8c51d5323d84a66928a2f017dd871492ef294",
8
+ "membershipSha256": "34f1d2be0152f5626824a5b62ab75771ee906e3abf6b7159b3dfc348a23bdfbc",
9
+ "recordKey": "memory:fixture",
10
+ "recordSha256": "920d936e47e8f3cc5847017da5975ef73f8d84c04cb113880fb10575eeb614f9",
11
+ "title": "fixture title",
12
+ "v": 1
13
+ }
@@ -68,6 +68,15 @@ hit is rejoined to a caller-supplied current authority record digest. A stale
68
68
  head, changed authority digest, changed record digest, concurrent publish, or
69
69
  purge returns no stale authority.
70
70
 
71
+ `publishedHead` returns the current compare-and-swap base or `null` for an
72
+ absent or purged authority. Its bounded projection contains only the authority,
73
+ generation, profile, renderer, membership, generation and publication
74
+ identities already held by the cache; it exposes no vector, record body or
75
+ formatted input. The read verifies that the pointer still matches its immutable
76
+ generation and fails with an integrity error on divergence. A host can read the
77
+ base, stage a later authoritative generation, and pass the returned generation
78
+ to `publish` without keeping a second cache pointer elsewhere.
79
+
71
80
  `purgeAuthority` writes a permanent tombstone, removes the head, memberships,
72
81
  and staged generations, then deletes vectors no remaining authority uses. The
73
82
  tombstone prevents the same authority ID from being staged or published again.
@@ -0,0 +1,175 @@
1
+ CREATE TABLE IF NOT EXISTS oh_semantic_schemas (
2
+ version INTEGER PRIMARY KEY,
3
+ name TEXT NOT NULL UNIQUE,
4
+ schema_sha256 TEXT NOT NULL,
5
+ applied_at TEXT NOT NULL
6
+ ) STRICT;
7
+
8
+ CREATE TABLE IF NOT EXISTS oh_semantic_isolations (
9
+ isolation_sha256 TEXT PRIMARY KEY,
10
+ authority_id TEXT NOT NULL,
11
+ created_at TEXT NOT NULL
12
+ ) STRICT;
13
+
14
+ CREATE TABLE IF NOT EXISTS oh_semantic_vectors (
15
+ isolation_sha256 TEXT NOT NULL,
16
+ profile_sha256 TEXT NOT NULL,
17
+ renderer_sha256 TEXT NOT NULL,
18
+ input_sha256 TEXT NOT NULL,
19
+ vector_sha256 TEXT NOT NULL,
20
+ vector BLOB NOT NULL,
21
+ created_at TEXT NOT NULL,
22
+ PRIMARY KEY(isolation_sha256, profile_sha256, renderer_sha256, input_sha256)
23
+ ) STRICT;
24
+
25
+ CREATE TABLE IF NOT EXISTS oh_semantic_generations (
26
+ authority_id TEXT NOT NULL,
27
+ generation INTEGER NOT NULL CHECK(generation >= 0),
28
+ authority_sha256 TEXT NOT NULL,
29
+ isolation_sha256 TEXT NOT NULL,
30
+ profile_sha256 TEXT NOT NULL,
31
+ renderer_sha256 TEXT NOT NULL,
32
+ membership_sha256 TEXT NOT NULL,
33
+ generation_sha256 TEXT NOT NULL UNIQUE,
34
+ document_count INTEGER NOT NULL CHECK(document_count >= 0),
35
+ chunk_count INTEGER NOT NULL CHECK(chunk_count >= 0),
36
+ created_at TEXT NOT NULL,
37
+ PRIMARY KEY(authority_id, generation)
38
+ ) STRICT;
39
+
40
+ CREATE TABLE IF NOT EXISTS oh_semantic_memberships (
41
+ authority_id TEXT NOT NULL,
42
+ generation INTEGER NOT NULL CHECK(generation >= 0),
43
+ generation_sha256 TEXT NOT NULL,
44
+ isolation_sha256 TEXT NOT NULL,
45
+ record_key TEXT NOT NULL,
46
+ record_sha256 TEXT NOT NULL,
47
+ ordinal INTEGER NOT NULL CHECK(ordinal >= 0),
48
+ input_sha256 TEXT NOT NULL,
49
+ PRIMARY KEY(authority_id, generation, record_key, ordinal)
50
+ ) STRICT;
51
+
52
+ CREATE TABLE IF NOT EXISTS oh_semantic_heads (
53
+ authority_id TEXT PRIMARY KEY,
54
+ generation INTEGER NOT NULL CHECK(generation >= 0),
55
+ authority_sha256 TEXT NOT NULL,
56
+ isolation_sha256 TEXT NOT NULL,
57
+ profile_sha256 TEXT NOT NULL,
58
+ renderer_sha256 TEXT NOT NULL,
59
+ membership_sha256 TEXT NOT NULL,
60
+ generation_sha256 TEXT NOT NULL,
61
+ published_at TEXT NOT NULL
62
+ ) STRICT;
63
+
64
+ CREATE TABLE IF NOT EXISTS oh_semantic_purges (
65
+ authority_id TEXT PRIMARY KEY,
66
+ isolation_sha256 TEXT NOT NULL,
67
+ profile_sha256 TEXT NOT NULL,
68
+ published_generation INTEGER CHECK(published_generation IS NULL OR published_generation >= 0),
69
+ published_generation_sha256 TEXT,
70
+ purged_at TEXT NOT NULL,
71
+ purge_marker_sha256 TEXT NOT NULL,
72
+ generation_count INTEGER NOT NULL CHECK(generation_count >= 0),
73
+ membership_count INTEGER NOT NULL CHECK(membership_count >= 0),
74
+ orphan_vector_count INTEGER NOT NULL CHECK(orphan_vector_count >= 0),
75
+ isolation_scope_count INTEGER NOT NULL CHECK(isolation_scope_count >= 0),
76
+ counts_recorded INTEGER NOT NULL CHECK(counts_recorded IN (0, 1))
77
+ ) STRICT;
78
+
79
+ CREATE INDEX IF NOT EXISTS oh_semantic_isolations_authority
80
+ ON oh_semantic_isolations(authority_id, isolation_sha256);
81
+
82
+ CREATE INDEX IF NOT EXISTS oh_semantic_memberships_generation
83
+ ON oh_semantic_memberships(authority_id, generation, record_key, ordinal);
84
+
85
+ CREATE INDEX IF NOT EXISTS oh_semantic_memberships_input
86
+ ON oh_semantic_memberships(isolation_sha256, input_sha256);
87
+
88
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_isolations_no_update
89
+ BEFORE UPDATE ON oh_semantic_isolations
90
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic isolations are immutable'); END;
91
+
92
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_isolations_no_delete
93
+ BEFORE DELETE ON oh_semantic_isolations
94
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic isolations are immutable'); END;
95
+
96
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_isolations_purge_guard
97
+ BEFORE INSERT ON oh_semantic_isolations
98
+ WHEN EXISTS (SELECT 1 FROM oh_semantic_purges WHERE authority_id = NEW.authority_id)
99
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic authority was purged'); END;
100
+
101
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_vectors_no_update
102
+ BEFORE UPDATE ON oh_semantic_vectors
103
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic vectors are immutable'); END;
104
+
105
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_vectors_isolation_guard
106
+ BEFORE INSERT ON oh_semantic_vectors
107
+ WHEN NOT EXISTS (SELECT 1 FROM oh_semantic_isolations
108
+ WHERE isolation_sha256 = NEW.isolation_sha256)
109
+ OR EXISTS (SELECT 1 FROM oh_semantic_purges AS purge
110
+ JOIN oh_semantic_isolations AS isolation
111
+ ON isolation.authority_id = purge.authority_id
112
+ WHERE isolation.isolation_sha256 = NEW.isolation_sha256)
113
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic vector isolation is unavailable'); END;
114
+
115
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_generations_no_update
116
+ BEFORE UPDATE ON oh_semantic_generations
117
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic generations are immutable'); END;
118
+
119
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_memberships_no_update
120
+ BEFORE UPDATE ON oh_semantic_memberships
121
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic memberships are immutable'); END;
122
+
123
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_generations_purge_guard
124
+ BEFORE INSERT ON oh_semantic_generations
125
+ WHEN EXISTS (SELECT 1 FROM oh_semantic_purges WHERE authority_id = NEW.authority_id)
126
+ OR NOT EXISTS (SELECT 1 FROM oh_semantic_isolations
127
+ WHERE isolation_sha256 = NEW.isolation_sha256 AND authority_id = NEW.authority_id)
128
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic authority or isolation is unavailable'); END;
129
+
130
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_memberships_purge_guard
131
+ BEFORE INSERT ON oh_semantic_memberships
132
+ WHEN EXISTS (SELECT 1 FROM oh_semantic_purges WHERE authority_id = NEW.authority_id)
133
+ OR NOT EXISTS (SELECT 1 FROM oh_semantic_generations
134
+ WHERE authority_id = NEW.authority_id AND generation = NEW.generation
135
+ AND generation_sha256 = NEW.generation_sha256
136
+ AND isolation_sha256 = NEW.isolation_sha256)
137
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic authority or isolation is unavailable'); END;
138
+
139
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_memberships_published_guard
140
+ BEFORE INSERT ON oh_semantic_memberships
141
+ WHEN EXISTS (SELECT 1 FROM oh_semantic_heads
142
+ WHERE authority_id = NEW.authority_id AND generation = NEW.generation)
143
+ AND NOT EXISTS (SELECT 1 FROM oh_semantic_memberships
144
+ WHERE authority_id = NEW.authority_id AND generation = NEW.generation
145
+ AND generation_sha256 = NEW.generation_sha256
146
+ AND isolation_sha256 = NEW.isolation_sha256
147
+ AND record_key = NEW.record_key AND record_sha256 = NEW.record_sha256
148
+ AND ordinal = NEW.ordinal AND input_sha256 = NEW.input_sha256)
149
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic generation is published'); END;
150
+
151
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_heads_insert_purge_guard
152
+ BEFORE INSERT ON oh_semantic_heads
153
+ WHEN EXISTS (SELECT 1 FROM oh_semantic_purges WHERE authority_id = NEW.authority_id)
154
+ OR NOT EXISTS (SELECT 1 FROM oh_semantic_generations
155
+ WHERE authority_id = NEW.authority_id AND generation = NEW.generation
156
+ AND generation_sha256 = NEW.generation_sha256
157
+ AND isolation_sha256 = NEW.isolation_sha256)
158
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic authority or isolation is unavailable'); END;
159
+
160
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_heads_update_purge_guard
161
+ BEFORE UPDATE ON oh_semantic_heads
162
+ WHEN EXISTS (SELECT 1 FROM oh_semantic_purges WHERE authority_id = NEW.authority_id)
163
+ OR NOT EXISTS (SELECT 1 FROM oh_semantic_generations
164
+ WHERE authority_id = NEW.authority_id AND generation = NEW.generation
165
+ AND generation_sha256 = NEW.generation_sha256
166
+ AND isolation_sha256 = NEW.isolation_sha256)
167
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic authority or isolation is unavailable'); END;
168
+
169
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_purges_no_update
170
+ BEFORE UPDATE ON oh_semantic_purges
171
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic purge markers are immutable'); END;
172
+
173
+ CREATE TRIGGER IF NOT EXISTS oh_semantic_purges_no_delete
174
+ BEFORE DELETE ON oh_semantic_purges
175
+ BEGIN SELECT RAISE(ABORT, 'Oh semantic purge markers are immutable'); END;
@@ -0,0 +1,14 @@
1
+ {
2
+ "authorityId": "fixture:semantic-authority",
3
+ "authoritySha256": "d1eac8dd93ce6e04cb1316c670008250b98ec32cb0555f21d10b782ddffe03a7",
4
+ "content": "fixture body",
5
+ "generation": 7,
6
+ "generationSha256": "5d848fcd482ca72781af87da7b2ff3f68cd5b9ef571495bc734a5a2582c719d1",
7
+ "inputSha256": "b2309e89dd3f903934c7b5f4a4c8c51d5323d84a66928a2f017dd871492ef294",
8
+ "isolationSha256": "217df103dc50da7d3ba2883d9c1cbad01014b2b9eb20e4f94f737291c583d9a5",
9
+ "membershipSha256": "73ec3fcf8af17d99e2ae84878329c834e044bb47ff08ecd0dca6a596504b26b8",
10
+ "recordKey": "memory:fixture",
11
+ "recordSha256": "920d936e47e8f3cc5847017da5975ef73f8d84c04cb113880fb10575eeb614f9",
12
+ "title": "fixture title",
13
+ "v": 2
14
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "cacheSchema": "./libsql-semantic-cache-schema-v2.sql",
3
+ "cacheSchemaSha256": "679edfc3cb02dc768843976093f90d5879d53e84b797ac60d77956a634facae9",
4
+ "digestFixture": "./libsql-semantic-digest-fixture-v2.json",
5
+ "id": "oh.semantic-cloud.v2",
6
+ "legacyCacheSchema": "../v1/libsql-semantic-cache-schema-v1.sql",
7
+ "profile": "../v1/cloudflare-embedding-profile.json",
8
+ "renderer": "../v1/cloudflare-embedding-renderer.json",
9
+ "specification": "./semantic-cloud.md",
10
+ "v": 2
11
+ }
@@ -0,0 +1,106 @@
1
+ # Hosted semantic cache V2
2
+
3
+ Hosted semantic-cache V2 is the isolated successor to the immutable
4
+ [semantic-cache V1 contract](../v1/semantic-cloud.md). It keeps the same fixed
5
+ Cloudflare EmbeddingGemma profile and renderer, but changes only the rebuildable
6
+ libSQL cache protocol. It does not change an Oh graph, record, operation, local
7
+ embedding, or provider contract.
8
+
9
+ Applications use `bootstrapOhLibSqlSemanticCacheV2` and
10
+ `openOhLibSqlSemanticCacheV2`. The parallel
11
+ `bootstrapOhLibSqlSemanticCacheV1`, `openOhLibSqlSemanticCacheV1`, V1 types,
12
+ V1 result values, and V1 digest preimages remain the released V1 behavior. V1
13
+ and V2 use the same physical table names and therefore cannot be open in one
14
+ database simultaneously. A V1 database can be upgraded to V2; a V2 database
15
+ cannot be opened as V1 or downgraded.
16
+
17
+ The host MUST quiesce V1 stage, publish, head, and search traffic before
18
+ upgrade. An already-open V1 object is not a V2 runtime capability. Bootstrap
19
+ does, however, arbitrate a racing V1 purge: a purge committed before transition
20
+ custody is copied, while a purge that reaches the changed schema cannot report
21
+ success.
22
+
23
+ ## Isolation and identity
24
+
25
+ Every V2 authority reference and every stage, publish, head, search, receipt,
26
+ and purge operation binds an `isolationSha256`. It is a validated opaque
27
+ SHA-256 supplied by the host. Private hosts SHOULD derive it from their private
28
+ authority handle, cache epoch, and exact cache profile. When omitted, Oh derives:
29
+
30
+ ```json
31
+ {"authorityId":"<authority>","kind":"oh.semantic-authority-isolation.v2","v":2}
32
+ ```
33
+
34
+ and hashes the canonical JSON. One isolation belongs immutably to one authority,
35
+ while one authority may reserve multiple isolation epochs. The digest never
36
+ enters renderer or provider text.
37
+
38
+ Vector primary identity is
39
+ `(isolation_sha256, profile_sha256, renderer_sha256, input_sha256)`.
40
+ Generations, memberships, and published heads retain the isolation explicitly.
41
+ The membership digest hashes a canonical
42
+ `oh.semantic-membership.v2` envelope; the generation digest hashes an
43
+ `oh.semantic-generation.v2` envelope. Consequently identical rendered text in
44
+ different authorities or epochs is embedded and stored independently. Isolation
45
+ prevents reuse and deletion coupling; it is not encryption and a database holder
46
+ can still observe equal raw input digests or vector bytes.
47
+
48
+ All public V2 values use `v: 2`. Provider and renderer values remain V1 because
49
+ those fixed contracts did not change.
50
+
51
+ ## Published reads and purge custody
52
+
53
+ `publishedHead` returns `null` for an isolation mismatch. Search checks the
54
+ same isolation before sending a query to the provider and again while reading
55
+ memberships. Publish requires the staged generation's exact isolation.
56
+
57
+ `purgeAuthority` accepts the current published isolation, writes the permanent
58
+ authority tombstone, deletes every generation and membership for the authority,
59
+ and deletes every vector in every isolation reserved by it. A mismatched current
60
+ isolation is a conflict and authorizes no deletion. The receipt records the
61
+ first execution's counts and returns them unchanged on replay. It contains
62
+ `purgeMarkerSha256`, the canonical
63
+ `oh.semantic-purge-receipt.v2` `purgeReceiptSha256`, and zero
64
+ `residualGenerations`, `residualMemberships`, and
65
+ `residualScopedVectors`. `purgeReceipt` replays the same content-free proof
66
+ without issuing deletes.
67
+
68
+ ## V1-to-V2 transition
69
+
70
+ Bootstrap recognizes only the exact published V1 inventory or the exact V2
71
+ inventory. An upgrade first starts one atomic write transaction that:
72
+
73
+ 1. creates `oh_semantic_v1_purge_transition`;
74
+ 2. copies every V1 authority tombstone into it with one database-side
75
+ `INSERT ... SELECT`;
76
+ 3. only after that copy, drops the unsafe globally deduplicated V1 derived rows;
77
+ and
78
+ 4. creates the V2 schema without a V2 schema marker.
79
+
80
+ The copy has no lifetime tombstone cap and returns no unbounded row set to the
81
+ host. A concurrent V1 purge either commits before this transaction and is copied,
82
+ or runs afterward against a non-V1 schema and cannot report success. Concurrent
83
+ V2 bootstraps converge on the one transition table.
84
+
85
+ While the transition table exists, `openOhLibSqlSemanticCacheV2` fails closed.
86
+ Bootstrap reads transition rows in fixed pages. Each page atomically creates the
87
+ V2 isolation and receipt for every legacy tombstone and deletes a transition row
88
+ only when the complete computed V2 custody record exists. A crash before a page
89
+ commit changes nothing; a crash after commit can replay safely. Live V1 heads,
90
+ generations, memberships, and vectors are deliberately invalidated because their
91
+ global vector identity cannot be assigned a private owner after the fact.
92
+
93
+ Finalization inserts the exact V2 schema marker only when the transition table
94
+ is empty, then drops that table in the same transaction. Until that commit,
95
+ neither a partially projected database nor a V2 runtime is accepted. Legacy
96
+ receipts use `countsRecorded: false`; new V2 purges use
97
+ `countsRecorded: true`.
98
+
99
+ The exact standalone schemas are
100
+ [revision 1](../v1/libsql-semantic-cache-schema-v1.sql) and
101
+ [revision 2](libsql-semantic-cache-schema-v2.sql). Fixed
102
+ [V1](../v1/libsql-semantic-digest-fixture-v1.json) and
103
+ [V2](libsql-semantic-digest-fixture-v2.json) fixtures make the incompatible
104
+ digest domains explicit. Hosted failure remains a
105
+ missing derived retrieval lane and MUST NOT weaken exact graph or Datalog
106
+ operations.
package/src/cli.ts CHANGED
@@ -9,7 +9,7 @@ import { Oh } from "./sdk";
9
9
  import { OH_SQLITE_SCHEMA_VERSION } from "./sqlite/migrations";
10
10
  import { createOhSyncBundleV1, parseOhSyncBundleV1 } from "./sync";
11
11
 
12
- export const OH_PACKAGE_VERSION = "0.3.0" as const;
12
+ export const OH_PACKAGE_VERSION = "0.3.2" as const;
13
13
 
14
14
  type ParsedArguments = { options: Map<string, string[]>; positionals: string[] };
15
15
  type ValidatedInvocation = Readonly<{