@gregiteen/ssss-cli 0.9.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 +41 -82
- package/VERSION +1 -1
- package/conformance/reference-bundle.ucw.json +19 -1
- package/docs/help/overview.md +6 -3
- package/docs/ssss-spec.md +11 -10
- package/package.json +1 -1
- package/registry/core.json +1 -1
- package/scripts/conformance-09.mjs +139 -7
- package/scripts/conformance.mjs +47 -0
- package/scripts/generate-sbom.mjs +96 -0
- package/scripts/phase9-cross-host-corpus.mjs +223 -0
- package/skills/ssss/SKILL.md +32 -204
- package/src/authorization.mjs +2 -1
- package/src/bundle.mjs +37 -1
- package/src/events.mjs +74 -9
- package/src/leases.mjs +50 -2
- package/src/projections.mjs +72 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,57 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.9.1] - 2026-09-24
|
|
11
|
+
### Fixed
|
|
12
|
+
- `JsonlEventStore.append` no longer re-reads and re-parses the whole
|
|
13
|
+
`<workspace>.jsonl` log on every append to reject a duplicate `event_id`.
|
|
14
|
+
Each store instance reads a log once on its first append, keeps a set of the
|
|
15
|
+
event ids it has seen, and re-reads only when the log's stat (size, mtime,
|
|
16
|
+
ctime, inode) shows another instance or process has changed it. A batch of N
|
|
17
|
+
appends is now one log read instead of N. Replaying 3,250 appends in fresh
|
|
18
|
+
instances of 400 against a 7,915-event, 9.6 MB log dropped from ~280 s to
|
|
19
|
+
~1.7 s. Duplicate rejection, symlink refusal (now also enforced with
|
|
20
|
+
`O_NOFOLLOW` where the platform has it), 0600 log mode, and replay order are
|
|
21
|
+
unchanged.
|
|
22
|
+
- `MemoryEventStore.append` checks for duplicates against a set of ids instead
|
|
23
|
+
of scanning every stored event.
|
|
24
|
+
- The SBOM generator derives the package tarball URL from `package.json`
|
|
25
|
+
instead of a hardcoded 0.9.0 URL.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- 0.9 conformance checks for the JSONL event store: duplicates rejected from
|
|
29
|
+
the same instance, another instance, and another process; one log read for
|
|
30
|
+
200 appends; rebuild after a foreign append; replay order and cursor resume;
|
|
31
|
+
0600 mode; symlink refusal.
|
|
32
|
+
|
|
33
|
+
## [0.9.0] - 2026-07-10
|
|
34
|
+
### Added
|
|
35
|
+
- Shared application kernel (`kernel.execute`) with verified-principal injection,
|
|
36
|
+
canonical request hashing, idempotent replay/conflict, resource prepare/finalize/
|
|
37
|
+
reconcile hooks, and TypeScript command contracts under `@gregiteen/ssss-cli/kernel`.
|
|
38
|
+
- Host adapter contracts and dual reference implementations for VFS, identity/
|
|
39
|
+
capability authorization, leases, idempotency, events, projections, semantic
|
|
40
|
+
retrieval/rendering, and generative UI manifests.
|
|
41
|
+
- Runtime primitive authoring (`ssss:primitive` meta-schema), layered registry
|
|
42
|
+
composition, aliases, dependency ranges, policy floors, and registry integrity locks.
|
|
43
|
+
- CLI surfaces: `ssss primitive`, `ssss registry`, `ssss adapter conformance`, and
|
|
44
|
+
`ssss migrate 0.8-to-0.9` dry-run diagnostics with backup manifests.
|
|
45
|
+
- Bundle `manifest.dependencies` for primitives, extensions, migrations, and
|
|
46
|
+
integrity digests aligned with provenance.
|
|
47
|
+
- Dedicated 0.9 conformance suite covering adapters, kernel leases, projection
|
|
48
|
+
rebuild/drift isolation, UI adversarial cases, and CLI smoke paths.
|
|
49
|
+
|
|
50
|
+
### Changed
|
|
51
|
+
- Core registry/spec version is now `0.9`. Canonical documents are authored once;
|
|
52
|
+
multilingual presentation uses embedding/render adapters instead of stored
|
|
53
|
+
translation trees.
|
|
54
|
+
- The reference engine is a compatibility façade over the shared kernel; hosts must
|
|
55
|
+
inject adapters rather than re-implement mutation stages.
|
|
56
|
+
|
|
57
|
+
### Removed
|
|
58
|
+
- The `translation` document primitive and required localized-vault materialization
|
|
59
|
+
workflow from the 0.9 core surface.
|
|
60
|
+
|
|
10
61
|
## [0.8.0] - 2026-07-10
|
|
11
62
|
### Changed
|
|
12
63
|
- Core registry/spec version is now `0.7`. Registry `patterns`,
|
|
@@ -128,14 +179,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
128
179
|
- **The `.ucw` bundle format (spec §16, registry `bundle`)** — the canonical transportable
|
|
129
180
|
package of a vault: a manifest plus path-sorted files, content-hashed for integrity. The
|
|
130
181
|
manifest declares `ssss_core_version`, `required_extensions`, `export_profile`,
|
|
131
|
-
`primitive_inventory` (registry-driven, replacing
|
|
182
|
+
`primitive_inventory` (registry-driven, replacing the legacy closed `categories` enum),
|
|
132
183
|
`provisioning[]`, `parameters[]`, and `provenance{content_hash, exporter, signature?}`.
|
|
133
|
-
Harvested from
|
|
184
|
+
Harvested from the legacy `WorkspaceVfsPackageService` and generalized to be portability-
|
|
134
185
|
and registry-driven.
|
|
135
186
|
- **The provisioning contract (spec §17, registry `provisioning`)** — three deterministic,
|
|
136
187
|
idempotent verbs: `export` (pure, profile-filtered), `provision` (params + link-integrity
|
|
137
188
|
+ id-remap → replayable envelope plan), `import` (engine replay, idempotent via
|
|
138
|
-
idempotency_key). Adopts
|
|
189
|
+
idempotency_key). Adopts the legacy `WorkspaceProvisioningStep.mode`/`system` and
|
|
139
190
|
`WorkspaceGraphEdge.relation` as the canonical binding/dependency vocabularies, plus
|
|
140
191
|
`installMode` composition and structural-only `migration`+`patch` upgrades.
|
|
141
192
|
- **Reference engine for bundles (`src/bundle.mjs`)** — dependency-free `exportBundle`,
|
|
@@ -166,7 +217,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
166
217
|
real resource bound at provision). Export profiles `backup | template | sale` are filters
|
|
167
218
|
over these classes; `template`/`sale` exports MUST drop all `tenant_private` primitives.
|
|
168
219
|
Files MAY override their type default with `x_portability`. Lifted and generalized from
|
|
169
|
-
|
|
220
|
+
the legacy ad-hoc export filter in `WorkspaceVfsPackageService`.
|
|
170
221
|
- **`delete` envelope (spec §6.2)** — promoted from festech's `SsssOperationService` into
|
|
171
222
|
the canonical Operation Contract as the fourth envelope type. Removes a replace-type file,
|
|
172
223
|
rejects append-type targets, and emits a deletion event for audit. Idempotent.
|
package/README.md
CHANGED
|
@@ -1,31 +1,22 @@
|
|
|
1
1
|
# SSSS — Structured Semantic Syntax System
|
|
2
2
|
|
|
3
3
|
> A database-free, Markdown-first schema and mutation contract for AI-agent state.
|
|
4
|
-
> Turn
|
|
4
|
+
> Turn an autonomous agent ecosystem into a single portable, tradeable bundle.
|
|
5
5
|
|
|
6
6
|
[](conformance/)
|
|
7
|
-
[](docs/ssss-spec.md)
|
|
8
8
|
[](docs/ssss-spec.md)
|
|
9
9
|
|
|
10
|
-
SSSS is
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- **
|
|
14
|
-
|
|
15
|
-
- **
|
|
16
|
-
|
|
17
|
-
- **The
|
|
18
|
-
|
|
19
|
-
- **
|
|
20
|
-
injected multilingual embeddings, and runtime rendering with symbolic controls fixed.
|
|
21
|
-
- **Portability classification** (§5.5) — the keystone: every primitive is
|
|
22
|
-
`structural`, `tenant_private`, or `resource_bound`, so a vault can be *sold*
|
|
23
|
-
without leaking the operator's private data.
|
|
24
|
-
- **The `.ucw` bundle format** (§16) and **provisioning contract** (§17) — package a
|
|
25
|
-
vault, then `export → provision → import` it into a fresh tenant, deterministically.
|
|
26
|
-
|
|
27
|
-
This package, `@gregiteen/ssss-cli`, is **dependency-free**: a host needs neither Zod nor a YAML
|
|
28
|
-
library to be conformant. The reference engine *is* the validator.
|
|
10
|
+
SSSS is a vendor-neutral standard and reference implementation for portable AI memory and state. It defines:
|
|
11
|
+
|
|
12
|
+
- **Document primitives** — typed Markdown files with YAML frontmatter (`workflow`, `rule`, `page`, `assistant`, …), validated against a registry, not hand-written code.
|
|
13
|
+
- **The Operation Contract** (§6) — four envelope types (`operation` / `patch` / `event` / `delete`) for mutating a vault, with idempotency replay and audit.
|
|
14
|
+
- **The Workflow Runtime Contract** (§11.8) — workflows own triggers; daemons, crons, and webhooks derive idempotent event/task/run envelopes from the vault.
|
|
15
|
+
- **Multilingual semantic runtime** (§11.9) — deterministic lexical evidence, injected multilingual embeddings, and runtime rendering with symbolic controls fixed.
|
|
16
|
+
- **Portability classification** (§5.5) — the keystone: every primitive is `structural`, `tenant_private`, or `resource_bound`, so a vault can be *sold* or *shared* without leaking private data.
|
|
17
|
+
- **The `.ucw` bundle format** (§16) and **provisioning contract** (§17) — package a vault, then `export → provision → import` it into a fresh environment, deterministically.
|
|
18
|
+
|
|
19
|
+
This package, `@gregiteen/ssss-cli`, is **dependency-free**: a host needs neither Zod nor a YAML library to be conformant. The reference engine *is* the validator.
|
|
29
20
|
|
|
30
21
|
## Install
|
|
31
22
|
|
|
@@ -41,10 +32,9 @@ Requires Node 18+. No runtime dependencies.
|
|
|
41
32
|
|
|
42
33
|
```bash
|
|
43
34
|
ssss new my-app --install # scaffold + install + run the conformance test
|
|
44
|
-
ssss new my-app --with-total-recall # also wire the Total Recall memory OS
|
|
45
35
|
```
|
|
46
36
|
|
|
47
|
-
Scaffolds a starter `vault/`, a dependency-free conformance test, and
|
|
37
|
+
Scaffolds a starter `vault/`, a dependency-free conformance test, and instructions.
|
|
48
38
|
See `ssss help scaffold`.
|
|
49
39
|
|
|
50
40
|
## Quick start — the lifecycle
|
|
@@ -56,24 +46,41 @@ vault ──export──▶ .ucw bundle ──provision──▶ envelope plan
|
|
|
56
46
|
|
|
57
47
|
```bash
|
|
58
48
|
# 1. Package a vault as a sellable bundle (drops tenant-private data)
|
|
59
|
-
ssss export ./my-vault --profile sale --out
|
|
49
|
+
ssss export ./my-vault --profile sale --out bundle.ucw.json
|
|
60
50
|
|
|
61
51
|
# 2. Inspect / verify it
|
|
62
|
-
ssss inspect
|
|
63
|
-
ssss validate
|
|
52
|
+
ssss inspect bundle.ucw.json --files
|
|
53
|
+
ssss validate bundle.ucw.json
|
|
64
54
|
|
|
65
55
|
# 3. Stand up a fresh tenant from it
|
|
66
|
-
ssss import
|
|
67
|
-
--param
|
|
56
|
+
ssss import bundle.ucw.json --vault ./new-tenant \
|
|
57
|
+
--param company_name="Acme Corp" --param domain=acme.com
|
|
68
58
|
|
|
69
59
|
# Re-running import commits nothing — it is idempotent.
|
|
70
60
|
```
|
|
71
61
|
|
|
62
|
+
## 0.9 application kernel
|
|
63
|
+
|
|
64
|
+
SSSS 0.9 provides a shared mutation kernel and host adapter contracts for any system using SSSS:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
ssss primitive create --namespace acme --name "New Item" --language en
|
|
68
|
+
ssss registry compose --extension ./acme-extension.json
|
|
69
|
+
ssss adapter conformance
|
|
70
|
+
ssss migrate 0.8-to-0.9 ./vault
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Library subpaths: `@gregiteen/ssss-cli/kernel`, `/vfs`, `/identity`, `/leases`, `/events`, `/projections`, `/semantic`, `/ui`.
|
|
74
|
+
|
|
72
75
|
## Commands
|
|
73
76
|
|
|
74
77
|
| Command | Purpose |
|
|
75
78
|
|---------|---------|
|
|
76
|
-
| `ssss new <dir>` | Scaffold a new SSSS project (`--
|
|
79
|
+
| `ssss new <dir>` | Scaffold a new SSSS project (`--install`). |
|
|
80
|
+
| `ssss primitive <verb>` | Create, validate, inspect, or migrate primitive definitions. |
|
|
81
|
+
| `ssss registry <verb>` | Compose, lock, or verify an effective registry. |
|
|
82
|
+
| `ssss adapter conformance` | Run machine-readable VFS/lease/idempotency contract suites. |
|
|
83
|
+
| `ssss migrate 0.8-to-0.9` | Dry-run migration diagnostics with backup manifest. |
|
|
77
84
|
| `ssss export <vault>` | Package a vault into a `.ucw` bundle (§16). Pure & deterministic. |
|
|
78
85
|
| `ssss validate <bundle>` | Check a bundle against the schema + portability rules. |
|
|
79
86
|
| `ssss inspect <bundle>` | Human-readable summary (manifest, inventory, params, steps). |
|
|
@@ -86,52 +93,6 @@ ssss import festival.ucw.json --vault ./new-tenant \
|
|
|
86
93
|
|
|
87
94
|
Run `ssss <command> --help` for flags, or `ssss help <topic>` for concepts.
|
|
88
95
|
|
|
89
|
-
## Use as a library
|
|
90
|
-
|
|
91
|
-
```js
|
|
92
|
-
import { createEngine } from '@gregiteen/ssss-cli/engine';
|
|
93
|
-
import { exportBundle, validateBundle, provisionBundle, importBundle } from '@gregiteen/ssss-cli/bundle';
|
|
94
|
-
import { planWorkflowTrigger } from '@gregiteen/ssss-cli/runtime';
|
|
95
|
-
import { buildSemanticIndex, searchSemanticIndex, renderSemanticRecord } from '@gregiteen/ssss-cli/semantic';
|
|
96
|
-
|
|
97
|
-
const bundle = exportBundle('./my-vault', { profile: 'sale', name: 'Festival in a Box' });
|
|
98
|
-
const { valid, errors } = validateBundle(bundle);
|
|
99
|
-
|
|
100
|
-
const engine = createEngine();
|
|
101
|
-
const plan = provisionBundle(bundle, { parameters: { domain: 'acme.live' }, workspaceId: 'ws-1' });
|
|
102
|
-
importBundle(plan.plan, './new-tenant', engine);
|
|
103
|
-
|
|
104
|
-
const workflowContent = `---
|
|
105
|
-
type: workflow
|
|
106
|
-
name: "Daily Digest"
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
1. Gather messages.
|
|
110
|
-
2. Summarize.
|
|
111
|
-
3. Send digest.
|
|
112
|
-
`;
|
|
113
|
-
const runtimePlan = planWorkflowTrigger({
|
|
114
|
-
workflowPath: 'workflows/daily-digest/WORKFLOW.md',
|
|
115
|
-
workflowContent,
|
|
116
|
-
workspaceId: 'ws-1',
|
|
117
|
-
trigger: { type: 'cron', id: 'daily-0800', cron: '0 8 * * *' },
|
|
118
|
-
scheduledFor: '2026-07-02T14:00:00.000Z',
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
const index = buildSemanticIndex('./my-vault');
|
|
122
|
-
const matches = searchSemanticIndex(index, 'política de reembolsos');
|
|
123
|
-
const rendered = await renderSemanticRecord(matches[0].document, { language: 'es', render: renderer });
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
Exports: `@gregiteen/ssss-cli` / `@gregiteen/ssss-cli/engine` (Operation Contract engine),
|
|
127
|
-
`@gregiteen/ssss-cli/bundle` (export/provision/import), `@gregiteen/ssss-cli/registry`,
|
|
128
|
-
`@gregiteen/ssss-cli/runtime`, `@gregiteen/ssss-cli/semantic`,
|
|
129
|
-
`@gregiteen/ssss-cli/frontmatter`.
|
|
130
|
-
|
|
131
|
-
Canonical content is authored once in any language. Multilingual embedding and render
|
|
132
|
-
adapters handle cross-language retrieval and presentation at runtime; primitive IDs,
|
|
133
|
-
field IDs, enum codes, permissions, paths, hashes, and relations never change.
|
|
134
|
-
|
|
135
96
|
## Portability — why a vault is safe to sell
|
|
136
97
|
|
|
137
98
|
| Class | Meaning | In a `sale` export? |
|
|
@@ -140,13 +101,11 @@ field IDs, enum codes, permissions, paths, hashes, and relations never change.
|
|
|
140
101
|
| `resource_bound` | Needs a real resource bound at provision (`domain`, `phone_number`). | ✅ shipped (as a parameter/step) |
|
|
141
102
|
| `tenant_private` | The operator's private data (`task`, customer records). | ❌ **dropped** |
|
|
142
103
|
|
|
143
|
-
`template` and `sale` exports MUST drop every `tenant_private` file. The reference
|
|
144
|
-
bundle proves it — see `ssss help portability`.
|
|
104
|
+
`template` and `sale` exports MUST drop every `tenant_private` file. The reference bundle proves it — see `ssss help portability`.
|
|
145
105
|
|
|
146
106
|
## Conformance
|
|
147
107
|
|
|
148
|
-
A host MUST NOT claim SSSS conformance without passing the suite in
|
|
149
|
-
[`conformance/`](conformance/):
|
|
108
|
+
A host MUST NOT claim SSSS conformance without passing the suite in `conformance/`:
|
|
150
109
|
|
|
151
110
|
```bash
|
|
152
111
|
ssss conformance # structural + registry validation
|
|
@@ -157,10 +116,10 @@ npm test # == ssss conformance --engine
|
|
|
157
116
|
## Repository layout
|
|
158
117
|
|
|
159
118
|
```
|
|
160
|
-
docs/ssss-spec.md The normative specification (v0.9
|
|
119
|
+
docs/ssss-spec.md The normative specification (v0.9).
|
|
161
120
|
docs/help/ Topic docs surfaced by `ssss help`.
|
|
162
121
|
registry/core.json 15 document + 5 contract primitives; semantic, bundle & provisioning schemas.
|
|
163
|
-
registry/extensions/ Application extension registries
|
|
122
|
+
registry/extensions/ Application extension registries.
|
|
164
123
|
src/engine.mjs Operation Contract engine (§6).
|
|
165
124
|
src/runtime.mjs Workflow trigger → event/task/run envelope planning (§11.8).
|
|
166
125
|
src/semantic.mjs Multilingual semantic retrieval + runtime rendering (§11.9).
|
|
@@ -174,4 +133,4 @@ skills/ssss/ Packaged SSSS system skill and dependency-free valida
|
|
|
174
133
|
|
|
175
134
|
## License
|
|
176
135
|
|
|
177
|
-
ISC © Greg Iteen. See [LICENSE](LICENSE).
|
|
136
|
+
ISC © Greg Iteen. See [LICENSE](./LICENSE).
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.9.
|
|
1
|
+
0.9.1
|
|
@@ -17,6 +17,24 @@
|
|
|
17
17
|
"rule": 1,
|
|
18
18
|
"workflow": 1
|
|
19
19
|
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"primitives": [
|
|
22
|
+
"assistant",
|
|
23
|
+
"domain",
|
|
24
|
+
"page",
|
|
25
|
+
"rule",
|
|
26
|
+
"security_role",
|
|
27
|
+
"workflow"
|
|
28
|
+
],
|
|
29
|
+
"extensions": [
|
|
30
|
+
"festech"
|
|
31
|
+
],
|
|
32
|
+
"migrations": [],
|
|
33
|
+
"integrity": {
|
|
34
|
+
"content_hash": "sha256:7febd673938a3dc04c068e0bf08097a9783c09e612606d2df9a4db8dcb8907f5",
|
|
35
|
+
"ssss_core_version": "0.9"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
20
38
|
"provisioning": [
|
|
21
39
|
{
|
|
22
40
|
"id": "ws",
|
|
@@ -88,7 +106,7 @@
|
|
|
88
106
|
"file_count": 14,
|
|
89
107
|
"provenance": {
|
|
90
108
|
"content_hash": "sha256:7febd673938a3dc04c068e0bf08097a9783c09e612606d2df9a4db8dcb8907f5",
|
|
91
|
-
"exporter": "@gregiteen/ssss-cli@0.9.
|
|
109
|
+
"exporter": "@gregiteen/ssss-cli@0.9.1"
|
|
92
110
|
}
|
|
93
111
|
},
|
|
94
112
|
"files": [
|
package/docs/help/overview.md
CHANGED
|
@@ -13,10 +13,12 @@ kernel, a registry-driven validator, multilingual semantic adapters, and the
|
|
|
13
13
|
| Piece | Where | What it is |
|
|
14
14
|
|-------|-------|------------|
|
|
15
15
|
| Spec | `docs/ssss-spec.md` | The normative standard. |
|
|
16
|
-
| Core registry | `registry/core.json` |
|
|
17
|
-
|
|
|
16
|
+
| Core registry | `registry/core.json` | Document primitives + contracts for semantic (§11.9), bundle (§16), and provisioning (§17). |
|
|
17
|
+
| Kernel | `src/kernel.mjs` | Shared 0.9 mutation authority (`kernel.execute`) used by hosts and the reference engine. |
|
|
18
|
+
| Engine | `src/engine.mjs` | Compatibility façade over the kernel for the §6 Operation Contract. |
|
|
18
19
|
| Runtime | `src/runtime.mjs` | The §11.8 workflow trigger → event/task/run envelope planner. |
|
|
19
20
|
| Semantic | `src/semantic.mjs` | Lexical evidence, multilingual embeddings, privacy filters, and runtime rendering (§11.9). |
|
|
21
|
+
| Adapters | `src/vfs.mjs`, `leases.mjs`, `events.mjs`, … | Host-injectable storage/security/projection contracts. |
|
|
20
22
|
| Bundle | `src/bundle.mjs` | export / validate / provision / import (§16–§17). |
|
|
21
23
|
| Conformance | `conformance/` | Fixtures every host must pass. |
|
|
22
24
|
|
|
@@ -34,4 +36,5 @@ Run `ssss help <topic>` for any of:
|
|
|
34
36
|
`export`, `provision`, `import`, `conformance`, `autolink`.
|
|
35
37
|
|
|
36
38
|
Run `ssss <command> --help` for: `export`, `validate`, `inspect`, `provision`,
|
|
37
|
-
`import`, `semantic`, `
|
|
39
|
+
`import`, `semantic`, `primitive`, `registry`, `adapter`, `migrate`,
|
|
40
|
+
`autolink`, `conformance`.
|
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
|
|
@@ -1156,7 +1157,7 @@ opaque ID as the slug and keep the human-readable label in `title` / `name`.
|
|
|
1156
1157
|
This document is versioned independently of any host and of the conformance
|
|
1157
1158
|
fixture set.
|
|
1158
1159
|
|
|
1159
|
-
- The spec version is stated in the document header (currently **v0.9
|
|
1160
|
+
- The spec version is stated in the document header (currently **v0.9**).
|
|
1160
1161
|
- Breaking changes to the file format, the type registry, or the Operation
|
|
1161
1162
|
Contract increment the spec version.
|
|
1162
1163
|
- Until **v1.0**, any version MAY introduce breaking changes.
|
|
@@ -1248,14 +1249,14 @@ inside, the resources it must bind at provision time, and provenance.
|
|
|
1248
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. |
|
|
1249
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. |
|
|
1250
1251
|
| `export_profile` | yes | `backup` \| `template` \| `sale` — the §5.5 profile this bundle was built under. Determines the allowed portability classes. |
|
|
1251
|
-
| `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). |
|
|
1252
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. |
|
|
1253
1254
|
| `parameters` | no | Array of parameter definitions (§16.5) the importer must resolve (e.g. business name, domain). |
|
|
1254
1255
|
| `source_workspace_id` | no | Origin workspace; OMITTED or nulled in `template`/`sale` profiles (it is operator-identifying). |
|
|
1255
1256
|
| `file_count` | yes | Length of `files`; a cheap integrity check. |
|
|
1256
1257
|
| `provenance` | yes | `{ content_hash, exporter, signature? }` (§16.3). |
|
|
1257
1258
|
|
|
1258
|
-
`primitive_inventory` supersedes
|
|
1259
|
+
`primitive_inventory` supersedes the legacy fixed `categories` object and `provisioning`
|
|
1259
1260
|
supersedes its `capabilities` booleans: both are now open and registry-driven rather than a
|
|
1260
1261
|
closed enum, so a bundle full of `festech` extension primitives inventories and provisions
|
|
1261
1262
|
them without a spec change.
|
|
@@ -1284,7 +1285,7 @@ honoring `x_portability`) MUST be permitted by `export_profile`.
|
|
|
1284
1285
|
|
|
1285
1286
|
`parameters` are the values an importer must supply to turn a template into a running
|
|
1286
1287
|
instance — the difference between "a festival in a box" and "*this* festival." The schema is
|
|
1287
|
-
adopted verbatim from
|
|
1288
|
+
adopted verbatim from the legacy `WorkspaceTemplateVariableDefinition`:
|
|
1288
1289
|
|
|
1289
1290
|
| Field | Meaning |
|
|
1290
1291
|
|-------|---------|
|
|
@@ -1325,7 +1326,7 @@ restoring an SSSS business are reliable operations rather than bespoke migration
|
|
|
1325
1326
|
### 17.2 Provisioning Steps & Binding Vocabulary
|
|
1326
1327
|
|
|
1327
1328
|
A manifest's `provisioning` array is an ordered list of steps. The step schema and its
|
|
1328
|
-
controlled vocabularies are adopted from
|
|
1329
|
+
controlled vocabularies are adopted from the legacy `WorkspaceProvisioningStep` and
|
|
1329
1330
|
`WorkspaceGraphEdge`:
|
|
1330
1331
|
|
|
1331
1332
|
A **step** is `{ id, label, system, mode, required, notes? }`:
|
|
@@ -1358,7 +1359,7 @@ cannot resolve a link MUST fail the provision rather than emit a dangling refere
|
|
|
1358
1359
|
### 17.4 Composition & Upgrade
|
|
1359
1360
|
|
|
1360
1361
|
- **Composition** — a bundle MAY depend on other bundles. A dependency carries an
|
|
1361
|
-
`installMode` of `optional | recommended | required` (adopted from
|
|
1362
|
+
`installMode` of `optional | recommended | required` (adopted from the legacy
|
|
1362
1363
|
`WorkspaceMarketplaceRecommendation`). `provision` installs `required` dependencies before
|
|
1363
1364
|
the bundle itself; `optional`/`recommended` are surfaced to the operator.
|
|
1364
1365
|
- **Upgrade** — moving an installed bundle from `v2` to a `v1`-conformant shape (or any
|
package/package.json
CHANGED
package/registry/core.json
CHANGED
|
@@ -205,7 +205,7 @@
|
|
|
205
205
|
"required": ["key", "label", "type", "scope", "source"],
|
|
206
206
|
"optional": ["required", "description", "defaultValue", "options", "dependsOn"],
|
|
207
207
|
"source_enum": ["user", "llm", "graph", "system"],
|
|
208
|
-
"note": "
|
|
208
|
+
"note": "resource_bound requirements ship as parameters of source user/system."
|
|
209
209
|
},
|
|
210
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`.",
|
|
211
211
|
"spec_ref": "§16"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
2
3
|
import os from 'node:os';
|
|
3
4
|
import path from 'node:path';
|
|
4
5
|
import { definePrimitive, definitionsToExtensionRegistry } from '../src/primitive.mjs';
|
|
@@ -13,7 +14,13 @@ import {
|
|
|
13
14
|
import { createValidator } from '../src/validator.mjs';
|
|
14
15
|
import { FileSystemVfs, MemoryVfs, runVfsContract } from '../src/vfs.mjs';
|
|
15
16
|
import { JsonlEventStore, MemoryEventStore, createCanonicalEvent } from '../src/events.mjs';
|
|
16
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
ProjectionCoordinator,
|
|
19
|
+
createSqlProjectionAdapter,
|
|
20
|
+
createSearchProjectionAdapter,
|
|
21
|
+
createQueueProjectionAdapter,
|
|
22
|
+
createViewModelProjectionAdapter,
|
|
23
|
+
} from '../src/projections.mjs';
|
|
17
24
|
import { createKernel } from '../src/kernel.mjs';
|
|
18
25
|
import { UiRegistry, actionToEnvelope, createDeterministicUi, planUi, validateUiManifest } from '../src/ui.mjs';
|
|
19
26
|
import { createCommandHandler, createDomainCommand } from '../src/http.mjs';
|
|
@@ -21,6 +28,84 @@ import { detectDirectWrites } from '../src/guard.mjs';
|
|
|
21
28
|
import { MemoryLeaseStore, FileLeaseStore, runLeaseContract } from '../src/leases.mjs';
|
|
22
29
|
import { MemoryIdempotencyStore, FileIdempotencyStore, runIdempotencyContract } from '../src/idempotency.mjs';
|
|
23
30
|
import { createCapabilityAuthorizer } from '../src/authorization.mjs';
|
|
31
|
+
import { validateVerifiedPrincipal } from '../src/authorization.mjs';
|
|
32
|
+
|
|
33
|
+
async function rejects(promise, pattern) {
|
|
34
|
+
try { await promise; return false; } catch (error) { return pattern.test(error.message); }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function replayed(store, options) {
|
|
38
|
+
const items = [];
|
|
39
|
+
for await (const item of store.replay(options)) items.push(item);
|
|
40
|
+
return items;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Counts bytes JsonlEventStore reads from disk, to prove appends do not re-read the log.
|
|
44
|
+
async function bytesReadDuring(fn) {
|
|
45
|
+
const { readSync, readFileSync } = fs;
|
|
46
|
+
let bytes = 0;
|
|
47
|
+
fs.readSync = (...args) => { const read = readSync(...args); bytes += read; return read; };
|
|
48
|
+
fs.readFileSync = (...args) => { const out = readFileSync(...args); bytes += Buffer.byteLength(out); return out; };
|
|
49
|
+
try { await fn(); } finally { fs.readSync = readSync; fs.readFileSync = readFileSync; }
|
|
50
|
+
return bytes;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function checkJsonlEventStoreIndex(root, principal, check) {
|
|
54
|
+
let seq = 0;
|
|
55
|
+
const make = (workspace = 'acme') => createCanonicalEvent({
|
|
56
|
+
workspace_id: workspace, action: 'event', subject: 'events/indexed.md',
|
|
57
|
+
operation_id: `indexed-${++seq}`, idempotency_key: `indexed-${seq}`, principal,
|
|
58
|
+
});
|
|
59
|
+
const log = path.join(root, 'acme.jsonl');
|
|
60
|
+
const first = new JsonlEventStore(root);
|
|
61
|
+
const second = new JsonlEventStore(root);
|
|
62
|
+
const appended = [];
|
|
63
|
+
const add = async (store, event) => { await store.append(event); appended.push(event.event_id); return event; };
|
|
64
|
+
|
|
65
|
+
const own = await add(first, make());
|
|
66
|
+
check('jsonl event store rejects a duplicate from its own index', await rejects(first.append(own), /Duplicate event_id/));
|
|
67
|
+
const foreign = await add(second, make());
|
|
68
|
+
check('jsonl event store rejects a duplicate appended by another store instance', await rejects(first.append(foreign), /Duplicate event_id/));
|
|
69
|
+
const external = make();
|
|
70
|
+
execFileSync(process.execPath, ['--input-type=module', '-e', [
|
|
71
|
+
`import { JsonlEventStore } from ${JSON.stringify(new URL('../src/events.mjs', import.meta.url).href)};`,
|
|
72
|
+
`await new JsonlEventStore(${JSON.stringify(root)}).append(${JSON.stringify(external)});`,
|
|
73
|
+
].join('\n')]);
|
|
74
|
+
appended.push(external.event_id);
|
|
75
|
+
check('jsonl event store rejects a duplicate appended by another process', await rejects(second.append(external), /Duplicate event_id/));
|
|
76
|
+
await add(first, make());
|
|
77
|
+
check('jsonl event store rejects a duplicate after interleaved writers', await rejects(second.append(own), /Duplicate event_id/));
|
|
78
|
+
|
|
79
|
+
const seeded = fs.statSync(log).size;
|
|
80
|
+
const fresh = new JsonlEventStore(root);
|
|
81
|
+
const batchBytes = await bytesReadDuring(async () => { for (let i = 0; i < 200; i++) await add(fresh, make()); });
|
|
82
|
+
check('jsonl event store reads the log once for many appends from one instance', batchBytes === seeded, `read ${batchBytes} bytes; log was ${seeded}`);
|
|
83
|
+
await add(second, make());
|
|
84
|
+
const revalidateBytes = await bytesReadDuring(() => add(fresh, make()));
|
|
85
|
+
check('jsonl event store rebuilds its index after another writer appends', revalidateBytes > seeded);
|
|
86
|
+
|
|
87
|
+
const other = [];
|
|
88
|
+
for (let i = 0; i < 3; i++) other.push((await (i % 2 ? second : first).append(make('aaa'))).event_id);
|
|
89
|
+
const acme = await replayed(fresh, { workspaceId: 'acme' });
|
|
90
|
+
check('jsonl event store replays one log in append order',
|
|
91
|
+
JSON.stringify(acme.map((item) => item.event.event_id)) === JSON.stringify(appended)
|
|
92
|
+
&& acme.every((item, index) => item.cursor === index + 1));
|
|
93
|
+
const all = await replayed(fresh, {});
|
|
94
|
+
const expected = [...other, ...appended];
|
|
95
|
+
const resumed = await replayed(fresh, { cursor: 2 });
|
|
96
|
+
check('jsonl event store replays all logs in file order and resumes from a cursor',
|
|
97
|
+
JSON.stringify(all.map((item) => item.event.event_id)) === JSON.stringify(expected)
|
|
98
|
+
&& JSON.stringify(resumed.map((item) => item.event.event_id)) === JSON.stringify(expected.slice(2)));
|
|
99
|
+
|
|
100
|
+
if (process.platform !== 'win32') {
|
|
101
|
+
check('jsonl event logs are created with mode 0600', (fs.statSync(path.join(root, 'aaa.jsonl')).mode & 0o777) === 0o600);
|
|
102
|
+
}
|
|
103
|
+
const target = path.join(root, 'target.txt');
|
|
104
|
+
fs.writeFileSync(target, '');
|
|
105
|
+
fs.symlinkSync(target, path.join(root, 'linked.jsonl'));
|
|
106
|
+
check('jsonl event store refuses symlinked logs',
|
|
107
|
+
await rejects(fresh.append(make('linked')), /Symlinked event logs are forbidden/) && fs.readFileSync(target, 'utf8') === '');
|
|
108
|
+
}
|
|
24
109
|
|
|
25
110
|
export async function runKernel09Conformance() {
|
|
26
111
|
const checks = [];
|
|
@@ -93,11 +178,23 @@ export async function runKernel09Conformance() {
|
|
|
93
178
|
['filesystem', new FileLeaseStore(path.join(temp, 'leases'))],
|
|
94
179
|
]) check(`${name} lease store passes the shared contract`, (await runLeaseContract(store)).passed);
|
|
95
180
|
|
|
96
|
-
const authorize = createCapabilityAuthorizer(
|
|
181
|
+
const authorize = createCapabilityAuthorizer({
|
|
182
|
+
policyFloors: { [definition.primitive_id]: { delete: ['booking:admin'] } },
|
|
183
|
+
});
|
|
184
|
+
check('verified principal contract requires provenance', !validateVerifiedPrincipal({ id: 'x', kind: 'human', workspaceIds: [] }).valid);
|
|
185
|
+
check('verified principal contract accepts complete identity', validateVerifiedPrincipal({
|
|
186
|
+
id: 'greg', kind: 'human', workspaceIds: ['acme'], authentication: { provider: 'conformance', assurance: 'verified' },
|
|
187
|
+
}).valid);
|
|
97
188
|
const protectedPrincipal = { id: 'agent', kind: 'agent', workspaceIds: ['acme'], capabilities: ['booking:update'], authentication: { provider: 'test', assurance: 'verified' } };
|
|
98
189
|
const protectedContext = { principal: protectedPrincipal, workspaceId: 'acme', definition, action: 'patch', context: { requiredAssurance: 'elevated' } };
|
|
99
190
|
check('protected actions require authentication step-up', !(await authorize(protectedContext)).allowed);
|
|
100
191
|
check('protected actions require explicit verified-human confirmation', !(await authorize({ ...protectedContext, principal: { ...protectedPrincipal, kind: 'human', authentication: { provider: 'test', assurance: 'elevated' } }, context: { requiresHumanConfirmation: true } })).allowed);
|
|
192
|
+
check('policy floors fail closed without elevated capabilities', !(await authorize({
|
|
193
|
+
principal: { ...protectedPrincipal, capabilities: ['booking:delete'] },
|
|
194
|
+
workspaceId: 'acme',
|
|
195
|
+
definition,
|
|
196
|
+
action: 'delete',
|
|
197
|
+
})).allowed);
|
|
101
198
|
|
|
102
199
|
const vfs = new MemoryVfs();
|
|
103
200
|
const eventStore = new MemoryEventStore();
|
|
@@ -110,27 +207,61 @@ export async function runKernel09Conformance() {
|
|
|
110
207
|
reset: async () => projected.clear(),
|
|
111
208
|
snapshot: async () => Object.fromEntries([...projected.entries()].sort()),
|
|
112
209
|
});
|
|
113
|
-
const
|
|
210
|
+
const sql = createSqlProjectionAdapter();
|
|
211
|
+
const search = createSearchProjectionAdapter();
|
|
212
|
+
const queue = createQueueProjectionAdapter();
|
|
213
|
+
const viewModel = createViewModelProjectionAdapter();
|
|
214
|
+
for (const adapter of [sql, search, queue, viewModel]) coordinator.register(adapter);
|
|
215
|
+
const leaseStore = new MemoryLeaseStore();
|
|
216
|
+
const kernel = createKernel({
|
|
217
|
+
vfs, eventStore, projectionCoordinator: coordinator, validator, leaseStore, authorize,
|
|
218
|
+
});
|
|
114
219
|
const principal = {
|
|
115
|
-
id: 'greg', kind: 'human', workspaceIds: ['acme'], capabilities: ['booking:create', 'booking:update', 'booking:delete'],
|
|
220
|
+
id: 'greg', kind: 'human', workspaceIds: ['acme'], capabilities: ['booking:create', 'booking:update', 'booking:delete', 'booking:admin'],
|
|
116
221
|
authentication: { provider: 'conformance', assurance: 'verified' },
|
|
117
222
|
};
|
|
118
223
|
const createEnvelope = { type: 'operation', workspace_id: 'acme', idempotency_key: 'create-booking', path: 'bookings/one.md', content };
|
|
119
224
|
const created = await kernel.execute(createEnvelope, { principal });
|
|
120
225
|
check('shared kernel commits an authorized runtime primitive', created.success, JSON.stringify(created));
|
|
226
|
+
const leaseDenied = await kernel.execute({
|
|
227
|
+
type: 'patch', workspace_id: 'acme', idempotency_key: 'lease-denied', path: 'bookings/one.md', patches: { status: 'confirmed' },
|
|
228
|
+
}, { principal, requireLease: true });
|
|
229
|
+
check('kernel fails closed when a required lease is missing', leaseDenied.success === false && /lease/i.test(leaseDenied.validation.errors.join(' ')));
|
|
230
|
+
const lease = await leaseStore.acquire({
|
|
231
|
+
workspace_id: 'acme', target: 'bookings/one.md', principal_id: principal.id, operation_id: 'lease-ok',
|
|
232
|
+
}, 30_000);
|
|
233
|
+
const leased = await kernel.execute({
|
|
234
|
+
type: 'patch', workspace_id: 'acme', idempotency_key: 'lease-ok', path: 'bookings/one.md',
|
|
235
|
+
patches: { status: 'confirmed' }, lease_id: lease.lease_id, operation_id: 'lease-ok',
|
|
236
|
+
}, { principal, requireLease: true });
|
|
237
|
+
check('kernel accepts a matching verified lease', leased.success, JSON.stringify(leased));
|
|
121
238
|
const replay = await kernel.execute(createEnvelope, { principal });
|
|
122
239
|
check('exact retries replay idempotently', replay.replay === true);
|
|
123
240
|
const conflict = await kernel.execute({ ...createEnvelope, content: content.replace('status: pending', 'status: confirmed') }, { principal });
|
|
124
241
|
check('idempotency key reuse with changed content conflicts', conflict.success === false);
|
|
125
|
-
const patched = await kernel.execute({ type: 'patch', workspace_id: 'acme', idempotency_key: 'patch-booking', path: 'bookings/one.md', patches: { status: '
|
|
126
|
-
check('shared kernel patches and events the primitive', patched.success && await eventStore.size() ===
|
|
242
|
+
const patched = await kernel.execute({ type: 'patch', workspace_id: 'acme', idempotency_key: 'patch-booking', path: 'bookings/one.md', patches: { status: 'pending' } }, { principal });
|
|
243
|
+
check('shared kernel patches and events the primitive', patched.success && await eventStore.size() === 3);
|
|
127
244
|
check('projection dispatch follows committed events', projected.has('bookings/one.md'));
|
|
245
|
+
const sqlSnap = await sql.snapshot();
|
|
246
|
+
const searchSnap = await search.snapshot();
|
|
247
|
+
const queueSnap = await queue.snapshot();
|
|
248
|
+
const viewSnap = await viewModel.snapshot();
|
|
249
|
+
check('SQL/search/queue/view-model adapters receive events',
|
|
250
|
+
Object.values(sqlSnap).some((row) => row.subject === 'bookings/one.md')
|
|
251
|
+
&& Object.hasOwn(searchSnap, 'bookings/one.md')
|
|
252
|
+
&& queueSnap.length >= 1
|
|
253
|
+
&& viewSnap['bookings/one.md']?.last_action);
|
|
128
254
|
const rebuilt = await coordinator.replay('bookings', { rebuild: true });
|
|
129
|
-
check('projection is rebuildable from the event log', rebuilt.applied ===
|
|
255
|
+
check('projection is rebuildable from the event log', rebuilt.applied === 3 && projected.has('bookings/one.md'));
|
|
130
256
|
const expectedProjectionHash = rebuilt.output_hash;
|
|
131
257
|
projected.set('tampered', 'direct-write');
|
|
132
258
|
check('projection drift is detectable', (await coordinator.detectDrift('bookings', expectedProjectionHash)).drift);
|
|
133
259
|
projected.delete('tampered');
|
|
260
|
+
const sqlBefore = await sql.snapshot();
|
|
261
|
+
await coordinator.replay(sql.id, { rebuild: true });
|
|
262
|
+
check('projections can be deleted and rebuilt without losing canonical meaning',
|
|
263
|
+
JSON.stringify(await sql.snapshot()) === JSON.stringify(sqlBefore)
|
|
264
|
+
&& (await vfs.read('bookings/one.md'))?.hash);
|
|
134
265
|
coordinator.register({ id: 'broken', apply: async () => { throw new Error('projection offline'); } });
|
|
135
266
|
const beforeFailure = (await vfs.read('bookings/one.md')).hash;
|
|
136
267
|
const failureDispatch = await coordinator.dispatch(createCanonicalEvent({ workspace_id: 'acme', action: 'patch', subject: 'bookings/one.md', operation_id: 'projection-failure', idempotency_key: 'projection-failure', principal }));
|
|
@@ -145,6 +276,7 @@ export async function runKernel09Conformance() {
|
|
|
145
276
|
for await (const item of store.replay({ workspaceId: 'acme' })) if (item.event.event_id === sample.event_id) replayed++;
|
|
146
277
|
check(`${name} event store is append-only and replayable`, duplicate && replayed === 1);
|
|
147
278
|
}
|
|
279
|
+
await checkJsonlEventStoreIndex(path.join(temp, 'events-index'), principal, check);
|
|
148
280
|
const denied = await kernel.execute({ type: 'patch', workspace_id: 'acme', idempotency_key: 'denied', path: 'bookings/one.md', patches: { status: 'pending' } }, {
|
|
149
281
|
principal: { ...principal, id: 'outsider', workspaceIds: [], capabilities: ['*:*'] },
|
|
150
282
|
});
|