@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.
- package/CHANGELOG.md +55 -4
- package/README.md +42 -84
- package/VERSION +1 -1
- package/conformance/README.md +1 -1
- package/conformance/fixtures.json +4 -48
- package/conformance/reference-bundle.ucw.json +22 -21
- package/docs/help/bundle.md +2 -2
- package/docs/help/overview.md +9 -6
- package/docs/help/semantic.md +5 -4
- package/docs/ssss-spec.md +42 -86
- package/package.json +22 -3
- package/registry/core.json +22 -29
- package/scripts/build-reference-bundle.mjs +0 -20
- package/scripts/cmd-adapter.mjs +29 -0
- package/scripts/cmd-import.mjs +1 -1
- package/scripts/cmd-migrate.mjs +26 -0
- package/scripts/cmd-new.mjs +5 -5
- package/scripts/cmd-primitive.mjs +48 -0
- package/scripts/cmd-registry.mjs +25 -0
- package/scripts/cmd-semantic.mjs +6 -8
- package/scripts/conformance-09.mjs +331 -0
- package/scripts/conformance.mjs +153 -186
- package/scripts/generate-sbom.mjs +96 -0
- package/scripts/phase9-cross-host-corpus.mjs +223 -0
- package/scripts/ssss.mjs +8 -2
- package/skills/ssss/SKILL.md +32 -208
- package/skills/ssss/evals/evals.json +3 -3
- package/skills/ssss/references/contract-reference.md +7 -8
- package/skills/ssss/scripts/validate-schema.mjs +1 -44
- package/src/authorization.mjs +62 -0
- package/src/bundle.mjs +41 -5
- package/src/engine.mjs +101 -503
- package/src/events.mjs +155 -0
- package/src/guard.mjs +32 -0
- package/src/http.mjs +26 -0
- package/src/idempotency.mjs +55 -0
- package/src/kernel.mjs +260 -0
- package/src/leases.mjs +173 -0
- package/src/primitive.mjs +227 -0
- package/src/projections.mjs +133 -0
- package/src/registry.mjs +190 -39
- package/src/semantic.mjs +124 -316
- package/src/ui.mjs +100 -0
- package/src/validator.mjs +87 -0
- package/src/vfs.mjs +203 -0
- package/types/kernel.d.ts +48 -0
- package/docs/help/localization.md +0 -43
- 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.
|
|
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.
|
|
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: **
|
|
11
|
-
>
|
|
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
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
| `
|
|
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`, `
|
|
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
|
-
#### `
|
|
565
|
+
#### `primitive`
|
|
565
566
|
|
|
566
|
-
A
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
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
|
|
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,
|
|
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.
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
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
|
|
1165
|
-
edges,
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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`,
|
|
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.
|
|
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
|
-
"
|
|
54
|
-
"
|
|
71
|
+
"multilingual",
|
|
72
|
+
"generative-ui",
|
|
73
|
+
"application-kernel"
|
|
55
74
|
],
|
|
56
75
|
"author": "Greg Iteen",
|
|
57
76
|
"license": "ISC",
|
package/registry/core.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://ssss.dev/registry/v1",
|
|
3
3
|
"registry": "core",
|
|
4
|
-
"spec_version": "0.
|
|
5
|
-
"description": "The canonical SSSS core
|
|
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
|
-
"
|
|
141
|
-
"family": "
|
|
142
|
-
"canonical_path": "
|
|
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", "
|
|
146
|
-
"optional_fields": ["
|
|
147
|
-
"immutable_fields": ["
|
|
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
|
-
"
|
|
150
|
-
"
|
|
149
|
+
"mutation": ["replace", "append"],
|
|
150
|
+
"portability": ["structural", "tenant_private", "resource_bound"],
|
|
151
|
+
"scopes": ["system", "account", "workspace", "user"]
|
|
151
152
|
},
|
|
152
153
|
"patterns": {
|
|
153
|
-
"
|
|
154
|
-
"
|
|
155
|
-
"
|
|
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
|
|
166
|
-
"spec_ref": "§5.4
|
|
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", "
|
|
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": "
|
|
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
|
-
"
|
|
257
|
-
"
|
|
258
|
-
"safe_default": "Semantic
|
|
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", "
|
|
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
|
+
}
|
package/scripts/cmd-import.mjs
CHANGED
|
@@ -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
|
+
}
|
package/scripts/cmd-new.mjs
CHANGED
|
@@ -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
|
+
}
|