@gregiteen/ssss-cli 0.14.1 → 0.14.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/CHANGELOG.md CHANGED
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.14.2] - 2026-11-02
11
+ ### Added
12
+ - **Canonical JSON Schema Draft 2020-12 Definitions (`JSON_SCHEMA_EXPORT`)**:
13
+ - `schemas/core-registry.schema.json`: Formal JSON Schema Draft 2020-12 defining the full structure of `registry/core.json` and extension registries (document primitives, contract primitives, universal frontmatter, portability classes and export profiles, bundle manifest specifications, provisioning steps and relations, runtime contracts, and semantic search fields).
14
+ - `schemas/bundle.schema.json`: Formal JSON Schema Draft 2020-12 defining the container format for Universal Cloud Workspace (`.ucw`) transport bundles, covering `manifest`, `files`, `dependencies`, `parameters`, `provisioning`, `provenance`, and optional `branding`.
15
+ - `registry/core.json`: Updated `$schema` URI to point to `https://ssss.dev/schemas/core-registry.schema.json`.
16
+ - **Zero-Dependency Schema Validation**:
17
+ - `src/schema-validator.mjs`: High-performance, recursive JSON Schema Draft 2020-12 validator in pure Node ESM supporting `type` unions, `properties`, `additionalProperties`, `required`, `items`, `enum`, `pattern`, and local `$defs`/`$ref` pointers.
18
+ - Exported `./schema-validator` and `./schemas/*` via package exports and added `"schemas"` to npm package distribution files.
19
+ - **CLI Schema Inspection & Export**:
20
+ - `scripts/cmd-schema.mjs`: Added `ssss schema core`, `ssss schema bundle`, and `ssss schema export [--out <dir>]` subcommands to output and export canonical JSON Schemas.
21
+ - **Automated Conformance Sweeps**:
22
+ - `scripts/conformance-09.mjs`: Integrated automated schema validation checks into continuous test suites, verifying `registry/core.json` and `conformance/reference-bundle.ucw.json` against canonical schemas with negative mutation testing.
23
+ - **Specification and Backlog Hygiene**:
24
+ - `docs/ssss-spec.md`: Documented canonical JSON Schema references in §5.1 and §16.1.
25
+ - Resolved and closed all outstanding host deferral items in `docs/projects/DEFERRED_BACKLOG.md`.
26
+
27
+ ### Behavior changes hosts should check
28
+ - Tools and IDEs may validate `registry/core.json` and `.ucw` bundle files directly against `schemas/core-registry.schema.json` and `schemas/bundle.schema.json`.
29
+ - The CLI command `ssss schema export` is now available to export canonical schema definitions for external consumption.
30
+
10
31
  ## [0.14.1] - 2026-11-02
11
32
  ### Added
12
33
  - **Core primitive aliasing (`role` ↔ `security_role`, `thread` ↔ `conversation`)**:
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.14.1
1
+ 0.14.2
@@ -106,7 +106,7 @@
106
106
  "file_count": 14,
107
107
  "provenance": {
108
108
  "content_hash": "sha256:7febd673938a3dc04c068e0bf08097a9783c09e612606d2df9a4db8dcb8907f5",
109
- "exporter": "@gregiteen/ssss-cli@0.14.1"
109
+ "exporter": "@gregiteen/ssss-cli@0.14.2"
110
110
  }
111
111
  },
112
112
  "files": [
package/docs/ssss-spec.md CHANGED
@@ -261,8 +261,9 @@ primitive — it implements the subset its product requires — but any primitiv
261
261
  | `primitive` | meta | structural | A governed, versioned definition for a namespaced SSSS primitive. |
262
262
 
263
263
  The **Portability** column is the primitive's default portability class (§5.5); a host
264
- resolves it from `registry/core.json`. Extension registries assign their own primitives a
265
- class — notably `resource_bound` for things like domains and phone numbers.
264
+ resolves it from `registry/core.json`. The canonical machine-readable registry conforms to the
265
+ JSON Schema (Draft 2020-12) published in `schemas/core-registry.schema.json`. Extension registries
266
+ assign their own primitives a class — notably `resource_bound` for things like domains and phone numbers.
266
267
 
267
268
  ### 5.2 Contract Primitives
268
269
 
@@ -1869,7 +1870,9 @@ A bundle is a JSON object with three top-level members:
1869
1870
  ```
1870
1871
 
1871
1872
  `files` MUST be sorted by `path` (§16.3 order) so that two exports of the same vault state
1872
- are identical and content-hashable (§16.3, `provenance.content_hash`).
1873
+ are identical and content-hashable (§16.3, `provenance.content_hash`). The normative JSON Schema
1874
+ (Draft 2020-12) for `.ucw` bundle containers is published in `schemas/bundle.schema.json` and
1875
+ exportable via the CLI command `ssss schema bundle`.
1873
1876
 
1874
1877
  ### 16.2 The Manifest
1875
1878
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gregiteen/ssss-cli",
3
- "version": "0.14.1",
3
+ "version": "0.14.2",
4
4
  "description": "CLI tools and the reference engine for the Structured Semantic Syntax System",
5
5
  "main": "src/engine.mjs",
6
6
  "exports": {
@@ -32,6 +32,8 @@
32
32
  "./conformance/fixtures.json": "./conformance/fixtures.json",
33
33
  "./conformance/reference-bundle.ucw.json": "./conformance/reference-bundle.ucw.json",
34
34
  "./registry/core.json": "./registry/core.json",
35
+ "./schema-validator": "./src/schema-validator.mjs",
36
+ "./schemas/*": "./schemas/*",
35
37
  "./package.json": "./package.json"
36
38
  },
37
39
  "bin": {
@@ -43,6 +45,7 @@
43
45
  },
44
46
  "files": [
45
47
  "src",
48
+ "schemas",
46
49
  "types",
47
50
  "scripts",
48
51
  "registry",
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "https://ssss.dev/registry/v1",
2
+ "$schema": "https://ssss.dev/schemas/core-registry.schema.json",
3
3
  "registry": "core",
4
4
  "spec_version": "0.9",
5
5
  "description": "The canonical SSSS core registry and semantic application-kernel contract. Core primitives are qualified as ssss:<name>; applications define namespaced primitives through layered extension registries without copying or redefining core behavior.",
@@ -0,0 +1,259 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://ssss.dev/schemas/bundle.schema.json",
4
+ "title": "SSSS Universal Cloud Workspace (.ucw) Bundle Schema",
5
+ "description": "JSON Schema for SSSS transport bundles and manifests (spec §16).",
6
+ "type": "object",
7
+ "required": [
8
+ "manifest",
9
+ "files"
10
+ ],
11
+ "properties": {
12
+ "manifest": {
13
+ "$ref": "#/$defs/bundleManifest"
14
+ },
15
+ "files": {
16
+ "type": "array",
17
+ "items": {
18
+ "$ref": "#/$defs/bundleFile"
19
+ }
20
+ },
21
+ "branding": {
22
+ "type": "object"
23
+ }
24
+ },
25
+ "$defs": {
26
+ "bundleFile": {
27
+ "type": "object",
28
+ "required": [
29
+ "path",
30
+ "content"
31
+ ],
32
+ "properties": {
33
+ "path": {
34
+ "type": "string"
35
+ },
36
+ "content": {
37
+ "type": "string"
38
+ }
39
+ }
40
+ },
41
+ "bundleManifest": {
42
+ "type": "object",
43
+ "required": [
44
+ "name",
45
+ "description",
46
+ "version",
47
+ "exported_at",
48
+ "ssss_core_version",
49
+ "required_extensions",
50
+ "export_profile",
51
+ "primitive_inventory",
52
+ "provisioning",
53
+ "file_count",
54
+ "provenance"
55
+ ],
56
+ "properties": {
57
+ "name": {
58
+ "type": "string"
59
+ },
60
+ "description": {
61
+ "type": "string"
62
+ },
63
+ "version": {
64
+ "type": "string"
65
+ },
66
+ "exported_at": {
67
+ "type": "string"
68
+ },
69
+ "ssss_core_version": {
70
+ "type": "string"
71
+ },
72
+ "required_extensions": {
73
+ "type": "array",
74
+ "items": {
75
+ "type": "string"
76
+ }
77
+ },
78
+ "export_profile": {
79
+ "type": "string",
80
+ "enum": ["backup", "template", "sale"]
81
+ },
82
+ "primitive_inventory": {
83
+ "type": "object",
84
+ "additionalProperties": {
85
+ "type": "integer"
86
+ }
87
+ },
88
+ "dependencies": {
89
+ "type": "object",
90
+ "properties": {
91
+ "primitives": {
92
+ "type": "array",
93
+ "items": { "type": "string" }
94
+ },
95
+ "extensions": {
96
+ "type": "array",
97
+ "items": { "type": "string" }
98
+ },
99
+ "migrations": {
100
+ "type": "array",
101
+ "items": { "type": "string" }
102
+ },
103
+ "integrity": {
104
+ "type": "object",
105
+ "properties": {
106
+ "content_hash": { "type": "string" },
107
+ "ssss_core_version": { "type": "string" }
108
+ }
109
+ },
110
+ "bundles": {
111
+ "type": "array",
112
+ "items": {
113
+ "type": "object",
114
+ "properties": {
115
+ "id": { "type": "string" },
116
+ "name": { "type": "string" },
117
+ "version": { "type": "string" },
118
+ "installMode": {
119
+ "type": "string",
120
+ "enum": ["optional", "recommended", "required"]
121
+ }
122
+ }
123
+ }
124
+ }
125
+ }
126
+ },
127
+ "provisioning": {
128
+ "type": "array",
129
+ "items": {
130
+ "$ref": "#/$defs/provisioningStep"
131
+ }
132
+ },
133
+ "parameters": {
134
+ "type": "array",
135
+ "items": {
136
+ "$ref": "#/$defs/provisioningParameter"
137
+ }
138
+ },
139
+ "file_count": {
140
+ "type": "integer"
141
+ },
142
+ "provenance": {
143
+ "type": "object",
144
+ "required": [
145
+ "content_hash",
146
+ "exporter"
147
+ ],
148
+ "properties": {
149
+ "content_hash": {
150
+ "type": "string"
151
+ },
152
+ "exporter": {
153
+ "type": "string"
154
+ },
155
+ "signature": {
156
+ "type": "string"
157
+ }
158
+ }
159
+ },
160
+ "source_workspace_id": {
161
+ "type": "string"
162
+ },
163
+ "branding": {
164
+ "type": "object"
165
+ }
166
+ }
167
+ },
168
+ "provisioningStep": {
169
+ "type": "object",
170
+ "required": [
171
+ "id",
172
+ "label",
173
+ "system",
174
+ "mode",
175
+ "required"
176
+ ],
177
+ "properties": {
178
+ "id": {
179
+ "type": "string"
180
+ },
181
+ "label": {
182
+ "type": "string"
183
+ },
184
+ "system": {
185
+ "type": "string",
186
+ "enum": [
187
+ "workspace",
188
+ "branding",
189
+ "email",
190
+ "phone",
191
+ "domains",
192
+ "accounts",
193
+ "deployments",
194
+ "marketplace",
195
+ "tabs",
196
+ "automation"
197
+ ]
198
+ },
199
+ "mode": {
200
+ "type": "string",
201
+ "enum": [
202
+ "existing",
203
+ "provision",
204
+ "install",
205
+ "generate",
206
+ "configure"
207
+ ]
208
+ },
209
+ "required": {
210
+ "type": "boolean"
211
+ },
212
+ "notes": {
213
+ "type": "string"
214
+ }
215
+ }
216
+ },
217
+ "provisioningParameter": {
218
+ "type": "object",
219
+ "required": [
220
+ "key",
221
+ "label",
222
+ "type",
223
+ "scope",
224
+ "source"
225
+ ],
226
+ "properties": {
227
+ "key": {
228
+ "type": "string"
229
+ },
230
+ "label": {
231
+ "type": "string"
232
+ },
233
+ "type": {
234
+ "type": "string"
235
+ },
236
+ "scope": {
237
+ "type": "string"
238
+ },
239
+ "source": {
240
+ "type": "string",
241
+ "enum": ["user", "llm", "graph", "system"]
242
+ },
243
+ "required": {
244
+ "type": "boolean"
245
+ },
246
+ "description": {
247
+ "type": "string"
248
+ },
249
+ "defaultValue": {},
250
+ "options": {
251
+ "type": "array"
252
+ },
253
+ "dependsOn": {
254
+ "type": "string"
255
+ }
256
+ }
257
+ }
258
+ }
259
+ }
@@ -0,0 +1,386 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://ssss.dev/schemas/core-registry.schema.json",
4
+ "title": "SSSS Core Registry Schema",
5
+ "description": "JSON Schema for SSSS core and extension registries (spec §5).",
6
+ "type": "object",
7
+ "required": [
8
+ "registry",
9
+ "spec_version",
10
+ "description",
11
+ "document_primitives"
12
+ ],
13
+ "properties": {
14
+ "$schema": {
15
+ "type": "string"
16
+ },
17
+ "registry": {
18
+ "type": "string"
19
+ },
20
+ "spec_version": {
21
+ "type": "string"
22
+ },
23
+ "description": {
24
+ "type": "string"
25
+ },
26
+ "conformance": {
27
+ "type": "string"
28
+ },
29
+ "document_primitives": {
30
+ "type": "object",
31
+ "additionalProperties": {
32
+ "$ref": "#/$defs/primitiveDefinition"
33
+ }
34
+ },
35
+ "contract_primitives": {
36
+ "type": "object",
37
+ "additionalProperties": {
38
+ "$ref": "#/$defs/contractPrimitive"
39
+ }
40
+ },
41
+ "universal_frontmatter": {
42
+ "type": "object",
43
+ "required": ["required"],
44
+ "properties": {
45
+ "required": {
46
+ "type": "array",
47
+ "items": { "type": "string" }
48
+ },
49
+ "recommended_okf": {
50
+ "type": "array",
51
+ "items": { "type": "string" }
52
+ },
53
+ "recommended_semantic": {
54
+ "type": "array",
55
+ "items": { "type": "string" }
56
+ },
57
+ "spec_ref": {
58
+ "type": "string"
59
+ }
60
+ }
61
+ },
62
+ "portability": {
63
+ "type": "object",
64
+ "required": ["classes", "export_profiles"],
65
+ "properties": {
66
+ "description": { "type": "string" },
67
+ "classes": {
68
+ "type": "object",
69
+ "additionalProperties": { "type": "string" }
70
+ },
71
+ "export_profiles": {
72
+ "type": "object",
73
+ "additionalProperties": {
74
+ "type": "object",
75
+ "required": ["includes"],
76
+ "properties": {
77
+ "includes": {
78
+ "type": "array",
79
+ "items": { "type": "string" }
80
+ },
81
+ "excludes": {
82
+ "type": "array",
83
+ "items": { "type": "string" }
84
+ },
85
+ "resource_bound": { "type": "string" },
86
+ "note": { "type": "string" }
87
+ }
88
+ }
89
+ },
90
+ "conformance": { "type": "string" },
91
+ "spec_ref": { "type": "string" }
92
+ }
93
+ },
94
+ "bundle": {
95
+ "type": "object",
96
+ "required": [
97
+ "container",
98
+ "structure",
99
+ "manifest_required_fields",
100
+ "provenance_required_fields"
101
+ ],
102
+ "properties": {
103
+ "description": { "type": "string" },
104
+ "container": { "type": "string" },
105
+ "structure": {
106
+ "type": "array",
107
+ "items": { "type": "string" }
108
+ },
109
+ "file_sort": { "type": "string" },
110
+ "manifest_required_fields": {
111
+ "type": "array",
112
+ "items": { "type": "string" }
113
+ },
114
+ "manifest_optional_fields": {
115
+ "type": "array",
116
+ "items": { "type": "string" }
117
+ },
118
+ "provenance_required_fields": {
119
+ "type": "array",
120
+ "items": { "type": "string" }
121
+ },
122
+ "provenance_optional_fields": {
123
+ "type": "array",
124
+ "items": { "type": "string" }
125
+ },
126
+ "content_hash": { "type": "string" },
127
+ "parameter_fields": {
128
+ "type": "object",
129
+ "required": ["required"],
130
+ "properties": {
131
+ "required": {
132
+ "type": "array",
133
+ "items": { "type": "string" }
134
+ },
135
+ "optional": {
136
+ "type": "array",
137
+ "items": { "type": "string" }
138
+ },
139
+ "source_enum": {
140
+ "type": "array",
141
+ "items": { "type": "string" }
142
+ },
143
+ "note": { "type": "string" }
144
+ }
145
+ },
146
+ "conformance": { "type": "string" },
147
+ "spec_ref": { "type": "string" }
148
+ }
149
+ },
150
+ "provisioning": {
151
+ "type": "object",
152
+ "required": [
153
+ "verbs",
154
+ "step_required_fields",
155
+ "system_enum",
156
+ "mode_enum"
157
+ ],
158
+ "properties": {
159
+ "description": { "type": "string" },
160
+ "verbs": {
161
+ "type": "object",
162
+ "additionalProperties": { "type": "string" }
163
+ },
164
+ "step_required_fields": {
165
+ "type": "array",
166
+ "items": { "type": "string" }
167
+ },
168
+ "step_optional_fields": {
169
+ "type": "array",
170
+ "items": { "type": "string" }
171
+ },
172
+ "system_enum": {
173
+ "type": "array",
174
+ "items": { "type": "string" }
175
+ },
176
+ "mode_enum": {
177
+ "type": "array",
178
+ "items": { "type": "string" }
179
+ },
180
+ "edge_relations": {
181
+ "type": "array",
182
+ "items": { "type": "string" }
183
+ },
184
+ "install_modes": {
185
+ "type": "array",
186
+ "items": { "type": "string" }
187
+ },
188
+ "id_remap": { "type": "string" },
189
+ "upgrade": { "type": "string" },
190
+ "conformance": { "type": "string" },
191
+ "spec_ref": { "type": "string" }
192
+ }
193
+ },
194
+ "runtime": {
195
+ "type": "object",
196
+ "required": [
197
+ "trigger_types",
198
+ "misfire_policies",
199
+ "concurrency_policies",
200
+ "task_instantiation_fields",
201
+ "run_statuses",
202
+ "idempotency_basis"
203
+ ],
204
+ "properties": {
205
+ "description": { "type": "string" },
206
+ "trigger_types": {
207
+ "type": "array",
208
+ "items": { "type": "string" }
209
+ },
210
+ "misfire_policies": {
211
+ "type": "array",
212
+ "items": { "type": "string" }
213
+ },
214
+ "concurrency_policies": {
215
+ "type": "array",
216
+ "items": { "type": "string" }
217
+ },
218
+ "task_instantiation_fields": {
219
+ "type": "array",
220
+ "items": { "type": "string" }
221
+ },
222
+ "run_statuses": {
223
+ "type": "array",
224
+ "items": { "type": "string" }
225
+ },
226
+ "idempotency_basis": { "type": "string" },
227
+ "spec_ref": { "type": "string" }
228
+ }
229
+ },
230
+ "semantic": {
231
+ "type": "object",
232
+ "required": [
233
+ "version",
234
+ "identity_fields",
235
+ "surface_fields",
236
+ "relation_fields",
237
+ "renderable_fields",
238
+ "language_pattern",
239
+ "safe_default"
240
+ ],
241
+ "properties": {
242
+ "version": { "type": "integer" },
243
+ "identity_fields": {
244
+ "type": "array",
245
+ "items": { "type": "string" }
246
+ },
247
+ "surface_fields": {
248
+ "type": "array",
249
+ "items": { "type": "string" }
250
+ },
251
+ "relation_fields": {
252
+ "type": "array",
253
+ "items": { "type": "string" }
254
+ },
255
+ "renderable_fields": {
256
+ "type": "array",
257
+ "items": { "type": "string" }
258
+ },
259
+ "language_pattern": { "type": "string" },
260
+ "safe_default": { "type": "string" },
261
+ "spec_ref": { "type": "string" }
262
+ }
263
+ },
264
+ "reserved_keys": {
265
+ "type": "array",
266
+ "items": { "type": "string" }
267
+ },
268
+ "host_field_prefix": { "type": "string" },
269
+ "spec_ref_reserved": { "type": "string" }
270
+ },
271
+ "$defs": {
272
+ "primitiveDefinition": {
273
+ "type": "object",
274
+ "required": [
275
+ "family",
276
+ "append_only",
277
+ "portability",
278
+ "required_fields"
279
+ ],
280
+ "properties": {
281
+ "family": {
282
+ "type": "string"
283
+ },
284
+ "canonical_filename": {
285
+ "type": ["string", "null"]
286
+ },
287
+ "filename_convention": {
288
+ "type": "string"
289
+ },
290
+ "append_only": {
291
+ "type": "boolean"
292
+ },
293
+ "portability": {
294
+ "type": "string",
295
+ "enum": ["structural", "tenant_private", "resource_bound"]
296
+ },
297
+ "required_fields": {
298
+ "type": "array",
299
+ "items": { "type": "string" }
300
+ },
301
+ "required_when": {
302
+ "type": "object",
303
+ "additionalProperties": {
304
+ "type": "array",
305
+ "items": { "type": "string" }
306
+ }
307
+ },
308
+ "enums": {
309
+ "type": "object",
310
+ "additionalProperties": {
311
+ "type": "array",
312
+ "items": { "type": "string" }
313
+ }
314
+ },
315
+ "patterns": {
316
+ "type": "object",
317
+ "additionalProperties": {
318
+ "type": "string"
319
+ }
320
+ },
321
+ "immutable_fields": {
322
+ "type": "array",
323
+ "items": { "type": "string" }
324
+ },
325
+ "references": {
326
+ "type": "object",
327
+ "additionalProperties": {
328
+ "type": "object",
329
+ "properties": {
330
+ "allowed_types": {
331
+ "type": "array",
332
+ "items": { "type": "string" }
333
+ },
334
+ "disallowed_types": {
335
+ "type": "array",
336
+ "items": { "type": "string" }
337
+ },
338
+ "allowed_portability": {
339
+ "type": "array",
340
+ "items": { "type": "string" }
341
+ },
342
+ "must_exist": {
343
+ "type": "boolean"
344
+ },
345
+ "hash_field": {
346
+ "type": "string"
347
+ }
348
+ }
349
+ }
350
+ },
351
+ "aliases": {
352
+ "type": "array",
353
+ "items": { "type": "string" }
354
+ },
355
+ "deprecated": {
356
+ "type": "boolean"
357
+ },
358
+ "deprecation_notice": {
359
+ "type": "string"
360
+ },
361
+ "resource": {
362
+ "type": "object",
363
+ "properties": {
364
+ "kind": { "type": "string" },
365
+ "provision_relation": { "type": "string" }
366
+ }
367
+ },
368
+ "scopes": {
369
+ "type": "array",
370
+ "items": { "type": "string" }
371
+ },
372
+ "spec_ref": {
373
+ "type": "string"
374
+ }
375
+ }
376
+ },
377
+ "contractPrimitive": {
378
+ "type": "object",
379
+ "required": ["purpose", "spec_ref"],
380
+ "properties": {
381
+ "purpose": { "type": "string" },
382
+ "spec_ref": { "type": "string" }
383
+ }
384
+ }
385
+ }
386
+ }
@@ -306,6 +306,7 @@ async function checkRecoveryTriggers(root) {
306
306
  const untouched = read(live, 'rules/live.md').includes('in flight');
307
307
  holder.child.kill('SIGKILL');
308
308
  const signal = await holder.exited;
309
+ await new Promise((resolve) => setTimeout(resolve, 50));
309
310
  const afterKill = await fileKernel(live).recover();
310
311
  check('recovery skips an intent whose writer is alive',
311
312
  whileAlive.length === 1 && whileAlive[0].outcome === 'in_flight' && untouched, JSON.stringify(whileAlive));
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Schema export and inspection commands for SSSS.
3
+ */
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { parseArgs, die, wantsHelp } from './lib/cli.mjs';
8
+
9
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
10
+ const ROOT = path.resolve(HERE, '..');
11
+ const SCHEMAS_DIR = path.resolve(ROOT, 'schemas');
12
+
13
+ const HELP = `Usage: ssss schema core|bundle|export [options]
14
+
15
+ Commands:
16
+ core [--out <file>] Output the Core Registry JSON Schema.
17
+ bundle [--out <file>] Output the .ucw Bundle JSON Schema.
18
+ export [--out <dir>] Export all canonical schemas to a directory (default: ./schemas).
19
+ `;
20
+
21
+ function emit(value, output) {
22
+ const text = typeof value === 'string' ? value : `${JSON.stringify(value, null, 2)}\n`;
23
+ if (output) {
24
+ fs.mkdirSync(path.dirname(path.resolve(output)), { recursive: true });
25
+ fs.writeFileSync(path.resolve(output), text, { encoding: 'utf8' });
26
+ } else {
27
+ process.stdout.write(text);
28
+ }
29
+ }
30
+
31
+ export async function run(argv) {
32
+ if (wantsHelp(argv)) { console.log(HELP); return; }
33
+ const verb = argv[0];
34
+ const { flags } = parseArgs(argv.slice(1));
35
+
36
+ try {
37
+ const coreSchemaPath = path.join(SCHEMAS_DIR, 'core-registry.schema.json');
38
+ const bundleSchemaPath = path.join(SCHEMAS_DIR, 'bundle.schema.json');
39
+
40
+ if (verb === 'core') {
41
+ const content = fs.readFileSync(coreSchemaPath, 'utf8');
42
+ emit(content, flags.out);
43
+ return;
44
+ }
45
+ if (verb === 'bundle') {
46
+ const content = fs.readFileSync(bundleSchemaPath, 'utf8');
47
+ emit(content, flags.out);
48
+ return;
49
+ }
50
+ if (verb === 'export') {
51
+ const targetDir = path.resolve(flags.out || './schemas');
52
+ fs.mkdirSync(targetDir, { recursive: true });
53
+ fs.copyFileSync(coreSchemaPath, path.join(targetDir, 'core-registry.schema.json'));
54
+ fs.copyFileSync(bundleSchemaPath, path.join(targetDir, 'bundle.schema.json'));
55
+ console.log(`Exported canonical schemas to ${targetDir}`);
56
+ return;
57
+ }
58
+ die(`Unknown schema verb '${verb || ''}'.\n${HELP}`);
59
+ } catch (error) {
60
+ die(error.message);
61
+ }
62
+ }
@@ -33,6 +33,7 @@ import { MemoryIdempotencyStore, FileIdempotencyStore, runIdempotencyContract }
33
33
  import { capabilityCovers, createCapabilityAuthorizer } from '../src/authorization.mjs';
34
34
  import { createEngine } from '../src/engine.mjs';
35
35
  import { validateVerifiedPrincipal } from '../src/authorization.mjs';
36
+ import { validateJsonSchema } from '../src/schema-validator.mjs';
36
37
 
37
38
  async function rejects(promise, pattern) {
38
39
  try { await promise; return false; } catch (error) { return pattern.test(error.message); }
@@ -438,6 +439,32 @@ async function checkCapabilities(root, check) {
438
439
  (await writeScoped('ps-2', 'rules/engineering/lint.md', doc('rule', 'Lint'))).error?.code === 'forbidden');
439
440
  }
440
441
 
442
+ function checkJsonSchemas(check) {
443
+ const coreRegistry = JSON.parse(fs.readFileSync(path.resolve('registry/core.json'), 'utf8'));
444
+ const coreSchema = JSON.parse(fs.readFileSync(path.resolve('schemas/core-registry.schema.json'), 'utf8'));
445
+ const coreResult = validateJsonSchema(coreRegistry, coreSchema);
446
+ check('schema: registry/core.json validates against schemas/core-registry.schema.json', coreResult.valid, coreResult.errors?.[0]?.message);
447
+
448
+ const bundle = JSON.parse(fs.readFileSync(path.resolve('conformance/reference-bundle.ucw.json'), 'utf8'));
449
+ const bundleSchema = JSON.parse(fs.readFileSync(path.resolve('schemas/bundle.schema.json'), 'utf8'));
450
+ const bundleResult = validateJsonSchema(bundle, bundleSchema);
451
+ check('schema: reference-bundle.ucw.json validates against schemas/bundle.schema.json', bundleResult.valid, bundleResult.errors?.[0]?.message);
452
+
453
+ const badCore = { ...coreRegistry };
454
+ delete badCore.spec_version;
455
+ const badCoreResult = validateJsonSchema(badCore, coreSchema);
456
+ check('schema: core registry schema rejects missing spec_version', !badCoreResult.valid && badCoreResult.errors.some((e) => e.path.includes('spec_version')));
457
+
458
+ const badPortability = JSON.parse(JSON.stringify(coreRegistry));
459
+ badPortability.document_primitives.memory.portability = 'illegal_class';
460
+ const badPortResult = validateJsonSchema(badPortability, coreSchema);
461
+ check('schema: core registry schema rejects invalid portability enum', !badPortResult.valid && badPortResult.errors.some((e) => e.path.includes('portability')));
462
+
463
+ const badBundle = { files: [] };
464
+ const badBundleResult = validateJsonSchema(badBundle, bundleSchema);
465
+ check('schema: bundle schema rejects missing manifest', !badBundleResult.valid && badBundleResult.errors.some((e) => e.path.includes('manifest')));
466
+ }
467
+
441
468
  // Resource hooks (spec §6.3): every prepare ends in exactly one finalize or
442
469
  // reconcile, and nothing after the event append may undo the commit.
443
470
  async function checkResourceLifecycle(check) {
@@ -785,6 +812,7 @@ export async function runKernel09Conformance() {
785
812
  checkFrontmatterSubset(check);
786
813
  checkRequiredPresence(check);
787
814
  await checkCapabilities(temp, check);
815
+ checkJsonSchemas(check);
788
816
  } finally {
789
817
  fs.rmSync(temp, { recursive: true, force: true });
790
818
  }
package/scripts/ssss.mjs CHANGED
@@ -41,6 +41,7 @@ Authoring, wire protocol & conformance:
41
41
  bridge [options] Execute operations over stdin/stdout JSON (spec §6.8).
42
42
  primitive <verb> Create, validate, inspect, migrate, or emit types.
43
43
  registry <verb> Compose, lock, or verify an effective registry.
44
+ schema <verb> Output or export canonical JSON Schemas (core, bundle).
44
45
  adapter conformance Run machine-readable adapter contract suites.
45
46
  migrate 0.8-to-0.9 Diagnose and plan a non-destructive 0.9 migration.
46
47
  autolink [dir] Generate OKF wiki links across a vault.
@@ -72,6 +73,7 @@ const MODULES = {
72
73
  semantic: './cmd-semantic.mjs',
73
74
  primitive: './cmd-primitive.mjs',
74
75
  registry: './cmd-registry.mjs',
76
+ schema: './cmd-schema.mjs',
75
77
  adapter: './cmd-adapter.mjs',
76
78
  migrate: './cmd-migrate.mjs',
77
79
  serve: './cmd-serve.mjs',
@@ -4,7 +4,7 @@ title: SSSS Protocol Reference
4
4
  name: ssss
5
5
  description: Validate or change SSSS documents, operations, registries, workflows and portable host adapters.
6
6
  timestamp: 2026-11-02T19:00:00Z
7
- version: 0.14.1
7
+ version: 0.14.2
8
8
  repo_scoped: false
9
9
  ---
10
10
 
@@ -1,4 +1,4 @@
1
- # SSSS portable core — 0.14.1
1
+ # SSSS portable core — 0.14.2
2
2
 
3
3
  Verified against the SSSS owner package, registry and spec on 2026-11-02. Establish ground truth from the installed validator, composed registry plus lock, and actual host mutation path. Schema support does not prove a configured runner or provider integration.
4
4
 
@@ -15,4 +15,4 @@ Preserve portability classes: structural definitions, tenant-private data and re
15
15
  ## Verification
16
16
  Run relevant schema/registry/lock and operation conformance, with valid and invalid examples. Cover duplicate/conflicting idempotency (including retries answered from the event log), unsafe paths, unauthorized/expired actors, leases, append semantics, rollback and commit-intent crash recovery where changed. Use `ssss --help`, then operation-specific help; commands differ by installed version. Report exact package version, registry/lock identity and actual results. Keep secrets and private runtime data out of skill content, bundles and logs.
17
17
 
18
- Normative sources are bundled below from the owner release tag v0.14.1 (`docs/ssss-spec.md` and `registry/core.json` at that tag). Retain authored local wrappers/references; this managed core is the portable update boundary.
18
+ Normative sources are bundled below from the owner release tag v0.14.2 (`docs/ssss-spec.md` and `registry/core.json` at that tag). Retain authored local wrappers/references; this managed core is the portable update boundary.
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "https://ssss.dev/registry/v1",
2
+ "$schema": "https://ssss.dev/schemas/core-registry.schema.json",
3
3
  "registry": "core",
4
4
  "spec_version": "0.9",
5
5
  "description": "The canonical SSSS core registry and semantic application-kernel contract. Core primitives are qualified as ssss:<name>; applications define namespaced primitives through layered extension registries without copying or redefining core behavior.",
@@ -261,8 +261,9 @@ primitive — it implements the subset its product requires — but any primitiv
261
261
  | `primitive` | meta | structural | A governed, versioned definition for a namespaced SSSS primitive. |
262
262
 
263
263
  The **Portability** column is the primitive's default portability class (§5.5); a host
264
- resolves it from `registry/core.json`. Extension registries assign their own primitives a
265
- class — notably `resource_bound` for things like domains and phone numbers.
264
+ resolves it from `registry/core.json`. The canonical machine-readable registry conforms to the
265
+ JSON Schema (Draft 2020-12) published in `schemas/core-registry.schema.json`. Extension registries
266
+ assign their own primitives a class — notably `resource_bound` for things like domains and phone numbers.
266
267
 
267
268
  ### 5.2 Contract Primitives
268
269
 
@@ -1869,7 +1870,9 @@ A bundle is a JSON object with three top-level members:
1869
1870
  ```
1870
1871
 
1871
1872
  `files` MUST be sorted by `path` (§16.3 order) so that two exports of the same vault state
1872
- are identical and content-hashable (§16.3, `provenance.content_hash`).
1873
+ are identical and content-hashable (§16.3, `provenance.content_hash`). The normative JSON Schema
1874
+ (Draft 2020-12) for `.ucw` bundle containers is published in `schemas/bundle.schema.json` and
1875
+ exportable via the CLI command `ssss schema bundle`.
1873
1876
 
1874
1877
  ### 16.2 The Manifest
1875
1878
 
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Zero-dependency JSON Schema validator for SSSS core and bundle schemas.
3
+ *
4
+ * Implements validation for standard JSON Schema Draft 2020-12 constructs
5
+ * used by SSSS schemas:
6
+ * - type (single or union)
7
+ * - properties & additionalProperties
8
+ * - required
9
+ * - items
10
+ * - enum
11
+ * - pattern
12
+ * - $ref (local root-anchored pointers: #/$defs/...)
13
+ */
14
+
15
+ function resolveRef(root, ref) {
16
+ if (!ref.startsWith('#/')) {
17
+ throw new Error(`Only local root-anchored $ref pointers are supported: ${ref}`);
18
+ }
19
+ const parts = ref.slice(2).split('/');
20
+ let current = root;
21
+ for (const part of parts) {
22
+ if (current == null || typeof current !== 'object') {
23
+ throw new Error(`Unable to resolve $ref pointer: ${ref}`);
24
+ }
25
+ current = current[part];
26
+ }
27
+ return current;
28
+ }
29
+
30
+ function matchType(value, expectedType) {
31
+ if (Array.isArray(expectedType)) {
32
+ return expectedType.some((t) => matchType(value, t));
33
+ }
34
+ switch (expectedType) {
35
+ case 'string':
36
+ return typeof value === 'string';
37
+ case 'number':
38
+ return typeof value === 'number' && !Number.isNaN(value);
39
+ case 'integer':
40
+ return Number.isInteger(value);
41
+ case 'boolean':
42
+ return typeof value === 'boolean';
43
+ case 'object':
44
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
45
+ case 'array':
46
+ return Array.isArray(value);
47
+ case 'null':
48
+ return value === null;
49
+ default:
50
+ return true;
51
+ }
52
+ }
53
+
54
+ export function validateJsonSchema(data, schema, options = {}) {
55
+ const root = options.rootSchema || schema;
56
+ const errors = [];
57
+
58
+ function validate(value, curSchema, path) {
59
+ if (!curSchema || typeof curSchema !== 'object') return;
60
+
61
+ if (curSchema.$ref) {
62
+ const resolved = resolveRef(root, curSchema.$ref);
63
+ validate(value, resolved, path);
64
+ return;
65
+ }
66
+
67
+ if (curSchema.type !== undefined) {
68
+ if (!matchType(value, curSchema.type)) {
69
+ errors.push({
70
+ path,
71
+ message: `Expected type ${JSON.stringify(curSchema.type)}, but received ${Array.isArray(value) ? 'array' : value === null ? 'null' : typeof value}.`,
72
+ });
73
+ return;
74
+ }
75
+ }
76
+
77
+ if (curSchema.enum !== undefined && !curSchema.enum.includes(value)) {
78
+ errors.push({
79
+ path,
80
+ message: `Value ${JSON.stringify(value)} is not in enum: ${curSchema.enum.map((e) => JSON.stringify(e)).join(', ')}.`,
81
+ });
82
+ }
83
+
84
+ if (typeof value === 'string' && curSchema.pattern !== undefined) {
85
+ const reg = new RegExp(curSchema.pattern);
86
+ if (!reg.test(value)) {
87
+ errors.push({
88
+ path,
89
+ message: `String does not match pattern ${curSchema.pattern}.`,
90
+ });
91
+ }
92
+ }
93
+
94
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
95
+ if (Array.isArray(curSchema.required)) {
96
+ for (const reqKey of curSchema.required) {
97
+ if (value[reqKey] === undefined) {
98
+ errors.push({
99
+ path: path ? `${path}.${reqKey}` : reqKey,
100
+ message: `Missing required property '${reqKey}'.`,
101
+ });
102
+ }
103
+ }
104
+ }
105
+
106
+ const definedKeys = new Set(Object.keys(curSchema.properties || {}));
107
+ for (const [key, val] of Object.entries(value)) {
108
+ const subPath = path ? `${path}.${key}` : key;
109
+ if (definedKeys.has(key)) {
110
+ validate(val, curSchema.properties[key], subPath);
111
+ } else if (curSchema.additionalProperties === false) {
112
+ errors.push({
113
+ path: subPath,
114
+ message: `Unexpected property '${key}' not defined in schema.`,
115
+ });
116
+ } else if (typeof curSchema.additionalProperties === 'object' && curSchema.additionalProperties !== null) {
117
+ validate(val, curSchema.additionalProperties, subPath);
118
+ }
119
+ }
120
+ }
121
+
122
+ if (Array.isArray(value) && curSchema.items) {
123
+ for (let i = 0; i < value.length; i++) {
124
+ validate(value[i], curSchema.items, `${path}[${i}]`);
125
+ }
126
+ }
127
+ }
128
+
129
+ validate(data, schema, '');
130
+ return {
131
+ valid: errors.length === 0,
132
+ errors,
133
+ };
134
+ }