@hraness/oh 0.2.3

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.
Files changed (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +598 -0
  3. package/dist/canonical.d.ts +32 -0
  4. package/dist/canonical.d.ts.map +1 -0
  5. package/dist/cli.d.ts +4 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +3419 -0
  8. package/dist/contract.d.ts +33 -0
  9. package/dist/contract.d.ts.map +1 -0
  10. package/dist/graph.d.ts +67 -0
  11. package/dist/graph.d.ts.map +1 -0
  12. package/dist/index.d.ts +9 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +1988 -0
  15. package/dist/libsql.d.ts +57 -0
  16. package/dist/libsql.d.ts.map +1 -0
  17. package/dist/libsql.js +2662 -0
  18. package/dist/memory.d.ts +366 -0
  19. package/dist/memory.d.ts.map +1 -0
  20. package/dist/memory.js +3650 -0
  21. package/dist/ontology.d.ts +242 -0
  22. package/dist/ontology.d.ts.map +1 -0
  23. package/dist/operation.d.ts +24 -0
  24. package/dist/operation.d.ts.map +1 -0
  25. package/dist/projection-public.d.ts +59 -0
  26. package/dist/projection-public.d.ts.map +1 -0
  27. package/dist/projection-public.js +1682 -0
  28. package/dist/projection-suss.d.ts +17 -0
  29. package/dist/projection-suss.d.ts.map +1 -0
  30. package/dist/projection-suss.js +1721 -0
  31. package/dist/projection.d.ts +315 -0
  32. package/dist/projection.d.ts.map +1 -0
  33. package/dist/schema.d.ts +45 -0
  34. package/dist/schema.d.ts.map +1 -0
  35. package/dist/sdk.d.ts +51 -0
  36. package/dist/sdk.d.ts.map +1 -0
  37. package/dist/sdk.js +3072 -0
  38. package/dist/search.d.ts +34 -0
  39. package/dist/search.d.ts.map +1 -0
  40. package/dist/semantic.d.ts +83 -0
  41. package/dist/semantic.d.ts.map +1 -0
  42. package/dist/semantic.js +706 -0
  43. package/dist/sqlite/driver.d.ts +6 -0
  44. package/dist/sqlite/driver.d.ts.map +1 -0
  45. package/dist/sqlite/index.d.ts +5 -0
  46. package/dist/sqlite/index.d.ts.map +1 -0
  47. package/dist/sqlite/index.js +2840 -0
  48. package/dist/sqlite/migrations.d.ts +10 -0
  49. package/dist/sqlite/migrations.d.ts.map +1 -0
  50. package/dist/sqlite/port.d.ts +40 -0
  51. package/dist/sqlite/port.d.ts.map +1 -0
  52. package/dist/sqlite/runtime.d.ts +23 -0
  53. package/dist/sqlite/runtime.d.ts.map +1 -0
  54. package/dist/sqlite/store.d.ts +85 -0
  55. package/dist/sqlite/store.d.ts.map +1 -0
  56. package/dist/store.d.ts +236 -0
  57. package/dist/store.d.ts.map +1 -0
  58. package/dist/store.js +996 -0
  59. package/dist/sync.d.ts +57 -0
  60. package/dist/sync.d.ts.map +1 -0
  61. package/dist/sync.js +1328 -0
  62. package/package.json +121 -0
  63. package/skills/oh/SKILL.md +206 -0
  64. package/skills/oh/agents/openai.yaml +4 -0
  65. package/spec/README.md +74 -0
  66. package/spec/manifest.json +41 -0
  67. package/spec/v1/canonical-json.md +59 -0
  68. package/spec/v1/contract.json +28 -0
  69. package/spec/v1/contract.schema.json +58 -0
  70. package/spec/v1/embedding-profile.json +11 -0
  71. package/spec/v1/embedding.md +56 -0
  72. package/spec/v1/graph.md +87 -0
  73. package/spec/v1/memory.md +193 -0
  74. package/spec/v1/migration.md +92 -0
  75. package/spec/v1/ontology.json +55 -0
  76. package/spec/v1/ontology.md +80 -0
  77. package/spec/v1/operation.schema.json +138 -0
  78. package/spec/v1/projection-identity.schema.json +58 -0
  79. package/spec/v1/projection-query.schema.json +60 -0
  80. package/spec/v1/projection-result.schema.json +452 -0
  81. package/spec/v1/projection-rule-pack.schema.json +182 -0
  82. package/spec/v1/projection.md +165 -0
  83. package/spec/v1/record.schema.json +95 -0
  84. package/spec/v1/schema-evolution.md +51 -0
  85. package/spec/v1/schema-revision.schema.json +178 -0
  86. package/spec/v1/storage.md +88 -0
  87. package/spec/v1/store.md +131 -0
  88. package/spec/v1/sync-bundle.schema.json +51 -0
  89. package/spec/v1/sync.md +67 -0
  90. package/src/canonical.test.ts +46 -0
  91. package/src/canonical.ts +203 -0
  92. package/src/cli.test.ts +103 -0
  93. package/src/cli.ts +308 -0
  94. package/src/contract.ts +87 -0
  95. package/src/contracts.test.ts +147 -0
  96. package/src/graph.ts +248 -0
  97. package/src/index.ts +8 -0
  98. package/src/libsql.test.ts +657 -0
  99. package/src/libsql.ts +1687 -0
  100. package/src/memory.test.ts +783 -0
  101. package/src/memory.ts +1684 -0
  102. package/src/ontology.ts +573 -0
  103. package/src/operation.ts +80 -0
  104. package/src/projection-public.ts +53 -0
  105. package/src/projection-suss.ts +129 -0
  106. package/src/projection.test.ts +418 -0
  107. package/src/projection.ts +1457 -0
  108. package/src/schema.ts +156 -0
  109. package/src/sdk.ts +96 -0
  110. package/src/search.ts +66 -0
  111. package/src/semantic.test.ts +480 -0
  112. package/src/semantic.ts +333 -0
  113. package/src/sqlite/driver.ts +47 -0
  114. package/src/sqlite/index.ts +4 -0
  115. package/src/sqlite/migrations.test.ts +44 -0
  116. package/src/sqlite/migrations.ts +178 -0
  117. package/src/sqlite/port.test.ts +127 -0
  118. package/src/sqlite/port.ts +120 -0
  119. package/src/sqlite/runtime.test.ts +68 -0
  120. package/src/sqlite/runtime.ts +53 -0
  121. package/src/sqlite/store.test.ts +295 -0
  122. package/src/sqlite/store.ts +988 -0
  123. package/src/store.test.ts +121 -0
  124. package/src/store.ts +701 -0
  125. package/src/sync.test.ts +117 -0
  126. package/src/sync.ts +227 -0
@@ -0,0 +1,182 @@
1
+ {
2
+ "$defs": {
3
+ "atom": {
4
+ "oneOf": [
5
+ {
6
+ "type": "boolean"
7
+ },
8
+ {
9
+ "type": "null"
10
+ },
11
+ {
12
+ "type": "number"
13
+ },
14
+ {
15
+ "maxLength": 16382,
16
+ "type": "string"
17
+ }
18
+ ]
19
+ },
20
+ "constantTerm": {
21
+ "additionalProperties": false,
22
+ "properties": {
23
+ "kind": {
24
+ "const": "constant"
25
+ },
26
+ "v": {
27
+ "const": 1
28
+ },
29
+ "value": {
30
+ "$ref": "#/$defs/atom"
31
+ }
32
+ },
33
+ "required": [
34
+ "kind",
35
+ "v",
36
+ "value"
37
+ ],
38
+ "type": "object"
39
+ },
40
+ "literal": {
41
+ "additionalProperties": false,
42
+ "properties": {
43
+ "relation": {
44
+ "$ref": "#/$defs/safeCode"
45
+ },
46
+ "terms": {
47
+ "items": {
48
+ "$ref": "#/$defs/term"
49
+ },
50
+ "maxItems": 32,
51
+ "minItems": 1,
52
+ "type": "array"
53
+ },
54
+ "v": {
55
+ "const": 1
56
+ }
57
+ },
58
+ "required": [
59
+ "relation",
60
+ "terms",
61
+ "v"
62
+ ],
63
+ "type": "object"
64
+ },
65
+ "rule": {
66
+ "additionalProperties": false,
67
+ "properties": {
68
+ "body": {
69
+ "items": {
70
+ "$ref": "#/$defs/literal"
71
+ },
72
+ "maxItems": 64,
73
+ "minItems": 1,
74
+ "type": "array"
75
+ },
76
+ "head": {
77
+ "$ref": "#/$defs/literal"
78
+ },
79
+ "ruleId": {
80
+ "$ref": "#/$defs/safeCode"
81
+ },
82
+ "ruleSha256": {
83
+ "$ref": "#/$defs/sha256"
84
+ },
85
+ "v": {
86
+ "const": 1
87
+ }
88
+ },
89
+ "required": [
90
+ "body",
91
+ "head",
92
+ "ruleId",
93
+ "ruleSha256",
94
+ "v"
95
+ ],
96
+ "type": "object"
97
+ },
98
+ "safeCode": {
99
+ "maxLength": 128,
100
+ "pattern": "^[a-z][a-z0-9]*(?:[._:/-][a-z0-9]+)*$",
101
+ "type": "string"
102
+ },
103
+ "sha256": {
104
+ "pattern": "^[a-f0-9]{64}$",
105
+ "type": "string"
106
+ },
107
+ "term": {
108
+ "oneOf": [
109
+ {
110
+ "$ref": "#/$defs/constantTerm"
111
+ },
112
+ {
113
+ "$ref": "#/$defs/variableTerm"
114
+ }
115
+ ]
116
+ },
117
+ "variableTerm": {
118
+ "additionalProperties": false,
119
+ "properties": {
120
+ "kind": {
121
+ "const": "variable"
122
+ },
123
+ "name": {
124
+ "$ref": "#/$defs/safeCode"
125
+ },
126
+ "v": {
127
+ "const": 1
128
+ }
129
+ },
130
+ "required": [
131
+ "kind",
132
+ "name",
133
+ "v"
134
+ ],
135
+ "type": "object"
136
+ }
137
+ },
138
+ "$id": "https://oh.computer/spec/v1/projection-rule-pack.schema.json",
139
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
140
+ "$comment": "Runtime conformance additionally measures each atom's exact 16 KiB canonical-JSON byte ceiling, requires safe head variables, canonical rule ordering, unique rule IDs, and recomputed rule and pack digests.",
141
+ "additionalProperties": false,
142
+ "properties": {
143
+ "rulePackId": {
144
+ "$ref": "#/$defs/safeCode"
145
+ },
146
+ "rulePackRevision": {
147
+ "minimum": 1,
148
+ "type": "integer"
149
+ },
150
+ "rulePackSha256": {
151
+ "$ref": "#/$defs/sha256"
152
+ },
153
+ "rules": {
154
+ "items": {
155
+ "$ref": "#/$defs/rule"
156
+ },
157
+ "maxItems": 1024,
158
+ "minItems": 1,
159
+ "type": "array"
160
+ },
161
+ "rulesSha256": {
162
+ "$ref": "#/$defs/sha256"
163
+ },
164
+ "semantics": {
165
+ "const": "oh.projection.positive-datalog.v1"
166
+ },
167
+ "v": {
168
+ "const": 1
169
+ }
170
+ },
171
+ "required": [
172
+ "rulePackId",
173
+ "rulePackRevision",
174
+ "rulePackSha256",
175
+ "rules",
176
+ "rulesSha256",
177
+ "semantics",
178
+ "v"
179
+ ],
180
+ "title": "Oh projection rule pack V1",
181
+ "type": "object"
182
+ }
@@ -0,0 +1,165 @@
1
+ # Derived projections
2
+
3
+ Oh projections are reproducible, non-authoritative views over one exact graph
4
+ snapshot. The runtime exposes the same typed rule, query, identity, and result
5
+ contract through `@hraness/oh/projection` without changing the V1 graph record,
6
+ operation, canonical JSON, or SQLite formats.
7
+
8
+ ## Authority boundary
9
+
10
+ A projection result has `authority: "derived"`. Its tuples and proof trees are
11
+ cache output. They MUST NOT be interpreted as graph assertions, review
12
+ decisions, accepted knowledge, or operation history. An application that wants
13
+ to retain a conclusion MUST create and review new graph records through its
14
+ ordinary authority path.
15
+
16
+ The projection module imports no SQLite runtime. A caller supplies an exact
17
+ snapshot assembled from the authority it selected. This keeps the same contract
18
+ usable in a Node 24 serverless process, a local agent, or an application-owned
19
+ remote-store adapter.
20
+
21
+ ## Exact input identity
22
+
23
+ `createOhProjectionSnapshotV1` accepts a space ID, current head, and complete
24
+ record snapshot. It:
25
+
26
+ 1. parses every record under the unchanged V1 graph contract;
27
+ 2. sorts record references by logical key;
28
+ 3. recomputes `recordsSha256` and requires it to equal the head;
29
+ 4. checks dependency closure; and
30
+ 5. hashes the contract digest, space, head, and record references into
31
+ `snapshotSha256`.
32
+
33
+ A fact is one relation tuple plus one or more exact source record keys and
34
+ digests. A dataset rejects a source that is not current at its snapshot.
35
+ Duplicate relation tuples are coalesced and retain the union of their source
36
+ records. The dataset identity binds:
37
+
38
+ - the snapshot digest;
39
+ - a fact-pack ID and revision;
40
+ - the SHA-256 digest of the application-owned extractor implementation; and
41
+ - the canonical ordered fact set.
42
+
43
+ Oh supplies structural `oh.record(key, kind, digest)` and
44
+ `oh.dependency(key, dependency)` facts. Domain packs may emit richer relations,
45
+ but their extractor digest and every source record remain explicit. The
46
+ package-owned structural extractor profile is published as
47
+ `OH_PROJECTION_RECORD_FACT_EXTRACTOR_V1`.
48
+
49
+ ## Positive rule semantics
50
+
51
+ A term is a variable or a JSON primitive constant. A literal names one relation
52
+ and an ordered term list. A rule has one head and a nonempty conjunction of
53
+ positive body literals. Every head variable MUST occur in its body. Relation
54
+ arity MUST be consistent across base facts, rule heads, rule bodies, and the
55
+ query.
56
+
57
+ The V1 projection evaluator implements finite, positive Datalog with set
58
+ semantics and synchronous naïve fixpoint rounds. It supports recursive rules.
59
+ It does not support negation, aggregation, arithmetic, function symbols, or
60
+ callbacks inside rules. Unknown objects are parsed with exact keys, so an
61
+ unsupported operator fails rather than being ignored.
62
+
63
+ Rule packs are sorted by rule ID and content-addressed. A query declares an
64
+ ordered `find` variable list, a nonempty positive body, and an output limit.
65
+ Query results use set semantics and sort tuples by canonical JSON. Declaration,
66
+ fact, and insertion order do not affect rule-pack identity or output bytes.
67
+ The projection identity also binds the selected engine and resolved evaluation
68
+ limits, so results created with different engines, proof budgets, or work
69
+ budgets cannot share a cache identity.
70
+
71
+ ## Evaluation limits
72
+
73
+ The implementation checks hard ceilings before or during work:
74
+
75
+ | Item | Maximum |
76
+ | --- | ---: |
77
+ | Tuple arity | 32 |
78
+ | Canonical bytes per atom | 16 KiB |
79
+ | Base facts | 262,144 |
80
+ | Derived tuples | 262,144 |
81
+ | Rules | 1,024 |
82
+ | Body literals per rule | 64 |
83
+ | Evaluation rounds | 1,024 |
84
+ | Join matches per rule or query body | 262,144 |
85
+ | Tuple-unification work units per evaluation | 16,777,216 |
86
+ | Returned query rows | 65,536 |
87
+ | Proof depth | 128 |
88
+ | Proof nodes per row | 4,096 |
89
+ | Proof nodes across returned rows | 65,536 |
90
+ | Canonical result bytes | 16 MiB |
91
+
92
+ A caller may request smaller derived-tuple, round, proof-depth, proof-node, and
93
+ global tuple-unification work bounds. The global work counter spans every rule,
94
+ round, and the final query, including unsuccessful candidate matches. Exceeding
95
+ a work bound fails closed. Result construction additionally stops before the
96
+ aggregate proof-node or canonical-byte ceilings. A query's declared output
97
+ limit or the result-byte ceiling returns a canonical prefix, sets
98
+ `stats.truncated: true`, and lists `query-limit` or `result-bytes` in
99
+ `stats.truncationReasons`.
100
+
101
+ ## Proofs
102
+
103
+ Each returned row carries one proof for each literal in one canonical supporting
104
+ query-body match. `supportCount` reports how many complete matches produced the
105
+ same projected value tuple; V1 deliberately does not serialize every alternate
106
+ witness. A fact leaf names its relation, tuple, and exact source record
107
+ references. A derived node names the rule ID and digest and recursively contains
108
+ its premises. Depth and cycle guards emit an explicit `truncated` node. If a
109
+ node or byte budget ends between sibling premises, the enclosing derived node
110
+ sets `premisesTruncated: true`; if it ends between query-body proofs, the row
111
+ sets `proofsTruncated: true`.
112
+ `stats.proofsTruncated` reports either form across all returned rows. A proof
113
+ establishes how the bounded evaluator derived a tuple from the supplied bytes;
114
+ it does not establish that a proposition is true.
115
+
116
+ ## Safe cached ingress
117
+
118
+ Projection declarations and cache output are untrusted exchange data. The
119
+ `parseOhProjectionRulePackV1`, `parseOhProjectionQueryV1`, and
120
+ `parseOhProjectionIdentityV1` parsers reject unknown keys and invalid digest
121
+ preimages. `parseOhProjectionProofV1` additionally applies the public proof
122
+ depth, node, tuple, source, atom-byte, and aggregate-byte ceilings before
123
+ returning a proof tree.
124
+
125
+ `parseOhProjectionResultV1` is the cache-ingress boundary. It verifies the
126
+ result digest; canonical row and source order; `supportCount`; proof-node,
127
+ work-unit, relation, match, round, and byte totals; every proof and result
128
+ truncation marker; and all declared evaluation ceilings. It also recomputes the
129
+ engine and evaluation digests and requires them to match the projection
130
+ identity. A valid SHA-256 string by itself is not enough to make an envelope
131
+ acceptable. Cache readers SHOULD pass the projection digest they requested as
132
+ the parser's second argument; an internally consistent envelope does not prove
133
+ that a cache returned the requested identity.
134
+
135
+ The discovery manifest publishes machine-readable schemas for rule packs,
136
+ queries, identities, and result envelopes. JSON Schema describes the exchange
137
+ shape and static maxima. The runtime parsers remain normative for canonical
138
+ ordering, digest preimages, aggregate budgets, and cross-field consistency that
139
+ the schemas cannot express.
140
+
141
+ ## Cache invalidation
142
+
143
+ `projectionSha256` binds the current contract, snapshot, dataset, rule pack,
144
+ query, engine, resolved evaluation limits, and positive-Datalog semantics. A cached
145
+ result is reusable only when that digest is unchanged. Any snapshot, dataset,
146
+ rule-pack, query, engine, or evaluation-limit change has `kind: "full-rebuild"` and
147
+ lists the changed identities. V1 does not claim incremental deletion or
148
+ cross-snapshot maintenance.
149
+
150
+ ## Optional Suss equivalence lane
151
+
152
+ `@hraness/oh/experimental/projection-suss` supports exactly
153
+ `@suss/datalog@0.20.0` as an optional peer. It encodes every JSON primitive atom
154
+ into canonical JSON text, evaluates the positive rules with Suss, and compares
155
+ every complete relation to the Oh reference semantics. It returns only after
156
+ exact set agreement.
157
+
158
+ Suss's public evaluator does not expose an execution-budget hook. Before calling
159
+ it, the adapter first runs the bounded reference evaluator, then computes a
160
+ conservative finite-domain upper bound on new tuples in rule-head relations and
161
+ refuses a program it cannot prove will remain under the requested derived-tuple
162
+ ceiling. It compares Suss's complete result to the reference materialization and
163
+ uses the reference witnesses for canonical proof construction. This lane
164
+ evaluates compatibility, not performance. Refusal does not disable the built-in
165
+ evaluator.
@@ -0,0 +1,95 @@
1
+ {
2
+ "$defs": {
3
+ "jsonValue": {
4
+ "oneOf": [
5
+ {
6
+ "type": [
7
+ "null",
8
+ "boolean",
9
+ "number",
10
+ "string"
11
+ ]
12
+ },
13
+ {
14
+ "items": {
15
+ "$ref": "#/$defs/jsonValue"
16
+ },
17
+ "type": "array"
18
+ },
19
+ {
20
+ "additionalProperties": {
21
+ "$ref": "#/$defs/jsonValue"
22
+ },
23
+ "type": "object"
24
+ }
25
+ ]
26
+ },
27
+ "recordKey": {
28
+ "maxLength": 512,
29
+ "pattern": "^[a-z][a-z0-9]*(?:[._:/-][a-z0-9]+)*$",
30
+ "type": "string"
31
+ },
32
+ "sha256": {
33
+ "pattern": "^[a-f0-9]{64}$",
34
+ "type": "string"
35
+ }
36
+ },
37
+ "$id": "https://oh.computer/spec/v1/record.schema.json",
38
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
39
+ "$comment": "Runtime conformance additionally requires canonical UTF-8 JSON, strictly ordered unique dependencies, a non-reflexive dependency set, the 1 MiB value bound, and a recomputed recordSha256.",
40
+ "additionalProperties": false,
41
+ "properties": {
42
+ "dependencies": {
43
+ "items": {
44
+ "$ref": "#/$defs/recordKey"
45
+ },
46
+ "maxItems": 4096,
47
+ "type": "array",
48
+ "uniqueItems": true
49
+ },
50
+ "key": {
51
+ "$ref": "#/$defs/recordKey"
52
+ },
53
+ "kind": {
54
+ "enum": [
55
+ "activity",
56
+ "assertion",
57
+ "context",
58
+ "dependency-manifest",
59
+ "edition",
60
+ "entity",
61
+ "evidence",
62
+ "identity-operation",
63
+ "inquiry",
64
+ "inquiry-event",
65
+ "review-decision",
66
+ "rights-decision",
67
+ "schema",
68
+ "shape",
69
+ "statement",
70
+ "type-membership",
71
+ "view",
72
+ "vocabulary"
73
+ ]
74
+ },
75
+ "recordSha256": {
76
+ "$ref": "#/$defs/sha256"
77
+ },
78
+ "v": {
79
+ "const": 1
80
+ },
81
+ "value": {
82
+ "$ref": "#/$defs/jsonValue"
83
+ }
84
+ },
85
+ "required": [
86
+ "dependencies",
87
+ "key",
88
+ "kind",
89
+ "recordSha256",
90
+ "v",
91
+ "value"
92
+ ],
93
+ "title": "Oh knowledge graph record V1",
94
+ "type": "object"
95
+ }
@@ -0,0 +1,51 @@
1
+ # Schema evolution V1
2
+
3
+ Oh stores product meaning as content-addressed schema and vocabulary records.
4
+ The envelope remains generic while each namespace owns the interpretation of
5
+ its concepts, predicates, shapes, mappings, units, and vocabularies.
6
+
7
+ ## Schema identity
8
+
9
+ A schema revision has a stable `namespace`, `code`, and `kind`. V1 kinds are
10
+ `concept`, `mapping`, `predicate`, `shape`, `unit`, and `vocabulary`. A positive
11
+ revision and `schemaSha256` identify one immutable body.
12
+
13
+ The revision also includes:
14
+
15
+ - `body`, a canonical JSON object owned by the schema;
16
+ - nonempty, canonically ordered localized `labels` and `description` entries;
17
+ - `compatibility`, either `additive` or `breaking`; and
18
+ - `previousSchemaSha256`, which is null only for revision one.
19
+
20
+ Revision one MUST declare `additive`. Later revisions MUST advance by exactly
21
+ one and bind the immediately prior digest.
22
+
23
+ ## Additive claims
24
+
25
+ An additive revision MUST retain every top-level key from the prior body with
26
+ the exact same canonical JSON value. It may add new top-level keys. Changing or
27
+ removing a prior value requires `breaking`.
28
+
29
+ This law is deliberately mechanical. It prevents a schema author from labeling
30
+ an incompatible byte change as additive. A domain may impose stricter semantic
31
+ compatibility rules in its codec.
32
+
33
+ ## Vocabulary revisions
34
+
35
+ A vocabulary revision binds one namespace, positive revision, and no more than
36
+ 65,536 schema references from that same namespace. References MUST be in
37
+ strict canonical order and unique. `vocabularySha256` hashes the payload
38
+ without the digest field.
39
+
40
+ ## Evolution procedure
41
+
42
+ 1. Parse and verify the prior immutable revision.
43
+ 2. Create the next revision with the prior digest and the correct compatibility
44
+ classification.
45
+ 3. Run the mechanical evolution check.
46
+ 4. Publish the new schema record without rewriting the prior record.
47
+ 5. Update dependent graph records explicitly when they adopt the new schema.
48
+
49
+ Schema revision is separate from Oh's envelope version. A breaking product
50
+ schema does not automatically require a new graph format, while a changed
51
+ graph field always does.
@@ -0,0 +1,178 @@
1
+ {
2
+ "$defs": {
3
+ "jsonValue": {
4
+ "oneOf": [
5
+ {
6
+ "type": [
7
+ "null",
8
+ "boolean",
9
+ "number",
10
+ "string"
11
+ ]
12
+ },
13
+ {
14
+ "items": {
15
+ "$ref": "#/$defs/jsonValue"
16
+ },
17
+ "type": "array"
18
+ },
19
+ {
20
+ "additionalProperties": {
21
+ "$ref": "#/$defs/jsonValue"
22
+ },
23
+ "type": "object"
24
+ }
25
+ ]
26
+ },
27
+ "localizedText": {
28
+ "additionalProperties": false,
29
+ "properties": {
30
+ "language": {
31
+ "maxLength": 64,
32
+ "pattern": "^(?:und|[a-z]{2,3}(?:-[a-z0-9]{2,8})*)$",
33
+ "type": "string"
34
+ },
35
+ "text": {
36
+ "minLength": 1,
37
+ "type": "string"
38
+ },
39
+ "v": {
40
+ "const": 1
41
+ }
42
+ },
43
+ "required": [
44
+ "language",
45
+ "text",
46
+ "v"
47
+ ],
48
+ "type": "object"
49
+ },
50
+ "safeCode": {
51
+ "maxLength": 128,
52
+ "pattern": "^[a-z][a-z0-9]*(?:[._:/-][a-z0-9]+)*$",
53
+ "type": "string"
54
+ },
55
+ "sha256": {
56
+ "pattern": "^[a-f0-9]{64}$",
57
+ "type": "string"
58
+ }
59
+ },
60
+ "$id": "https://oh.computer/spec/v1/schema-revision.schema.json",
61
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
62
+ "$comment": "Runtime conformance additionally requires NFC bounded text, canonical ordering, unique localized values, canonical body JSON, and a recomputed schemaSha256.",
63
+ "additionalProperties": false,
64
+ "allOf": [
65
+ {
66
+ "else": {
67
+ "properties": {
68
+ "previousSchemaSha256": {
69
+ "$ref": "#/$defs/sha256"
70
+ }
71
+ }
72
+ },
73
+ "if": {
74
+ "properties": {
75
+ "revision": {
76
+ "const": 1
77
+ }
78
+ },
79
+ "required": [
80
+ "revision"
81
+ ]
82
+ },
83
+ "then": {
84
+ "properties": {
85
+ "compatibility": {
86
+ "const": "additive"
87
+ },
88
+ "previousSchemaSha256": {
89
+ "const": null
90
+ }
91
+ }
92
+ }
93
+ }
94
+ ],
95
+ "properties": {
96
+ "body": {
97
+ "additionalProperties": {
98
+ "$ref": "#/$defs/jsonValue"
99
+ },
100
+ "type": "object"
101
+ },
102
+ "code": {
103
+ "$ref": "#/$defs/safeCode"
104
+ },
105
+ "compatibility": {
106
+ "enum": [
107
+ "additive",
108
+ "breaking"
109
+ ]
110
+ },
111
+ "description": {
112
+ "items": {
113
+ "$ref": "#/$defs/localizedText"
114
+ },
115
+ "maxItems": 128,
116
+ "minItems": 1,
117
+ "type": "array",
118
+ "uniqueItems": true
119
+ },
120
+ "kind": {
121
+ "enum": [
122
+ "concept",
123
+ "mapping",
124
+ "predicate",
125
+ "shape",
126
+ "unit",
127
+ "vocabulary"
128
+ ]
129
+ },
130
+ "labels": {
131
+ "items": {
132
+ "$ref": "#/$defs/localizedText"
133
+ },
134
+ "maxItems": 128,
135
+ "minItems": 1,
136
+ "type": "array",
137
+ "uniqueItems": true
138
+ },
139
+ "namespace": {
140
+ "$ref": "#/$defs/safeCode"
141
+ },
142
+ "previousSchemaSha256": {
143
+ "anyOf": [
144
+ {
145
+ "type": "null"
146
+ },
147
+ {
148
+ "$ref": "#/$defs/sha256"
149
+ }
150
+ ]
151
+ },
152
+ "revision": {
153
+ "minimum": 1,
154
+ "type": "integer"
155
+ },
156
+ "schemaSha256": {
157
+ "$ref": "#/$defs/sha256"
158
+ },
159
+ "v": {
160
+ "const": 1
161
+ }
162
+ },
163
+ "required": [
164
+ "body",
165
+ "code",
166
+ "compatibility",
167
+ "description",
168
+ "kind",
169
+ "labels",
170
+ "namespace",
171
+ "previousSchemaSha256",
172
+ "revision",
173
+ "schemaSha256",
174
+ "v"
175
+ ],
176
+ "title": "Oh knowledge schema revision V1",
177
+ "type": "object"
178
+ }