@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 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 ultrachat's closed `categories` enum),
182
+ `primitive_inventory` (registry-driven, replacing the legacy closed `categories` enum),
132
183
  `provisioning[]`, `parameters[]`, and `provenance{content_hash, exporter, signature?}`.
133
- Harvested from ultrachat's `WorkspaceVfsPackageService` and generalized to be portability-
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 ultrachat's `WorkspaceProvisioningStep.mode`/`system` and
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
- ultrachat's ad-hoc export filter in `WorkspaceVfsPackageService`.
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 a running business into a single tradeable file — *a festival in a box.*
4
+ > Turn an autonomous agent ecosystem into a single portable, tradeable bundle.
5
5
 
6
6
  [![conformance](https://img.shields.io/badge/conformance-fixtures%20%2B%20runtime%20%2B%20bundle-brightgreen)](conformance/)
7
- [![spec](https://img.shields.io/badge/spec-v0.9%20draft-blue)](docs/ssss-spec.md)
7
+ [![spec](https://img.shields.io/badge/spec-v0.9-blue)](docs/ssss-spec.md)
8
8
  [![OKF](https://img.shields.io/badge/OKF-v0.1%20compatible-blue)](docs/ssss-spec.md)
9
9
 
10
- SSSS is the vendor-neutral standard and reference implementation shared by
11
- [festech.live](https://festech.live), **ultrachat**, and **total-recall**. It defines:
12
-
13
- - **Document primitives** — typed Markdown files with YAML frontmatter (`workflow`,
14
- `rule`, `page`, `assistant`, …), validated against a registry, not hand-written code.
15
- - **The Operation Contract** (§6) — four envelope types (`operation` / `patch` /
16
- `event` / `delete`) for mutating a vault, with idempotency replay and audit.
17
- - **The Workflow Runtime Contract** (§11.8) — workflows own triggers; daemons,
18
- crons, and webhooks derive idempotent event/task/run envelopes from the vault.
19
- - **Multilingual semantic runtime** (§11.9) — deterministic lexical evidence,
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 a `CLAUDE.md`.
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 festival.ucw.json
49
+ ssss export ./my-vault --profile sale --out bundle.ucw.json
60
50
 
61
51
  # 2. Inspect / verify it
62
- ssss inspect festival.ucw.json --files
63
- ssss validate festival.ucw.json
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 festival.ucw.json --vault ./new-tenant \
67
- --param business_name="Acme Fest" --param domain=acme.live
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 (`--with-total-recall`, `--install`). |
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 draft).
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 (e.g. festech).
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.0
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.0"
109
+ "exporter": "@gregiteen/ssss-cli@0.9.1"
92
110
  }
93
111
  },
94
112
  "files": [
@@ -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` | The 15 document primitives + 5 contract primitives, plus semantic (§11.9), bundle (§16), and provisioning (§17) contracts. |
17
- | Engine | `src/engine.mjs` | The §6 Operation Contract pipeline (operation/patch/event/delete). |
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`, `autolink`, `conformance`.
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.7 — Draft**
3
+ **Specification v0.9**
4
4
 
5
5
  > This is the canonical, vendor-neutral specification for SSSS. It is the ground
6
6
  > truth on which all SSSS implementations are built. It is intended to be vendored
7
- > byte-for-byte into any repository that implements SSSS (e.g. the UltraChat
7
+ > byte-for-byte into any repository that implements SSSS (e.g. a compatible host
8
8
  > Sovereign AI OS, the Total Recall reference kernel).
9
9
  >
10
- > Status: **Draft**. The format, type registry, and conformance contract are under
11
- > active development. Expect breaking changes until v1.0.
10
+ > Status: **Stable for 0.9**. The format, type registry, and conformance contract
11
+ > are settled for this version; until v1.0 a later version MAY still introduce
12
+ > breaking changes.
12
13
  >
13
14
  > Implementation-specific detail (routes, services, storage backends, deployment)
14
15
  > does **not** belong in this document — it belongs in each implementation's own
@@ -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 — Draft**).
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 ultrachat's hard-coded `categories`; it is registry-driven, so extension types appear automatically). |
1252
+ | `primitive_inventory` | yes | Map of primitive `type` → count, over every file in the bundle (replaces the legacy hard-coded `categories`; it is registry-driven, so extension types appear automatically). |
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 ultrachat's fixed `categories` object and `provisioning`
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 ultrachat's `WorkspaceTemplateVariableDefinition`:
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 ultrachat's `WorkspaceProvisioningStep` and
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 ultrachat's
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gregiteen/ssss-cli",
3
- "version": "0.9.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": {
@@ -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": "Adopted verbatim from ultrachat WorkspaceTemplateVariableDefinition; resource_bound requirements ship as parameters of source user/system."
208
+ "note": "resource_bound requirements ship as parameters of source user/system."
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 { ProjectionCoordinator } from '../src/projections.mjs';
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 kernel = createKernel({ vfs, eventStore, projectionCoordinator: coordinator, validator });
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: 'confirmed' } }, { principal });
126
- check('shared kernel patches and events the primitive', patched.success && await eventStore.size() === 2);
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 === 2 && projected.has('bookings/one.md'));
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
  });