@gregiteen/ssss-cli 0.8.0 → 0.9.1

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 (48) hide show
  1. package/CHANGELOG.md +55 -4
  2. package/README.md +42 -84
  3. package/VERSION +1 -1
  4. package/conformance/README.md +1 -1
  5. package/conformance/fixtures.json +4 -48
  6. package/conformance/reference-bundle.ucw.json +22 -21
  7. package/docs/help/bundle.md +2 -2
  8. package/docs/help/overview.md +9 -6
  9. package/docs/help/semantic.md +5 -4
  10. package/docs/ssss-spec.md +42 -86
  11. package/package.json +22 -3
  12. package/registry/core.json +22 -29
  13. package/scripts/build-reference-bundle.mjs +0 -20
  14. package/scripts/cmd-adapter.mjs +29 -0
  15. package/scripts/cmd-import.mjs +1 -1
  16. package/scripts/cmd-migrate.mjs +26 -0
  17. package/scripts/cmd-new.mjs +5 -5
  18. package/scripts/cmd-primitive.mjs +48 -0
  19. package/scripts/cmd-registry.mjs +25 -0
  20. package/scripts/cmd-semantic.mjs +6 -8
  21. package/scripts/conformance-09.mjs +331 -0
  22. package/scripts/conformance.mjs +153 -186
  23. package/scripts/generate-sbom.mjs +96 -0
  24. package/scripts/phase9-cross-host-corpus.mjs +223 -0
  25. package/scripts/ssss.mjs +8 -2
  26. package/skills/ssss/SKILL.md +32 -208
  27. package/skills/ssss/evals/evals.json +3 -3
  28. package/skills/ssss/references/contract-reference.md +7 -8
  29. package/skills/ssss/scripts/validate-schema.mjs +1 -44
  30. package/src/authorization.mjs +62 -0
  31. package/src/bundle.mjs +41 -5
  32. package/src/engine.mjs +101 -503
  33. package/src/events.mjs +155 -0
  34. package/src/guard.mjs +32 -0
  35. package/src/http.mjs +26 -0
  36. package/src/idempotency.mjs +55 -0
  37. package/src/kernel.mjs +260 -0
  38. package/src/leases.mjs +173 -0
  39. package/src/primitive.mjs +227 -0
  40. package/src/projections.mjs +133 -0
  41. package/src/registry.mjs +190 -39
  42. package/src/semantic.mjs +124 -316
  43. package/src/ui.mjs +100 -0
  44. package/src/validator.mjs +87 -0
  45. package/src/vfs.mjs +203 -0
  46. package/types/kernel.d.ts +48 -0
  47. package/docs/help/localization.md +0 -43
  48. package/scripts/cmd-localize.mjs +0 -44
package/docs/ssss-spec.md CHANGED
@@ -1,14 +1,15 @@
1
1
  # SSSS — Structured Semantic Syntax System
2
2
 
3
- **Specification v0.7 — Draft**
3
+ **Specification v0.9**
4
4
 
5
5
  > This is the canonical, vendor-neutral specification for SSSS. It is the ground
6
6
  > truth on which all SSSS implementations are built. It is intended to be vendored
7
- > byte-for-byte into any repository that implements SSSS (e.g. the UltraChat
7
+ > byte-for-byte into any repository that implements SSSS (e.g. a compatible host
8
8
  > Sovereign AI OS, the Total Recall reference kernel).
9
9
  >
10
- > Status: **Draft**. The format, type registry, and conformance contract are under
11
- > active development. Expect breaking changes until v1.0.
10
+ > Status: **Stable for 0.9**. The format, type registry, and conformance contract
11
+ > are settled for this version; until v1.0 a later version MAY still introduce
12
+ > breaking changes.
12
13
  >
13
14
  > Implementation-specific detail (routes, services, storage backends, deployment)
14
15
  > does **not** belong in this document — it belongs in each implementation's own
@@ -31,9 +32,9 @@ SSSS additionally defines the **Operation Contract**: a single, validated, idemp
31
32
  envelope through which all agent-generated mutations must flow. This makes
32
33
  AI-generated state changes deterministic, replayable, conflict-safe, and auditable.
33
34
 
34
- Its semantic projection contract turns the same canonical files into a deterministic
35
- searchable graph, while translation overlays localize natural-language surfaces
36
- without mutating symbolic control fields or private operational data.
35
+ Its semantic runtime turns the same canonical files into a deterministic searchable
36
+ graph, while multilingual embedding and render adapters localize presentation without
37
+ mutating symbolic control fields or private operational data.
37
38
 
38
39
  Any tool, IDE, agent framework, daemon, or CLI that can read Markdown can
39
40
  interoperate with an SSSS vault. The only thing that distinguishes a memory engine,
@@ -195,7 +196,7 @@ primitive — it implements the subset its product requires — but any primitiv
195
196
  | `page` | capability | structural | A VFS-native sandboxed custom workspace page. |
196
197
  | `migration` | meta | structural | An SSSS schema migration state record. |
197
198
  | `release` | meta | structural | An SSSS system schema version release record. |
198
- | `translation` | localization | structural | A hash-bound, non-destructive localization overlay for a structural source document. |
199
+ | `primitive` | meta | structural | A governed, versioned definition for a namespaced SSSS primitive. |
199
200
 
200
201
  The **Portability** column is the primitive's default portability class (§5.5); a host
201
202
  resolves it from `registry/core.json`. Extension registries assign their own primitives a
@@ -217,7 +218,7 @@ Document primitives are either:
217
218
 
218
219
  - **Replace-type** — the whole file represents current state; a write replaces it
219
220
  entirely (`memory`, `skill`, `assistant`, `workflow`, `model`, `task`,
220
- `conflict`, `translation`).
221
+ `conflict`, `primitive`).
221
222
  - **Append-type** — the file is an ordered, append-only log; writes add records to
222
223
  the body and MUST NOT rewrite prior records (`conversation`, `run`).
223
224
 
@@ -561,48 +562,13 @@ released_at: 2026-05-16T12:00:00Z
561
562
  ---
562
563
  ```
563
564
 
564
- #### `translation`
565
+ #### `primitive`
565
566
 
566
- A non-destructive localization overlay for one structural source document. A
567
- translation is itself `structural`, but it does not replace or fork the source. The
568
- source remains authoritative for identity, behavior, permissions, enums, links, and
569
- all other symbolic control fields.
570
-
571
- REQUIRED: `type`, `translation_id`, `source_path`, `source_hash`, `locale`, `status`
572
- (`draft|reviewed|approved`), and `translated_fields` (a subset of
573
- `title|description|body`). OPTIONAL: `translated_title`, `translated_description`,
574
- `translator`, `reviewed_at`. The Markdown body is the translated body when
575
- `translated_fields` includes `body`.
576
-
577
- `translation_id`, `source_path`, and `locale` are immutable after creation.
578
- `source_path` MUST be a safe, vault-relative path to an existing `structural`
579
- document whose type is not `translation`. `source_hash` MUST equal the source's
580
- current SHA-256 content hash, written as `sha256:<64 lowercase hex characters>`.
581
- A stale, missing, private, resource-bound, recursive, traversing, or symlinked source
582
- MUST be rejected.
583
-
584
- ```markdown
585
- ---
586
- type: translation
587
- title: "Política de reembolsos — español"
588
- description: "Traducción al español de la política de reembolsos."
589
- timestamp: 2026-07-10T00:00:00Z
590
- translation_id: refund-policy-es
591
- source_path: rules/refund-policy.md
592
- source_hash: sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
593
- locale: es
594
- status: approved
595
- translated_fields: [title, description, body]
596
- translated_title: "Política de reembolsos"
597
- translated_description: "Reglas de reembolso para compradores."
598
- ---
599
-
600
- Los reembolsos están disponibles dentro de los treinta días.
601
- ```
602
-
603
- Draft and reviewed translations MAY be stored and previewed, but only approved,
604
- hash-current overlays are applied by default to canonical localized projections
605
- (§11.9).
567
+ A governed definition for a namespaced primitive that can be authored in any human
568
+ language without changing the core registry. REQUIRED: `type`, `primitive_id`,
569
+ `namespace`, `version`, `name`, `mutation`, `portability`, `scopes`, and `fields`.
570
+ Stable primitive, field, enum, capability, and action identifiers are symbolic and
571
+ language-independent. Labels and descriptions are multilingual presentation data.
606
572
 
607
573
  ### 5.5 Portability Classification
608
574
 
@@ -1106,10 +1072,10 @@ for planning a workflow trigger into a `workflow_triggered` event envelope and a
1106
1072
  task `operation` envelope. Hosts MAY implement their own daemon, but SHOULD pass the
1107
1073
  runtime conformance checks to prove the same source-of-truth and idempotency rules.
1108
1074
 
1109
- ### 11.9 Semantic Projection & Localization Contract
1075
+ ### 11.9 Multilingual Semantic Runtime Contract
1110
1076
 
1111
1077
  The semantic layer is a **derived projection** over validated vault documents. It
1112
- MUST be deterministic for the same vault bytes, registry set, locale, and projection
1078
+ MUST be deterministic for the same vault bytes, registry set, adapter identity, and projection
1113
1079
  options. It MUST NOT become a second source of truth.
1114
1080
 
1115
1081
  Each projected record SHOULD expose a stable identity, source path and content hash,
@@ -1121,29 +1087,20 @@ available as a dependency-free interoperability baseline.
1121
1087
 
1122
1088
  The safe default projection contains only `structural` documents. It MUST exclude
1123
1089
  `tenant_private` and `resource_bound` documents unless the caller explicitly enables
1124
- private indexing in an authorized context. A localized export MUST use the same safe
1125
- default, so requesting a locale can never implicitly disclose conversations, runs,
1126
- tasks, credentials, customer data, or bound resource values.
1127
-
1128
- When a locale is requested, the host MAY apply one eligible `translation` (§5.4) per
1129
- source. Eligibility requires all of the following:
1130
-
1131
- 1. The overlay locale normalizes to the requested locale.
1132
- 2. Its status is `approved`, unless a caller explicitly opts into draft preview.
1133
- 3. The source is an existing, non-symlinked `structural` document.
1134
- 4. `source_hash` exactly matches the current source bytes.
1135
- 5. There is no second eligible overlay for the same source and locale.
1136
-
1137
- An overlay may replace only `title`, `description`, and body. It MUST preserve `type`,
1138
- paths, ids, enums, status, permissions, portability, relations, and every other
1139
- symbolic field from the source. Stale hashes, duplicate overlays, traversal, symlink
1140
- targets, recursive translations, and translations of non-structural sources MUST
1141
- fail closed.
1142
-
1143
- A materialized locale is a disposable projection, not a translated vault. It MUST be
1144
- written outside the source vault to a new or empty non-symlinked directory, preserve
1145
- source-relative paths, and include enough projection metadata to identify its locale
1146
- and source hashes. It may be deleted and rebuilt at any time.
1090
+ private indexing in an authorized context. Language selection MUST NOT widen scope or
1091
+ implicitly disclose conversations, runs, tasks, credentials, customer data, or bound
1092
+ resource values.
1093
+
1094
+ A host MAY inject a multilingual embedding adapter. Every enriched record MUST carry
1095
+ the embedding model identity and vector dimension. Search SHOULD report lexical and
1096
+ semantic evidence separately before computing a hybrid score.
1097
+
1098
+ A host MAY inject an LLM render adapter at presentation time. The render request MUST
1099
+ carry an invariant-control block. Rendering MAY change natural-language title,
1100
+ description, body, formatting, dates, and units, but MUST NOT change primitive IDs,
1101
+ field IDs, enum codes, actions, permissions, paths, versions, hashes, or relations.
1102
+ Canonical documents are authored once in any language; SSSS 0.9 neither requires nor
1103
+ materializes translation documents or localized vault trees.
1147
1104
 
1148
1105
  ---
1149
1106
 
@@ -1161,8 +1118,8 @@ Fixtures are distributed as a JSON document carrying:
1161
1118
  - `runtime_contract` — trigger vocabulary and daemon idempotency rules (§11.8).
1162
1119
  - registry-extension checks — schema shape, regex validity, symlink rejection, and
1163
1120
  core/sibling collision rejection.
1164
- - semantic/localization checks — deterministic indexing, privacy defaults, graph
1165
- edges, exact source hashes, immutable overlay identity, and safe materialization
1121
+ - multilingual semantic checks — deterministic indexing, privacy defaults, graph
1122
+ edges, embedding provenance, cross-language retrieval, and invariant rendering
1166
1123
  (§11.9).
1167
1124
  - `fixtures[]` — each with a `request`, an `expected_response`, and an
1168
1125
  OPTIONAL `expected_http_status`.
@@ -1172,7 +1129,7 @@ The conformance fixture set is the shared test contract between all SSSS
1172
1129
  implementations. A host MUST NOT claim SSSS conformance without passing the current
1173
1130
  fixture set. Hosts implementing workflow daemons SHOULD also run the reference
1174
1131
  runtime checks exposed by `@gregiteen/ssss-cli/runtime`. Hosts exposing semantic
1175
- search or localized projections MUST also pass the §11.9 checks.
1132
+ search or runtime rendering MUST also pass the §11.9 checks.
1176
1133
 
1177
1134
  ---
1178
1135
 
@@ -1200,7 +1157,7 @@ opaque ID as the slug and keep the human-readable label in `title` / `name`.
1200
1157
  This document is versioned independently of any host and of the conformance
1201
1158
  fixture set.
1202
1159
 
1203
- - The spec version is stated in the document header (currently **v0.7 — Draft**).
1160
+ - The spec version is stated in the document header (currently **v0.9**).
1204
1161
  - Breaking changes to the file format, the type registry, or the Operation
1205
1162
  Contract increment the spec version.
1206
1163
  - Until **v1.0**, any version MAY introduce breaking changes.
@@ -1292,14 +1249,14 @@ inside, the resources it must bind at provision time, and provenance.
1292
1249
  | `ssss_core_version` | yes | The `registry/core.json` `spec_version` the bundle targets (e.g. `"0.3"`). A host MUST refuse a bundle whose core version it does not support. |
1293
1250
  | `required_extensions` | yes | Array of extension registry ids the files rely on (e.g. `["festech"]`). Empty array if the bundle uses only core primitives. A host MUST refuse a bundle naming an extension it has not loaded. |
1294
1251
  | `export_profile` | yes | `backup` \| `template` \| `sale` — the §5.5 profile this bundle was built under. Determines the allowed portability classes. |
1295
- | `primitive_inventory` | yes | Map of primitive `type` → count, over every file in the bundle (replaces ultrachat's hard-coded `categories`; it is registry-driven, so extension types appear automatically). |
1252
+ | `primitive_inventory` | yes | Map of primitive `type` → count, over every file in the bundle (replaces the legacy hard-coded `categories`; it is registry-driven, so extension types appear automatically). |
1296
1253
  | `provisioning` | yes | Array of provisioning steps (§17.2) — the ordered, declarative plan for binding this bundle into a live workspace. Empty for a pure `backup` that is restored in place. |
1297
1254
  | `parameters` | no | Array of parameter definitions (§16.5) the importer must resolve (e.g. business name, domain). |
1298
1255
  | `source_workspace_id` | no | Origin workspace; OMITTED or nulled in `template`/`sale` profiles (it is operator-identifying). |
1299
1256
  | `file_count` | yes | Length of `files`; a cheap integrity check. |
1300
1257
  | `provenance` | yes | `{ content_hash, exporter, signature? }` (§16.3). |
1301
1258
 
1302
- `primitive_inventory` supersedes ultrachat's fixed `categories` object and `provisioning`
1259
+ `primitive_inventory` supersedes the legacy fixed `categories` object and `provisioning`
1303
1260
  supersedes its `capabilities` booleans: both are now open and registry-driven rather than a
1304
1261
  closed enum, so a bundle full of `festech` extension primitives inventories and provisions
1305
1262
  them without a spec change.
@@ -1328,7 +1285,7 @@ honoring `x_portability`) MUST be permitted by `export_profile`.
1328
1285
 
1329
1286
  `parameters` are the values an importer must supply to turn a template into a running
1330
1287
  instance — the difference between "a festival in a box" and "*this* festival." The schema is
1331
- adopted verbatim from ultrachat's `WorkspaceTemplateVariableDefinition`:
1288
+ adopted verbatim from the legacy `WorkspaceTemplateVariableDefinition`:
1332
1289
 
1333
1290
  | Field | Meaning |
1334
1291
  |-------|---------|
@@ -1369,7 +1326,7 @@ restoring an SSSS business are reliable operations rather than bespoke migration
1369
1326
  ### 17.2 Provisioning Steps & Binding Vocabulary
1370
1327
 
1371
1328
  A manifest's `provisioning` array is an ordered list of steps. The step schema and its
1372
- controlled vocabularies are adopted from ultrachat's `WorkspaceProvisioningStep` and
1329
+ controlled vocabularies are adopted from the legacy `WorkspaceProvisioningStep` and
1373
1330
  `WorkspaceGraphEdge`:
1374
1331
 
1375
1332
  A **step** is `{ id, label, system, mode, required, notes? }`:
@@ -1402,7 +1359,7 @@ cannot resolve a link MUST fail the provision rather than emit a dangling refere
1402
1359
  ### 17.4 Composition & Upgrade
1403
1360
 
1404
1361
  - **Composition** — a bundle MAY depend on other bundles. A dependency carries an
1405
- `installMode` of `optional | recommended | required` (adopted from ultrachat's
1362
+ `installMode` of `optional | recommended | required` (adopted from the legacy
1406
1363
  `WorkspaceMarketplaceRecommendation`). `provision` installs `required` dependencies before
1407
1364
  the bundle itself; `optional`/`recommended` are surfaced to the operator.
1408
1365
  - **Upgrade** — moving an installed bundle from `v2` to a `v1`-conformant shape (or any
@@ -1417,8 +1374,7 @@ cannot resolve a link MUST fail the provision rather than emit a dangling refere
1417
1374
 
1418
1375
  The following frontmatter keys are reserved by this spec across all primitives and
1419
1376
  MUST NOT be repurposed by hosts: `type`, `slug`, `schema_version`, `status`,
1420
- `feedback`, `confidence`, `semantic_id`, `relations`, `locale`, `translation_id`,
1421
- `source_path`, `source_hash`, and `translated_fields`.
1377
+ `feedback`, `confidence`, `semantic_id`, `relations`, and `language`.
1422
1378
 
1423
1379
  Hosts adding their own frontmatter fields SHOULD prefix them `x_` to remain
1424
1380
  forward-compatible with future spec revisions.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gregiteen/ssss-cli",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
4
4
  "description": "CLI tools and the reference engine for the Structured Semantic Syntax System",
5
5
  "main": "src/engine.mjs",
6
6
  "exports": {
@@ -11,6 +11,23 @@
11
11
  "./frontmatter": "./src/frontmatter.mjs",
12
12
  "./bundle": "./src/bundle.mjs",
13
13
  "./semantic": "./src/semantic.mjs",
14
+ "./primitive": "./src/primitive.mjs",
15
+ "./validator": "./src/validator.mjs",
16
+ "./kernel": {
17
+ "types": "./types/kernel.d.ts",
18
+ "import": "./src/kernel.mjs",
19
+ "default": "./src/kernel.mjs"
20
+ },
21
+ "./vfs": "./src/vfs.mjs",
22
+ "./identity": "./src/authorization.mjs",
23
+ "./authorization": "./src/authorization.mjs",
24
+ "./leases": "./src/leases.mjs",
25
+ "./idempotency": "./src/idempotency.mjs",
26
+ "./events": "./src/events.mjs",
27
+ "./projections": "./src/projections.mjs",
28
+ "./ui": "./src/ui.mjs",
29
+ "./http": "./src/http.mjs",
30
+ "./guard": "./src/guard.mjs",
14
31
  "./conformance/fixtures.json": "./conformance/fixtures.json",
15
32
  "./conformance/reference-bundle.ucw.json": "./conformance/reference-bundle.ucw.json",
16
33
  "./registry/core.json": "./registry/core.json",
@@ -25,6 +42,7 @@
25
42
  },
26
43
  "files": [
27
44
  "src",
45
+ "types",
28
46
  "scripts",
29
47
  "registry",
30
48
  "conformance",
@@ -50,8 +68,9 @@
50
68
  "ucw",
51
69
  "provisioning",
52
70
  "semantic-search",
53
- "localization",
54
- "translation"
71
+ "multilingual",
72
+ "generative-ui",
73
+ "application-kernel"
55
74
  ],
56
75
  "author": "Greg Iteen",
57
76
  "license": "ISC",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "https://ssss.dev/registry/v1",
3
3
  "registry": "core",
4
- "spec_version": "0.7",
5
- "description": "The canonical SSSS core primitive registry. These 15 document primitives and 5 contract primitives are defined by the SSSS specification (docs/ssss-spec.md §5) and are owned by this standard. Every document primitive declares a portability class (§5.5); the registry also defines the workflow runtime contract (§11.8), semantic/localization contract (§11.9), `.ucw` bundle format (§16), and provisioning contract (§17). A conformant host MUST implement any core primitive it uses according to this registry. Applications extend the core with their own primitives via an extension registry; extensions MUST NOT redefine a core or sibling-extension type.",
4
+ "spec_version": "0.9",
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.",
6
6
  "conformance": "A host is core-conformant for a primitive if a file of that type validates exactly when all required_fields are present and non-empty, and (for append_only types) writes only append.",
7
7
 
8
8
  "document_primitives": {
@@ -137,33 +137,26 @@
137
137
  "required_fields": ["type", "release_id", "version", "schema_version", "summary", "released_at"],
138
138
  "spec_ref": "§5.4 release"
139
139
  },
140
- "translation": {
141
- "family": "localization",
142
- "canonical_path": "translations/<locale>/<source-path>.md",
140
+ "primitive": {
141
+ "family": "meta",
142
+ "canonical_path": "primitives/<namespace>/<id>.md",
143
143
  "append_only": false,
144
144
  "portability": "structural",
145
- "required_fields": ["type", "translation_id", "source_path", "source_hash", "locale", "status", "translated_fields"],
146
- "optional_fields": ["translated_title", "translated_description", "translator", "reviewed_at"],
147
- "immutable_fields": ["translation_id", "source_path", "locale"],
145
+ "required_fields": ["type", "primitive_id", "namespace", "version", "name", "mutation", "portability", "scopes", "fields"],
146
+ "optional_fields": ["language", "aliases", "path_template", "capabilities", "events", "projections", "ui", "migrations", "requires"],
147
+ "immutable_fields": ["primitive_id", "namespace"],
148
148
  "enums": {
149
- "status": ["draft", "reviewed", "approved"],
150
- "translated_fields": ["title", "description", "body"]
149
+ "mutation": ["replace", "append"],
150
+ "portability": ["structural", "tenant_private", "resource_bound"],
151
+ "scopes": ["system", "account", "workspace", "user"]
151
152
  },
152
153
  "patterns": {
153
- "translation_id": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$",
154
- "locale": "^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$",
155
- "source_hash": "^sha256:[a-f0-9]{64}$"
156
- },
157
- "references": {
158
- "source_path": {
159
- "must_exist": true,
160
- "disallowed_types": ["translation"],
161
- "allowed_portability": ["structural"],
162
- "hash_field": "source_hash"
163
- }
154
+ "primitive_id": "^[a-z][a-z0-9_-]{0,63}:[a-z][a-z0-9_-]{0,127}$",
155
+ "namespace": "^[a-z][a-z0-9_-]{0,63}$",
156
+ "language": "^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$"
164
157
  },
165
- "notes": "A non-destructive presentation overlay. Only title, description, and body may be translated; symbolic control fields remain byte-for-byte sourced from the referenced structural document.",
166
- "spec_ref": "§5.4 translation"
158
+ "notes": "A governed, versioned definition for a repository, extension, workspace, or user primitive. Natural-language labels may use any language; stable field, enum, capability, and action identifiers are never translated.",
159
+ "spec_ref": "§5.4 primitive"
167
160
  }
168
161
  },
169
162
 
@@ -178,7 +171,7 @@
178
171
  "universal_frontmatter": {
179
172
  "required": ["type", "title", "description", "timestamp"],
180
173
  "recommended_okf": ["resource", "tags", "aliases"],
181
- "recommended_semantic": ["semantic_id", "resource", "tags", "aliases", "relations", "locale"],
174
+ "recommended_semantic": ["semantic_id", "resource", "tags", "aliases", "relations", "language"],
182
175
  "spec_ref": "§4.2"
183
176
  },
184
177
 
@@ -212,7 +205,7 @@
212
205
  "required": ["key", "label", "type", "scope", "source"],
213
206
  "optional": ["required", "description", "defaultValue", "options", "dependsOn"],
214
207
  "source_enum": ["user", "llm", "graph", "system"],
215
- "note": "Adopted verbatim from ultrachat WorkspaceTemplateVariableDefinition; resource_bound requirements ship as parameters of source user/system."
208
+ "note": "resource_bound requirements ship as parameters of source user/system."
216
209
  },
217
210
  "conformance": "A host MUST refuse a bundle whose `ssss_core_version` it does not support or that names a `required_extensions` entry it has not loaded. An exporter MUST NOT emit a file whose resolved portability class is excluded by `export_profile`. `primitive_inventory` MUST count every file by its registry `type`.",
218
211
  "spec_ref": "§16"
@@ -253,13 +246,13 @@
253
246
  "identity_fields": ["semantic_id", "resource", "type", "path"],
254
247
  "surface_fields": ["title", "description", "name", "slug", "aliases", "tags"],
255
248
  "relation_fields": ["relations", "wiki_links", "markdown_links"],
256
- "translatable_fields": ["title", "description", "body"],
257
- "locale_pattern": "^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$",
258
- "safe_default": "Semantic indexes exclude tenant_private and resource_bound documents unless include_private is explicitly enabled. Translation overlays may reference structural sources only.",
249
+ "renderable_fields": ["title", "description", "body"],
250
+ "language_pattern": "^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$",
251
+ "safe_default": "Semantic adapters exclude tenant_private and resource_bound documents unless an authorized context explicitly includes them. Runtime rendering never changes symbolic controls.",
259
252
  "spec_ref": "§11.9"
260
253
  },
261
254
 
262
- "reserved_keys": ["type", "slug", "schema_version", "status", "feedback", "confidence", "semantic_id", "locale", "translation_of", "translation_source_hash"],
255
+ "reserved_keys": ["type", "slug", "schema_version", "status", "feedback", "confidence", "semantic_id", "language", "relations"],
263
256
  "host_field_prefix": "x_",
264
257
  "spec_ref_reserved": "Appendix A"
265
258
  }
@@ -15,7 +15,6 @@ import os from 'node:os';
15
15
  import path from 'node:path';
16
16
  import { fileURLToPath } from 'node:url';
17
17
  import { exportBundle, validateBundle } from '../src/bundle.mjs';
18
- import { documentHash } from '../src/registry.mjs';
19
18
  import { generateIndexes } from './autolink.mjs';
20
19
 
21
20
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -113,25 +112,6 @@ status: pending
113
112
  Attendee jane@example.com requested a seat change.`,
114
113
  };
115
114
 
116
- // Structural translation overlay — hash-bound to the exact source document and
117
- // applied only by the semantic/localization projection (§11.9).
118
- VAULT['translations/es/rules/refund-policy.md'] =
119
- `---
120
- type: translation
121
- title: Spanish Refund Policy Translation
122
- description: Approved Spanish presentation overlay for the festival refund policy.
123
- timestamp: 2026-07-10T00:00:00Z
124
- translation_id: refund-policy-es
125
- source_path: rules/refund-policy.md
126
- source_hash: ${documentHash(VAULT['rules/refund-policy.md'])}
127
- locale: es
128
- status: approved
129
- translated_fields: [title, description, body]
130
- translated_title: Política de reembolsos
131
- translated_description: Explica cuándo están disponibles los reembolsos para asistentes.
132
- ---
133
- Los reembolsos están disponibles hasta catorce días antes del evento.`;
134
-
135
115
  function buildVault() {
136
116
  const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ssss-refvault-'));
137
117
  const files = [];
@@ -0,0 +1,29 @@
1
+ /** Machine-readable reference adapter conformance report. */
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { MemoryVfs, FileSystemVfs, runVfsContract } from '../src/vfs.mjs';
6
+ import { MemoryLeaseStore, FileLeaseStore, runLeaseContract } from '../src/leases.mjs';
7
+ import { MemoryIdempotencyStore, FileIdempotencyStore, runIdempotencyContract } from '../src/idempotency.mjs';
8
+ import { parseArgs, die, wantsHelp } from './lib/cli.mjs';
9
+
10
+ export async function run(argv) {
11
+ if (wantsHelp(argv)) { console.log('Usage: ssss adapter conformance [--out <file>]'); return; }
12
+ if (argv[0] !== 'conformance') die('Expected adapter conformance.');
13
+ const { flags } = parseArgs(argv.slice(1));
14
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ssss-adapters-'));
15
+ try {
16
+ const suites = {
17
+ vfs_memory: await runVfsContract(new MemoryVfs()),
18
+ vfs_filesystem: await runVfsContract(new FileSystemVfs(path.join(root, 'vfs'))),
19
+ lease_memory: await runLeaseContract(new MemoryLeaseStore()),
20
+ lease_filesystem: await runLeaseContract(new FileLeaseStore(path.join(root, 'leases'))),
21
+ idempotency_memory: await runIdempotencyContract(new MemoryIdempotencyStore()),
22
+ idempotency_filesystem: await runIdempotencyContract(new FileIdempotencyStore(path.join(root, 'idempotency'))),
23
+ };
24
+ const report = { version: 1, passed: Object.values(suites).every((suite) => suite.passed), suites };
25
+ const text = `${JSON.stringify(report, null, 2)}\n`;
26
+ if (flags.out) fs.writeFileSync(path.resolve(flags.out), text, { flag: 'wx', mode: 0o600 }); else process.stdout.write(text);
27
+ if (!report.passed) process.exitCode = 1;
28
+ } finally { fs.rmSync(root, { recursive: true, force: true }); }
29
+ }
@@ -61,7 +61,7 @@ export async function run(argv) {
61
61
  }
62
62
 
63
63
  const engine = createEngine({ registryDir: flags.registry });
64
- const result = importBundle(envelopes, vault, engine);
64
+ const result = await importBundle(envelopes, vault, engine);
65
65
  const failed = result.results.filter((r) => !r.success);
66
66
  if (flags['dry-run']) {
67
67
  console.log(`${result.ok ? '✓' : '✗'} import dry-run — ${result.wouldCommit || 0} would commit, ${envelopes.length - (result.wouldCommit || 0)} unchanged (idempotent), ${failed.length} failed → ${vault}`);
@@ -0,0 +1,26 @@
1
+ /** Non-destructive 0.8 -> 0.9 migration diagnostics and backup manifest. */
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import crypto from 'node:crypto';
5
+ import { parseArgs, die, wantsHelp } from './lib/cli.mjs';
6
+
7
+ function walk(root, dir = root, out = []) {
8
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
9
+ const absolute = path.join(dir, entry.name);
10
+ if (entry.isSymbolicLink()) continue;
11
+ if (entry.isDirectory()) walk(root, absolute, out); else if (entry.isFile()) out.push(path.relative(root, absolute).split(path.sep).join('/'));
12
+ }
13
+ return out.sort();
14
+ }
15
+ export async function run(argv) {
16
+ if (wantsHelp(argv)) { console.log('Usage: ssss migrate 0.8-to-0.9 <vault> [--out <report.json>]'); return; }
17
+ if (argv[0] !== '0.8-to-0.9' || !argv[1]) die('Expected migrate 0.8-to-0.9 <vault>.');
18
+ const { flags } = parseArgs(argv.slice(2));
19
+ const root = fs.realpathSync(path.resolve(argv[1]));
20
+ const files = walk(root);
21
+ const translationArtifacts = files.filter((file) => /(^|\/)translations?\//i.test(file) || /translation/i.test(fs.readFileSync(path.join(root, file), 'utf8').slice(0, 2048)));
22
+ const backup = files.map((file) => ({ path: file, hash: `sha256:${crypto.createHash('sha256').update(fs.readFileSync(path.join(root, file))).digest('hex')}` }));
23
+ const report = { migration: '0.8-to-0.9', dry_run: true, source: root, backup_manifest: backup, diagnostics: { translation_artifacts: translationArtifacts, required_actions: translationArtifacts.length ? ['Preserve canonical authored content; archive obsolete translation overlays outside the vault.'] : [] }, writes_performed: 0 };
24
+ const text = `${JSON.stringify(report, null, 2)}\n`;
25
+ if (flags.out) fs.writeFileSync(path.resolve(flags.out), text, { flag: 'wx', mode: 0o600 }); else process.stdout.write(text);
26
+ }
@@ -160,12 +160,12 @@ const require = createRequire(import.meta.url);
160
160
  const { fixtures } = require('@gregiteen/ssss-cli/conformance/fixtures.json');
161
161
  const VAULT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'vault');
162
162
 
163
- test('engine implements the canonical Operation Contract (spec §6)', () => {
163
+ test('engine implements the canonical Operation Contract (spec §6)', async () => {
164
164
  const vault = fs.mkdtempSync(path.join(os.tmpdir(), 'ssss-conf-'));
165
165
  const engine = createEngine();
166
166
  try {
167
167
  for (const f of fixtures) {
168
- const res = engine.processOperation(JSON.parse(JSON.stringify(f.request)), vault);
168
+ const res = await engine.processOperation(JSON.parse(JSON.stringify(f.request)), vault);
169
169
  const exp = f.expected_response;
170
170
  if (exp.success !== undefined) assert.equal(res.success, exp.success, f.id + ' success');
171
171
  if (exp.validation && exp.validation.valid !== undefined) {
@@ -175,7 +175,7 @@ test('engine implements the canonical Operation Contract (spec §6)', () => {
175
175
  } finally { fs.rmSync(vault, { recursive: true, force: true }); }
176
176
  });
177
177
 
178
- test('this vault exports, validates, and round-trips as a sale bundle (spec §16/§17)', () => {
178
+ test('this vault exports, validates, and round-trips as a sale bundle (spec §16/§17)', async () => {
179
179
  const bundle = exportBundle(VAULT, { profile: 'sale', name: 'starter' });
180
180
  const { valid, errors } = validateBundle(bundle);
181
181
  assert.ok(valid, 'bundle invalid: ' + errors.join('; '));
@@ -187,9 +187,9 @@ test('this vault exports, validates, and round-trips as a sale bundle (spec §16
187
187
  const target = fs.mkdtempSync(path.join(os.tmpdir(), 'ssss-import-'));
188
188
  try {
189
189
  const engine = createEngine();
190
- const r1 = importBundle(plan.plan, target, engine);
190
+ const r1 = await importBundle(plan.plan, target, engine);
191
191
  assert.ok(r1.ok && r1.committed === bundle.files.length, 'import did not commit every file');
192
- const r2 = importBundle(plan.plan, target, engine);
192
+ const r2 = await importBundle(plan.plan, target, engine);
193
193
  assert.equal(r2.committed, 0, 're-import was not idempotent');
194
194
  } finally { fs.rmSync(target, { recursive: true, force: true }); }
195
195
  });
@@ -0,0 +1,48 @@
1
+ /** Primitive authoring and inspection commands for SSSS 0.9. */
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { definePrimitive, definitionsToExtensionRegistry } from '../src/primitive.mjs';
5
+ import { parseArgs, die, wantsHelp } from './lib/cli.mjs';
6
+
7
+ const HELP = `Usage: ssss primitive create|validate|inspect|migrate [file] [options]\n\n` +
8
+ `create: --namespace <id> --name <text> [--language <tag>] [--fields <json>] [--out <file>]\n` +
9
+ `validate|inspect: <definition.json> [--typescript] [--out <file>]\n` +
10
+ `migrate: <definition.json> --to-version <n> [--out <file>]`;
11
+
12
+ function readJson(file) { return JSON.parse(fs.readFileSync(path.resolve(file), 'utf8')); }
13
+ function emit(value, output) {
14
+ const text = typeof value === 'string' ? value : `${JSON.stringify(value, null, 2)}\n`;
15
+ if (output) fs.writeFileSync(path.resolve(output), text, { flag: 'wx', mode: 0o600 });
16
+ else process.stdout.write(text);
17
+ }
18
+ function typeScript(definition) {
19
+ const name = definition.primitive_id.replace(/[^A-Za-z0-9]+/g, '_').replace(/^\d/, '_$&');
20
+ const fields = definition.fields.map((field) => {
21
+ const kind = field.kind === 'boolean' ? 'boolean' : field.kind === 'number' ? 'number' : field.kind === 'enum' ? field.values.map(JSON.stringify).join(' | ') : 'string';
22
+ return ` ${JSON.stringify(field.id)}${field.required ? '' : '?'}: ${kind};`;
23
+ }).join('\n');
24
+ return `export interface ${name} {\n type: ${JSON.stringify(definition.primitive_id)};\n${fields}\n}\n`;
25
+ }
26
+
27
+ export async function run(argv) {
28
+ if (wantsHelp(argv)) { console.log(HELP); return; }
29
+ const verb = argv[0];
30
+ const { positionals, flags } = parseArgs(argv.slice(1), { booleans: ['typescript'] });
31
+ try {
32
+ if (verb === 'create') {
33
+ if (!flags.namespace || !flags.name) die(`create requires --namespace and --name.\n${HELP}`);
34
+ const definition = definePrimitive({ namespace: flags.namespace, name: flags.name, language: flags.language, fields: flags.fields ? JSON.parse(flags.fields) : [] });
35
+ emit(definition, flags.out); return;
36
+ }
37
+ if (!positionals[0]) die(`A definition file is required.\n${HELP}`);
38
+ const definition = definePrimitive(readJson(positionals[0]));
39
+ if (verb === 'validate') { emit({ valid: true, primitive_id: definition.primitive_id }, flags.out); return; }
40
+ if (verb === 'inspect') { emit(flags.typescript ? typeScript(definition) : { definition, registry: definitionsToExtensionRegistry(definition.namespace, [definition]) }, flags.out); return; }
41
+ if (verb === 'migrate') {
42
+ const version = Number(flags['to-version']);
43
+ if (!Number.isInteger(version) || version <= definition.version) die('--to-version must be an integer greater than the current version.');
44
+ emit({ ...definition, version, revision: 1, migrations: [...(definition.migrations || []), { from: definition.version, to: version, strategy: 'explicit' }] }, flags.out); return;
45
+ }
46
+ die(`Unknown primitive verb '${verb || ''}'.\n${HELP}`);
47
+ } catch (error) { die(error.message); }
48
+ }
@@ -0,0 +1,25 @@
1
+ /** Effective registry composition and integrity-lock commands. */
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { composeRegistryLayers, createRegistryLock, loadRegistries, verifyRegistryLock } from '../src/registry.mjs';
5
+ import { parseArgs, die, wantsHelp } from './lib/cli.mjs';
6
+
7
+ const HELP = 'Usage: ssss registry compose|lock|verify [--extension <file> ...] [--lock <file>] [--out <file>]';
8
+ const read = (file) => JSON.parse(fs.readFileSync(path.resolve(file), 'utf8'));
9
+ function serializable(set) { return { primitives: Object.fromEntries(set.primitives), aliases: Object.fromEntries(set.aliases), extension_versions: Object.fromEntries(set.extensionVersions) }; }
10
+ export async function run(argv) {
11
+ if (wantsHelp(argv)) { console.log(HELP); return; }
12
+ const verb = argv[0];
13
+ const { flags } = parseArgs(argv.slice(1), { multi: ['extension'] });
14
+ try {
15
+ const base = loadRegistries();
16
+ const set = composeRegistryLayers({ core: base.core, repository: (flags.extension || []).map(read) });
17
+ let result;
18
+ if (verb === 'compose') result = serializable(set);
19
+ else if (verb === 'lock') result = createRegistryLock(set);
20
+ else if (verb === 'verify') { if (!flags.lock) die('verify requires --lock <file>.'); result = verifyRegistryLock(set, read(flags.lock)); }
21
+ else die(`Unknown registry verb '${verb || ''}'.\n${HELP}`);
22
+ const text = `${JSON.stringify(result, null, 2)}\n`;
23
+ if (flags.out) fs.writeFileSync(path.resolve(flags.out), text, { flag: 'wx', mode: 0o600 }); else process.stdout.write(text);
24
+ } catch (error) { die(error.message); }
25
+ }