@gregiteen/ssss-cli 0.7.1 → 0.8.0
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 +42 -4
- package/README.md +20 -4
- package/VERSION +1 -1
- package/conformance/README.md +11 -7
- package/conformance/fixtures.json +68 -2
- package/conformance/reference-bundle.ucw.json +21 -4
- package/docs/help/bundle.md +6 -2
- package/docs/help/import.md +2 -0
- package/docs/help/localization.md +43 -0
- package/docs/help/overview.md +8 -4
- package/docs/help/semantic.md +37 -0
- package/docs/ssss-spec.md +112 -7
- package/package.json +8 -3
- package/registry/core.json +43 -3
- package/scripts/audit-registry-field-usage.mjs +10 -1
- package/scripts/build-reference-bundle.mjs +24 -1
- package/scripts/cmd-export.mjs +3 -3
- package/scripts/cmd-localize.mjs +44 -0
- package/scripts/cmd-semantic.mjs +88 -0
- package/scripts/conformance.mjs +374 -2
- package/scripts/ssss.mjs +4 -0
- package/skills/ssss/SKILL.md +225 -0
- package/skills/ssss/evals/evals.json +37 -0
- package/skills/ssss/references/contract-reference.md +96 -0
- package/skills/ssss/scripts/validate-schema.mjs +258 -0
- package/skills/ssss/subagents/audit-ssss-contract.md +22 -0
- package/src/bundle.mjs +170 -34
- package/src/engine.mjs +78 -4
- package/src/registry.mjs +246 -6
- package/src/semantic.mjs +402 -0
package/docs/ssss-spec.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SSSS — Structured Semantic Syntax System
|
|
2
2
|
|
|
3
|
-
**Specification v0.
|
|
3
|
+
**Specification v0.7 — Draft**
|
|
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
|
|
@@ -31,6 +31,10 @@ SSSS additionally defines the **Operation Contract**: a single, validated, idemp
|
|
|
31
31
|
envelope through which all agent-generated mutations must flow. This makes
|
|
32
32
|
AI-generated state changes deterministic, replayable, conflict-safe, and auditable.
|
|
33
33
|
|
|
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.
|
|
37
|
+
|
|
34
38
|
Any tool, IDE, agent framework, daemon, or CLI that can read Markdown can
|
|
35
39
|
interoperate with an SSSS vault. The only thing that distinguishes a memory engine,
|
|
36
40
|
a chat runtime, and a workflow orchestrator is *which primitive types they read and
|
|
@@ -124,6 +128,17 @@ For OKF-compatible discovery, SSSS document primitives SHOULD also include:
|
|
|
124
128
|
| `tags` | array | Array of string tags for categorization and routing. |
|
|
125
129
|
| `aliases` | array | Array of alternative names for this concept, used for wiki autolinking. |
|
|
126
130
|
|
|
131
|
+
For stable semantic identity and graph projection, documents SHOULD also include:
|
|
132
|
+
|
|
133
|
+
| Field | Type | Description |
|
|
134
|
+
|-------|------|-------------|
|
|
135
|
+
| `semantic_id` | string | Stable concept identity that survives file moves and localized presentation. |
|
|
136
|
+
| `relations` | array | Explicit semantic edges to other vault-relative document paths or semantic ids. |
|
|
137
|
+
| `locale` | string | BCP-47-style locale of the authored natural-language surface, when known. |
|
|
138
|
+
|
|
139
|
+
`resource`, `tags`, and `aliases` serve both OKF discovery and the semantic layer.
|
|
140
|
+
These fields are recommendations, not new universal requirements.
|
|
141
|
+
|
|
127
142
|
Every primitive type defines its own additional REQUIRED and OPTIONAL fields
|
|
128
143
|
(§5.4). To maintain forward compatibility and allow agents scratchpad space, hosts MUST NOT reject documents containing unknown frontmatter keys. Unknown fields MUST be preserved.
|
|
129
144
|
|
|
@@ -180,6 +195,7 @@ primitive — it implements the subset its product requires — but any primitiv
|
|
|
180
195
|
| `page` | capability | structural | A VFS-native sandboxed custom workspace page. |
|
|
181
196
|
| `migration` | meta | structural | An SSSS schema migration state record. |
|
|
182
197
|
| `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. |
|
|
183
199
|
|
|
184
200
|
The **Portability** column is the primitive's default portability class (§5.5); a host
|
|
185
201
|
resolves it from `registry/core.json`. Extension registries assign their own primitives a
|
|
@@ -201,7 +217,7 @@ Document primitives are either:
|
|
|
201
217
|
|
|
202
218
|
- **Replace-type** — the whole file represents current state; a write replaces it
|
|
203
219
|
entirely (`memory`, `skill`, `assistant`, `workflow`, `model`, `task`,
|
|
204
|
-
`conflict`).
|
|
220
|
+
`conflict`, `translation`).
|
|
205
221
|
- **Append-type** — the file is an ordered, append-only log; writes add records to
|
|
206
222
|
the body and MUST NOT rewrite prior records (`conversation`, `run`).
|
|
207
223
|
|
|
@@ -545,6 +561,49 @@ released_at: 2026-05-16T12:00:00Z
|
|
|
545
561
|
---
|
|
546
562
|
```
|
|
547
563
|
|
|
564
|
+
#### `translation`
|
|
565
|
+
|
|
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).
|
|
606
|
+
|
|
548
607
|
### 5.5 Portability Classification
|
|
549
608
|
|
|
550
609
|
A workspace is not one undifferentiated blob. Some of it is the **reusable business
|
|
@@ -1042,11 +1101,50 @@ run, plus an event explaining the terminal failure.
|
|
|
1042
1101
|
|
|
1043
1102
|
#### Runtime Helpers
|
|
1044
1103
|
|
|
1045
|
-
The reference package exposes `@ssss
|
|
1104
|
+
The reference package exposes `@gregiteen/ssss-cli/runtime`, including deterministic helpers
|
|
1046
1105
|
for planning a workflow trigger into a `workflow_triggered` event envelope and a
|
|
1047
1106
|
task `operation` envelope. Hosts MAY implement their own daemon, but SHOULD pass the
|
|
1048
1107
|
runtime conformance checks to prove the same source-of-truth and idempotency rules.
|
|
1049
1108
|
|
|
1109
|
+
### 11.9 Semantic Projection & Localization Contract
|
|
1110
|
+
|
|
1111
|
+
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
|
|
1113
|
+
options. It MUST NOT become a second source of truth.
|
|
1114
|
+
|
|
1115
|
+
Each projected record SHOULD expose a stable identity, source path and content hash,
|
|
1116
|
+
primitive type, portability class, surface text, normalized search tokens, and graph
|
|
1117
|
+
edges. Identity is resolved from `semantic_id`, then `resource`, then the source path.
|
|
1118
|
+
Edges are derived from explicit `relations`, wiki links, and Markdown links. Hosts MAY
|
|
1119
|
+
add embeddings or richer ranking, but the exact lexical/graph projection MUST remain
|
|
1120
|
+
available as a dependency-free interoperability baseline.
|
|
1121
|
+
|
|
1122
|
+
The safe default projection contains only `structural` documents. It MUST exclude
|
|
1123
|
+
`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.
|
|
1147
|
+
|
|
1050
1148
|
---
|
|
1051
1149
|
|
|
1052
1150
|
## 12. Conformance
|
|
@@ -1061,6 +1159,11 @@ Fixtures are distributed as a JSON document carrying:
|
|
|
1061
1159
|
- `idempotency` — TTL and replay behavior.
|
|
1062
1160
|
- `validation_rules` — envelope and content rules.
|
|
1063
1161
|
- `runtime_contract` — trigger vocabulary and daemon idempotency rules (§11.8).
|
|
1162
|
+
- registry-extension checks — schema shape, regex validity, symlink rejection, and
|
|
1163
|
+
core/sibling collision rejection.
|
|
1164
|
+
- semantic/localization checks — deterministic indexing, privacy defaults, graph
|
|
1165
|
+
edges, exact source hashes, immutable overlay identity, and safe materialization
|
|
1166
|
+
(§11.9).
|
|
1064
1167
|
- `fixtures[]` — each with a `request`, an `expected_response`, and an
|
|
1065
1168
|
OPTIONAL `expected_http_status`.
|
|
1066
1169
|
- `error_codes` — the canonical code table.
|
|
@@ -1068,7 +1171,8 @@ Fixtures are distributed as a JSON document carrying:
|
|
|
1068
1171
|
The conformance fixture set is the shared test contract between all SSSS
|
|
1069
1172
|
implementations. A host MUST NOT claim SSSS conformance without passing the current
|
|
1070
1173
|
fixture set. Hosts implementing workflow daemons SHOULD also run the reference
|
|
1071
|
-
runtime checks exposed by `@ssss
|
|
1174
|
+
runtime checks exposed by `@gregiteen/ssss-cli/runtime`. Hosts exposing semantic
|
|
1175
|
+
search or localized projections MUST also pass the §11.9 checks.
|
|
1072
1176
|
|
|
1073
1177
|
---
|
|
1074
1178
|
|
|
@@ -1096,7 +1200,7 @@ opaque ID as the slug and keep the human-readable label in `title` / `name`.
|
|
|
1096
1200
|
This document is versioned independently of any host and of the conformance
|
|
1097
1201
|
fixture set.
|
|
1098
1202
|
|
|
1099
|
-
- The spec version is stated in the document header (currently **v0.
|
|
1203
|
+
- The spec version is stated in the document header (currently **v0.7 — Draft**).
|
|
1100
1204
|
- Breaking changes to the file format, the type registry, or the Operation
|
|
1101
1205
|
Contract increment the spec version.
|
|
1102
1206
|
- Until **v1.0**, any version MAY introduce breaking changes.
|
|
@@ -1208,7 +1312,7 @@ them without a spec change.
|
|
|
1208
1312
|
serialization of `files` (path-sorted, §16.1). An importer MUST recompute it and reject
|
|
1209
1313
|
a bundle whose files do not match. This is what makes a bundle tamper-evident and what a
|
|
1210
1314
|
marketplace lists against.
|
|
1211
|
-
- `exporter` — an identifier for the tool/host that produced the bundle (`"@ssss
|
|
1315
|
+
- `exporter` — an identifier for the tool/host that produced the bundle (`"@gregiteen/ssss-cli@0.8.0"`).
|
|
1212
1316
|
- `signature` — OPTIONAL detached signature over `content_hash` for a sold bundle, so a
|
|
1213
1317
|
buyer can verify authorship. Unsigned bundles are valid; signing is a marketplace concern.
|
|
1214
1318
|
|
|
@@ -1313,7 +1417,8 @@ cannot resolve a link MUST fail the provision rather than emit a dangling refere
|
|
|
1313
1417
|
|
|
1314
1418
|
The following frontmatter keys are reserved by this spec across all primitives and
|
|
1315
1419
|
MUST NOT be repurposed by hosts: `type`, `slug`, `schema_version`, `status`,
|
|
1316
|
-
`feedback`, `confidence
|
|
1420
|
+
`feedback`, `confidence`, `semantic_id`, `relations`, `locale`, `translation_id`,
|
|
1421
|
+
`source_path`, `source_hash`, and `translated_fields`.
|
|
1317
1422
|
|
|
1318
1423
|
Hosts adding their own frontmatter fields SHOULD prefix them `x_` to remain
|
|
1319
1424
|
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.8.0",
|
|
4
4
|
"description": "CLI tools and the reference engine for the Structured Semantic Syntax System",
|
|
5
5
|
"main": "src/engine.mjs",
|
|
6
6
|
"exports": {
|
|
@@ -10,13 +10,14 @@
|
|
|
10
10
|
"./registry": "./src/registry.mjs",
|
|
11
11
|
"./frontmatter": "./src/frontmatter.mjs",
|
|
12
12
|
"./bundle": "./src/bundle.mjs",
|
|
13
|
+
"./semantic": "./src/semantic.mjs",
|
|
13
14
|
"./conformance/fixtures.json": "./conformance/fixtures.json",
|
|
14
15
|
"./conformance/reference-bundle.ucw.json": "./conformance/reference-bundle.ucw.json",
|
|
15
16
|
"./registry/core.json": "./registry/core.json",
|
|
16
17
|
"./package.json": "./package.json"
|
|
17
18
|
},
|
|
18
19
|
"bin": {
|
|
19
|
-
"ssss": "
|
|
20
|
+
"ssss": "scripts/ssss.mjs"
|
|
20
21
|
},
|
|
21
22
|
"type": "module",
|
|
22
23
|
"engines": {
|
|
@@ -29,6 +30,7 @@
|
|
|
29
30
|
"conformance",
|
|
30
31
|
"docs/ssss-spec.md",
|
|
31
32
|
"docs/help",
|
|
33
|
+
"skills/ssss",
|
|
32
34
|
"VERSION",
|
|
33
35
|
"README.md",
|
|
34
36
|
"CHANGELOG.md",
|
|
@@ -46,7 +48,10 @@
|
|
|
46
48
|
"vault",
|
|
47
49
|
"operation-contract",
|
|
48
50
|
"ucw",
|
|
49
|
-
"provisioning"
|
|
51
|
+
"provisioning",
|
|
52
|
+
"semantic-search",
|
|
53
|
+
"localization",
|
|
54
|
+
"translation"
|
|
50
55
|
],
|
|
51
56
|
"author": "Greg Iteen",
|
|
52
57
|
"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 primitive registry. These
|
|
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.",
|
|
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": {
|
|
@@ -136,6 +136,34 @@
|
|
|
136
136
|
"portability": "structural",
|
|
137
137
|
"required_fields": ["type", "release_id", "version", "schema_version", "summary", "released_at"],
|
|
138
138
|
"spec_ref": "§5.4 release"
|
|
139
|
+
},
|
|
140
|
+
"translation": {
|
|
141
|
+
"family": "localization",
|
|
142
|
+
"canonical_path": "translations/<locale>/<source-path>.md",
|
|
143
|
+
"append_only": false,
|
|
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"],
|
|
148
|
+
"enums": {
|
|
149
|
+
"status": ["draft", "reviewed", "approved"],
|
|
150
|
+
"translated_fields": ["title", "description", "body"]
|
|
151
|
+
},
|
|
152
|
+
"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
|
+
}
|
|
164
|
+
},
|
|
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"
|
|
139
167
|
}
|
|
140
168
|
},
|
|
141
169
|
|
|
@@ -150,6 +178,7 @@
|
|
|
150
178
|
"universal_frontmatter": {
|
|
151
179
|
"required": ["type", "title", "description", "timestamp"],
|
|
152
180
|
"recommended_okf": ["resource", "tags", "aliases"],
|
|
181
|
+
"recommended_semantic": ["semantic_id", "resource", "tags", "aliases", "relations", "locale"],
|
|
153
182
|
"spec_ref": "§4.2"
|
|
154
183
|
},
|
|
155
184
|
|
|
@@ -219,7 +248,18 @@
|
|
|
219
248
|
"spec_ref": "§11.8"
|
|
220
249
|
},
|
|
221
250
|
|
|
222
|
-
"
|
|
251
|
+
"semantic": {
|
|
252
|
+
"version": 1,
|
|
253
|
+
"identity_fields": ["semantic_id", "resource", "type", "path"],
|
|
254
|
+
"surface_fields": ["title", "description", "name", "slug", "aliases", "tags"],
|
|
255
|
+
"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.",
|
|
259
|
+
"spec_ref": "§11.9"
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
"reserved_keys": ["type", "slug", "schema_version", "status", "feedback", "confidence", "semantic_id", "locale", "translation_of", "translation_source_hash"],
|
|
223
263
|
"host_field_prefix": "x_",
|
|
224
264
|
"spec_ref_reserved": "Appendix A"
|
|
225
265
|
}
|
|
@@ -37,7 +37,15 @@ const ENGINE_FILES = [
|
|
|
37
37
|
// spec §6.3 + §9). If a primitive declares one, the engine source MUST
|
|
38
38
|
// reference it — otherwise the registry is making a promise the engine
|
|
39
39
|
// doesn't keep.
|
|
40
|
-
const ENGINE_ENFORCED_KEYS = [
|
|
40
|
+
const ENGINE_ENFORCED_KEYS = [
|
|
41
|
+
'required_fields',
|
|
42
|
+
'required_when',
|
|
43
|
+
'enums',
|
|
44
|
+
'patterns',
|
|
45
|
+
'immutable_fields',
|
|
46
|
+
'references',
|
|
47
|
+
'append_only',
|
|
48
|
+
];
|
|
41
49
|
|
|
42
50
|
// Keys that are real and load-bearing but are intentionally consumed
|
|
43
51
|
// elsewhere (export/bundle time, documentation, or the registry loader
|
|
@@ -75,6 +83,7 @@ function engineReferencesKey(engineSources, key) {
|
|
|
75
83
|
const patterns = [
|
|
76
84
|
new RegExp(`\\bdef\\.${key}\\b`),
|
|
77
85
|
new RegExp(`\\btypeDef\\.${key}\\b`),
|
|
86
|
+
new RegExp(`(?:\\.|\\?\\.)${key}\\b`),
|
|
78
87
|
new RegExp(`\\['${key}'\\]`),
|
|
79
88
|
new RegExp(`\\["${key}"\\]`),
|
|
80
89
|
];
|
|
@@ -15,10 +15,14 @@ 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';
|
|
18
19
|
import { generateIndexes } from './autolink.mjs';
|
|
19
20
|
|
|
20
21
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
21
22
|
const OUT = path.resolve(__dirname, '..', 'conformance', 'reference-bundle.ucw.json');
|
|
23
|
+
const PACKAGE = JSON.parse(
|
|
24
|
+
fs.readFileSync(path.resolve(__dirname, '..', 'package.json'), 'utf8')
|
|
25
|
+
);
|
|
22
26
|
|
|
23
27
|
// ─── The reference vault ────────────────────────────────────────────────────
|
|
24
28
|
const VAULT = {
|
|
@@ -109,6 +113,25 @@ status: pending
|
|
|
109
113
|
Attendee jane@example.com requested a seat change.`,
|
|
110
114
|
};
|
|
111
115
|
|
|
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
|
+
|
|
112
135
|
function buildVault() {
|
|
113
136
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ssss-refvault-'));
|
|
114
137
|
const files = [];
|
|
@@ -134,7 +157,7 @@ try {
|
|
|
134
157
|
name: 'Festival in a Box',
|
|
135
158
|
description: 'A complete music-festival operation as a tradeable SSSS bundle.',
|
|
136
159
|
version: '1.0.0',
|
|
137
|
-
exporter:
|
|
160
|
+
exporter: `${PACKAGE.name}@${PACKAGE.version}`,
|
|
138
161
|
requiredExtensions: ['festech'],
|
|
139
162
|
parameters: [
|
|
140
163
|
{ key: 'business_name', label: 'Festival name', type: 'string', scope: 'workspace', source: 'user', required: true },
|
package/scripts/cmd-export.mjs
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
|
-
import { exportBundle } from '../src/bundle.mjs';
|
|
10
|
+
import { DEFAULT_EXPORTER, exportBundle } from '../src/bundle.mjs';
|
|
11
11
|
import { parseArgs, usage, die, wantsHelp } from './lib/cli.mjs';
|
|
12
12
|
|
|
13
13
|
const HELP = usage({
|
|
@@ -19,7 +19,7 @@ const HELP = usage({
|
|
|
19
19
|
['--name <s>', 'Bundle display name.'],
|
|
20
20
|
['--description <s>', 'Bundle description.'],
|
|
21
21
|
['--bundle-version <s>', 'Bundle version string (default: 0.1.0).'],
|
|
22
|
-
['--exporter <s>',
|
|
22
|
+
['--exporter <s>', `Exporter id recorded in provenance (default: ${DEFAULT_EXPORTER}).`],
|
|
23
23
|
['--ext <name>', 'Declare a required extension registry (repeatable).'],
|
|
24
24
|
['--exported-at <iso>', 'Override the export timestamp (default: epoch, for determinism).'],
|
|
25
25
|
['--registry <dir>', 'Registry directory (default: the package core registry).'],
|
|
@@ -50,7 +50,7 @@ export async function run(argv) {
|
|
|
50
50
|
name: flags.name || path.basename(path.resolve(vault)),
|
|
51
51
|
description: flags.description || '',
|
|
52
52
|
version: flags['bundle-version'] || '0.1.0',
|
|
53
|
-
exporter: flags.exporter ||
|
|
53
|
+
exporter: flags.exporter || DEFAULT_EXPORTER,
|
|
54
54
|
requiredExtensions: flags.ext || [],
|
|
55
55
|
...(flags['exported-at'] ? { exportedAt: flags['exported-at'] } : {}),
|
|
56
56
|
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** `ssss localize` — materialize a non-destructive locale projection. */
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { materializeLocale } from '../src/semantic.mjs';
|
|
4
|
+
import { parseArgs, usage, die, wantsHelp } from './lib/cli.mjs';
|
|
5
|
+
|
|
6
|
+
const HELP = usage({
|
|
7
|
+
summary: 'Materialize approved translation overlays as a derived vault projection (spec §11.9).',
|
|
8
|
+
usage: 'ssss localize <vault-dir> --locale <tag> --out <empty-dir> [options]',
|
|
9
|
+
options: [
|
|
10
|
+
['--locale <tag>', 'Required BCP47-style target locale.'],
|
|
11
|
+
['--out <dir>', 'Required empty directory outside the source vault.'],
|
|
12
|
+
['--include-private', 'Explicitly include private/resource-bound source documents.'],
|
|
13
|
+
['--include-drafts', 'Allow non-approved translation overlays.'],
|
|
14
|
+
['--registry <dir>', 'Registry directory (default: package registry).'],
|
|
15
|
+
],
|
|
16
|
+
examples: [
|
|
17
|
+
'ssss localize ./vault --locale es --out ./derived/es',
|
|
18
|
+
],
|
|
19
|
+
seeAlso: ['ssss semantic', 'ssss help localization'],
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export async function run(argv) {
|
|
23
|
+
if (wantsHelp(argv)) { console.log(HELP); return; }
|
|
24
|
+
const { positionals, flags } = parseArgs(argv, {
|
|
25
|
+
booleans: ['include-private', 'include-drafts'],
|
|
26
|
+
});
|
|
27
|
+
const vault = positionals[0];
|
|
28
|
+
if (!vault || !flags.locale || !flags.out) die('localize requires a vault, --locale, and --out.\n\n' + HELP);
|
|
29
|
+
try {
|
|
30
|
+
const manifest = materializeLocale(
|
|
31
|
+
path.resolve(vault),
|
|
32
|
+
flags.locale,
|
|
33
|
+
path.resolve(flags.out),
|
|
34
|
+
{
|
|
35
|
+
includePrivate: !!flags['include-private'],
|
|
36
|
+
includeDrafts: !!flags['include-drafts'],
|
|
37
|
+
registryDir: flags.registry,
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
process.stdout.write(JSON.stringify(manifest, null, 2) + '\n');
|
|
41
|
+
} catch (error) {
|
|
42
|
+
die(error.message);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** `ssss semantic` — build/query a deterministic, privacy-safe semantic projection. */
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import crypto from 'node:crypto';
|
|
5
|
+
import { buildSemanticIndex, searchSemanticIndex } from '../src/semantic.mjs';
|
|
6
|
+
import { parseArgs, usage, die, wantsHelp } from './lib/cli.mjs';
|
|
7
|
+
|
|
8
|
+
const HELP = usage({
|
|
9
|
+
summary: 'Build or query the deterministic SSSS semantic index (spec §11.9).',
|
|
10
|
+
usage: 'ssss semantic <vault-dir> [options]',
|
|
11
|
+
options: [
|
|
12
|
+
['--query <text>', 'Return ranked matches instead of the entire index.'],
|
|
13
|
+
['--locale <tag>', 'Apply approved translation overlays for this locale.'],
|
|
14
|
+
['--type <name>', 'Restrict query results to a primitive type (repeatable).'],
|
|
15
|
+
['--limit <n>', 'Maximum query results (default: 10).'],
|
|
16
|
+
['--include-private', 'Explicitly include tenant_private/resource_bound documents.'],
|
|
17
|
+
['--include-drafts', 'Allow non-approved translation overlays.'],
|
|
18
|
+
['--registry <dir>', 'Registry directory (default: package registry).'],
|
|
19
|
+
['--out <file>', 'Write JSON outside the vault instead of stdout.'],
|
|
20
|
+
],
|
|
21
|
+
examples: [
|
|
22
|
+
'ssss semantic ./vault --query "refund policy"',
|
|
23
|
+
'ssss semantic ./vault --locale es --out ./derived/semantic-es.json',
|
|
24
|
+
],
|
|
25
|
+
seeAlso: ['ssss localize', 'ssss help semantic', 'ssss help localization'],
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
function physicalTarget(value) {
|
|
29
|
+
const requested = path.resolve(value);
|
|
30
|
+
let cursor = requested;
|
|
31
|
+
const suffix = [];
|
|
32
|
+
while (!fs.existsSync(cursor)) {
|
|
33
|
+
suffix.unshift(path.basename(cursor));
|
|
34
|
+
cursor = path.dirname(cursor);
|
|
35
|
+
}
|
|
36
|
+
return path.join(fs.realpathSync(cursor), ...suffix);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function writeOutsideVault(vault, output, content) {
|
|
40
|
+
const source = fs.realpathSync(path.resolve(vault));
|
|
41
|
+
const target = physicalTarget(output);
|
|
42
|
+
if (target === source || target.startsWith(source + path.sep)) {
|
|
43
|
+
throw new Error('Semantic projections must be written outside the source vault.');
|
|
44
|
+
}
|
|
45
|
+
if (fs.existsSync(target) && (fs.lstatSync(target).isSymbolicLink() || fs.statSync(target).isDirectory())) {
|
|
46
|
+
throw new Error(`Output must be a regular file: ${target}`);
|
|
47
|
+
}
|
|
48
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
49
|
+
const temporary = path.join(path.dirname(target), `.${path.basename(target)}.${crypto.randomUUID()}.tmp`);
|
|
50
|
+
fs.writeFileSync(temporary, content);
|
|
51
|
+
fs.renameSync(temporary, target);
|
|
52
|
+
return target;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function run(argv) {
|
|
56
|
+
if (wantsHelp(argv)) { console.log(HELP); return; }
|
|
57
|
+
const { positionals, flags } = parseArgs(argv, {
|
|
58
|
+
booleans: ['include-private', 'include-drafts'],
|
|
59
|
+
multi: ['type'],
|
|
60
|
+
});
|
|
61
|
+
const vault = positionals[0];
|
|
62
|
+
if (!vault) die('semantic needs a vault directory.\n\n' + HELP);
|
|
63
|
+
const limit = flags.limit === undefined ? 10 : Number(flags.limit);
|
|
64
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 1000) die('--limit must be an integer from 1 to 1000.');
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const index = buildSemanticIndex(path.resolve(vault), {
|
|
68
|
+
locale: flags.locale,
|
|
69
|
+
includePrivate: !!flags['include-private'],
|
|
70
|
+
includeDrafts: !!flags['include-drafts'],
|
|
71
|
+
registryDir: flags.registry,
|
|
72
|
+
});
|
|
73
|
+
const result = flags.query ? {
|
|
74
|
+
query: flags.query,
|
|
75
|
+
locale: index.locale,
|
|
76
|
+
include_private: index.include_private,
|
|
77
|
+
index_hash: index.index_hash,
|
|
78
|
+
results: searchSemanticIndex(index, flags.query, { types: flags.type, limit }),
|
|
79
|
+
} : index;
|
|
80
|
+
const json = JSON.stringify(result, null, 2) + '\n';
|
|
81
|
+
if (flags.out) {
|
|
82
|
+
const target = writeOutsideVault(vault, flags.out, json);
|
|
83
|
+
console.error(`Wrote semantic projection to ${target}`);
|
|
84
|
+
} else process.stdout.write(json);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
die(error.message);
|
|
87
|
+
}
|
|
88
|
+
}
|