@gregiteen/ssss-cli 0.12.1 → 0.13.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 +55 -0
- package/VERSION +1 -1
- package/conformance/reference-bundle.ucw.json +1 -1
- package/docs/ssss-spec.md +50 -7
- package/package.json +3 -2
- package/scripts/check-commit-durability.mjs +392 -0
- package/scripts/cmd-adapter.mjs +3 -0
- package/skills/ssss/SKILL.md +6 -47
- package/skills/ssss/core/SKILL.md +18 -0
- package/skills/ssss/core/references/core-registry.json +333 -0
- package/skills/ssss/core/references/ssss-spec.md +1959 -0
- package/skills/ssss/core/scripts/maintain.mjs +75 -0
- package/skills/ssss/evals/maintain.test.mjs +11 -0
- package/skills/ssss/references/protocol-reference-legacy.md +54 -0
- package/src/bundle.mjs +4 -1
- package/src/durable-fs.mjs +32 -0
- package/src/engine.mjs +4 -0
- package/src/events.mjs +119 -33
- package/src/intents.mjs +217 -0
- package/src/kernel.mjs +175 -54
- package/src/vfs.mjs +22 -3
- package/types/kernel.d.ts +29 -0
|
@@ -0,0 +1,1959 @@
|
|
|
1
|
+
# SSSS — Structured Semantic Syntax System
|
|
2
|
+
|
|
3
|
+
**Specification v0.9**
|
|
4
|
+
|
|
5
|
+
> This is the canonical, vendor-neutral specification for SSSS. It is the ground
|
|
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.
|
|
8
|
+
>
|
|
9
|
+
> Status: **Stable for 0.9**. The format, type registry, and conformance contract
|
|
10
|
+
> are settled for this version; until v1.0 a later version MAY still introduce
|
|
11
|
+
> breaking changes.
|
|
12
|
+
>
|
|
13
|
+
> Revision: 2026-09-24. Editorial alignment with the 0.9 reference kernel
|
|
14
|
+
> (`@gregiteen/ssss-cli`). This revision documents behavior that already ships:
|
|
15
|
+
> the verified-principal model, the real stage order, the canonical event
|
|
16
|
+
> record, and symbolic error codes. It changes no wire format. Every example
|
|
17
|
+
> document in this file is validated by the conformance suite (§12).
|
|
18
|
+
>
|
|
19
|
+
> Implementation-specific detail (routes, services, storage backends, deployment)
|
|
20
|
+
> does **not** belong in this document — it belongs in each implementation's own
|
|
21
|
+
> `SKILL.md`. This file describes *what SSSS is*, never *how one product wires it up*.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1. Abstract
|
|
26
|
+
|
|
27
|
+
SSSS — the Structured Semantic Syntax System — is a **database-free, Markdown-first
|
|
28
|
+
schema and mutation contract for AI agent state**.
|
|
29
|
+
|
|
30
|
+
Every unit of agent-relevant state — a memory, a skill, a conversation, a workflow
|
|
31
|
+
run, an assistant definition, a runtime task — is a plain Markdown file with YAML
|
|
32
|
+
frontmatter. There is no relational database of record, no binary format, and no
|
|
33
|
+
proprietary container. A relational store MAY exist, but only as a *disposable
|
|
34
|
+
projection* rebuildable from the Markdown.
|
|
35
|
+
|
|
36
|
+
SSSS additionally defines the **Operation Contract**: a single, validated, idempotent
|
|
37
|
+
envelope through which all agent-generated mutations must flow. This makes
|
|
38
|
+
AI-generated state changes deterministic, replayable, conflict-safe, and auditable.
|
|
39
|
+
|
|
40
|
+
Its semantic runtime turns the same canonical files into a deterministic searchable
|
|
41
|
+
graph, while multilingual embedding and render adapters localize presentation without
|
|
42
|
+
mutating symbolic control fields or private operational data.
|
|
43
|
+
|
|
44
|
+
Any tool, IDE, agent framework, daemon, or CLI that can read Markdown can
|
|
45
|
+
interoperate with an SSSS vault. The only thing that distinguishes a memory engine,
|
|
46
|
+
a chat runtime, and a workflow orchestrator is *which primitive types they read and
|
|
47
|
+
write* — the underlying file format and mutation contract are identical.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 2. Conformance Terminology
|
|
52
|
+
|
|
53
|
+
The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**,
|
|
54
|
+
**SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** in this
|
|
55
|
+
document are to be interpreted as described in RFC 2119.
|
|
56
|
+
|
|
57
|
+
Defined terms used throughout:
|
|
58
|
+
|
|
59
|
+
| Term | Meaning |
|
|
60
|
+
|------|---------|
|
|
61
|
+
| **Host** | A system that implements SSSS — stores SSSS files and/or processes the Operation Contract. |
|
|
62
|
+
| **Vault** | The version-controlled directory tree of SSSS files. The source of truth. |
|
|
63
|
+
| **VFS** | Virtual File System — the addressable namespace of paths within a vault. |
|
|
64
|
+
| **Primitive** | A defined `type` of SSSS file or contract structure (see §5). |
|
|
65
|
+
| **Document primitive** | A primitive that exists as an addressable Markdown file. |
|
|
66
|
+
| **Contract primitive** | A primitive that exists only as a protocol structure (envelope, lease, event). |
|
|
67
|
+
| **Projection** | A derived, disposable representation of vault state (e.g. an SQL table, a search index). Never source-of-truth. |
|
|
68
|
+
| **Agent** | Any human or AI actor issuing operations. |
|
|
69
|
+
| **Envelope** | A JSON command submitted to the Operation Contract: `operation`, `patch`, `event`, or `delete` (§6). |
|
|
70
|
+
| **Kernel** | The component that runs the §6.3 pipeline. It owns stage order and semantics; storage is delegated to adapters. |
|
|
71
|
+
| **Adapter** | A host-supplied implementation of one storage or policy contract: VFS, event store, idempotency store, lease store, authorizer, projections, or resource coordinator (§6.7). |
|
|
72
|
+
| **Verified principal** | The authenticated identity a host attaches to an envelope *outside* the envelope body (§6.6). Never taken from client-supplied fields. |
|
|
73
|
+
| **Capability** | A permission string of the form `<scope>:<action>` held by a principal and required by a primitive (§6.6). |
|
|
74
|
+
| **Qualified type** | A primitive identity prefixed by its registry namespace: `ssss:assistant` for core, `<ext>:<type>` for an extension (§5.6). |
|
|
75
|
+
| **Portability class** | `structural`, `tenant_private`, or `resource_bound`: whether a document may leave the workspace (§5.5). |
|
|
76
|
+
| **Bundle** | A `.ucw` package of vault files plus a manifest (§16). |
|
|
77
|
+
| **Commit point** | The moment a mutation becomes durable: the canonical event is appended to the event log (§6.3). |
|
|
78
|
+
|
|
79
|
+
A host is **conformant** if it satisfies every MUST in this document and passes the
|
|
80
|
+
conformance fixtures of §12.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 3. Design Principles
|
|
85
|
+
|
|
86
|
+
| Principle | Rule |
|
|
87
|
+
|-----------|------|
|
|
88
|
+
| **No database of record** | Product-meaningful state MUST live in Markdown files, not in a relational database. A database MAY hold projections only. |
|
|
89
|
+
| **Markdown is law** | Every state primitive exists as a `.md` file (document primitives) or as a JSON envelope over the contract (contract primitives). |
|
|
90
|
+
| **Semantic frontmatter** | Every SSSS file MUST carry YAML frontmatter with a `type` field that identifies how engines interpret it. |
|
|
91
|
+
| **One mutation contract** | All agent-generated mutations MUST flow through the Operation Contract (§6). Direct, unvalidated writes by agents are forbidden. |
|
|
92
|
+
| **Deterministic validation** | Every mutation MUST be validated against the primitive's schema before commit. Validation MUST be deterministic — same input, same verdict. |
|
|
93
|
+
| **Idempotent by key** | Every operation carries an idempotency key. Replays MUST NOT double-apply. |
|
|
94
|
+
| **Append-only history** | The event log is immutable, and so are the records in append-type documents. Neither is ever updated or deleted. |
|
|
95
|
+
| **Disposable indexes** | Derived indexes and projections are ephemeral caches, fully rebuildable from the vault. They may be deleted at any time. |
|
|
96
|
+
| **Git-versioned** | The vault is version-controlled. History is provenance. |
|
|
97
|
+
| **Portable** | A vault is interpretable by any Markdown-capable tool. No host is privileged. |
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 4. The SSSS File
|
|
102
|
+
|
|
103
|
+
### 4.1 Anatomy
|
|
104
|
+
|
|
105
|
+
A document primitive is a UTF-8 Markdown file with two regions:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
---
|
|
109
|
+
type: <primitive-type>
|
|
110
|
+
<frontmatter fields...>
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
<Markdown body>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
1. **Frontmatter** — a YAML block delimited by a leading `---` line and a closing
|
|
117
|
+
`---` line. It MUST be the first content in the file. It is machine-readable
|
|
118
|
+
structured data.
|
|
119
|
+
2. **Body** — everything after the closing `---`. It is human-readable Markdown
|
|
120
|
+
prose, and for append-type primitives (§5.3) it carries the ordered records.
|
|
121
|
+
|
|
122
|
+
A Markdown file with no frontmatter, or with frontmatter that omits `type`, is **not
|
|
123
|
+
an SSSS file** — it is an ordinary Markdown file (e.g. a `README`) and is out of SSSS
|
|
124
|
+
scope. Where a context *requires* an SSSS file — a write through the Operation
|
|
125
|
+
Contract (§6) to an SSSS path — a missing or unrecognized `type` MUST be rejected.
|
|
126
|
+
|
|
127
|
+
### 4.2 Universal Frontmatter Fields
|
|
128
|
+
|
|
129
|
+
Every SSSS document primitive MUST include:
|
|
130
|
+
|
|
131
|
+
| Field | Type | Description |
|
|
132
|
+
|-------|------|-------------|
|
|
133
|
+
| `type` | string | The primitive type. MUST match an entry in the Type Registry (§5). |
|
|
134
|
+
| `title` | string | Human-readable title of the document. |
|
|
135
|
+
| `description` | string | One-sentence summary of the document's purpose. |
|
|
136
|
+
| `timestamp` | string | ISO 8601 timestamp of last modification. |
|
|
137
|
+
|
|
138
|
+
For OKF-compatible discovery, SSSS document primitives SHOULD also include:
|
|
139
|
+
|
|
140
|
+
| Field | Type | Description |
|
|
141
|
+
|-------|------|-------------|
|
|
142
|
+
| `resource` | string | Canonical URI for the underlying asset, when the primitive describes one. |
|
|
143
|
+
| `tags` | array | Array of string tags for categorization and routing. |
|
|
144
|
+
| `aliases` | array | Array of alternative names for this concept, used for wiki autolinking. |
|
|
145
|
+
|
|
146
|
+
For stable semantic identity and graph projection, documents SHOULD also include:
|
|
147
|
+
|
|
148
|
+
| Field | Type | Description |
|
|
149
|
+
|-------|------|-------------|
|
|
150
|
+
| `semantic_id` | string | Stable concept identity that survives file moves and localized presentation. |
|
|
151
|
+
| `relations` | array | Explicit semantic edges to other vault-relative document paths or semantic ids. |
|
|
152
|
+
| `language` | string | BCP-47 tag of the authored natural-language surface, when known (e.g. `ja`, `es-MX`). |
|
|
153
|
+
|
|
154
|
+
`resource`, `tags`, and `aliases` serve both OKF discovery and the semantic layer.
|
|
155
|
+
These fields are recommendations, not new universal requirements. Earlier drafts
|
|
156
|
+
called the last field `locale`; a host SHOULD read `locale` as a fallback when
|
|
157
|
+
`language` is absent, and MUST write `language`.
|
|
158
|
+
|
|
159
|
+
Every primitive type defines its own additional REQUIRED and OPTIONAL fields
|
|
160
|
+
(§5.4). The four universal fields above are REQUIRED **in addition to** each
|
|
161
|
+
type's own list. To maintain forward compatibility and allow agents scratchpad
|
|
162
|
+
space, hosts MUST NOT reject documents containing unknown frontmatter keys.
|
|
163
|
+
Unknown fields MUST be preserved.
|
|
164
|
+
|
|
165
|
+
Directory index files (`index.md`, §4.3) are the one exemption. They carry no
|
|
166
|
+
frontmatter and are not validated as primitives.
|
|
167
|
+
|
|
168
|
+
### 4.3 Linking & Progressive Disclosure
|
|
169
|
+
|
|
170
|
+
SSSS adopts the OKF standards for hyperlinking and directory indices:
|
|
171
|
+
|
|
172
|
+
1. **Absolute Bundle-Relative Links**: Links to other primitives SHOULD be absolute paths starting from the vault root (e.g., `[Customer Assistant](/assistants/support.md)`).
|
|
173
|
+
2. **The `# Citations` Section**: If a primitive's body makes claims sourced from other primitives or external data, it SHOULD include a `# Citations` heading at the bottom with numbered reference links.
|
|
174
|
+
3. **Index Files (`index.md`)**: A host MAY generate `index.md` files in directories to provide progressive disclosure of the vault's contents. These files contain no frontmatter and exist solely to serve as a table of contents for agents traversing the VFS.
|
|
175
|
+
|
|
176
|
+
### 4.4 Canonical Filenames
|
|
177
|
+
|
|
178
|
+
Document primitives that have a fixed role in a directory use an **uppercase
|
|
179
|
+
canonical filename** that signals the primitive at a glance:
|
|
180
|
+
|
|
181
|
+
| Filename | Primitive |
|
|
182
|
+
|----------|-----------|
|
|
183
|
+
| `SKILL.md` | `skill` |
|
|
184
|
+
| `ASSISTANT.md` | `assistant` |
|
|
185
|
+
| `WORKFLOW.md` | `workflow` |
|
|
186
|
+
| `MODEL.md` | `model` |
|
|
187
|
+
| `CONVERSATION.md` | `conversation` |
|
|
188
|
+
| `RUN.md` | `run` |
|
|
189
|
+
| `ROLE.md` | `security_role` (canonically `roles/<role>/ROLE.md`) |
|
|
190
|
+
|
|
191
|
+
Free-standing primitives (e.g. `memory`, `task`) instead use a `kebab-case` slug
|
|
192
|
+
filename matching their `slug` field. Governed `primitive` definitions live at
|
|
193
|
+
`primitives/<namespace>/<id>.md`. The frontmatter `type` is always
|
|
194
|
+
authoritative; the filename is a convention, not a substitute for validation.
|
|
195
|
+
|
|
196
|
+
### 4.5 The Frontmatter Subset
|
|
197
|
+
|
|
198
|
+
Frontmatter is YAML, but a conformant host is only required to accept the subset
|
|
199
|
+
below. A document that stays inside it round-trips identically through every
|
|
200
|
+
conformant parser. The reference parser is dependency-free and implements
|
|
201
|
+
exactly this subset.
|
|
202
|
+
|
|
203
|
+
- **Scalars**: strings (bare, `"double"`, or `'single'` quoted), integers,
|
|
204
|
+
decimals, `true`/`false`, and `null`/`~`. ISO 8601 timestamps are read as
|
|
205
|
+
**strings**, not native dates, so they round-trip byte-for-byte.
|
|
206
|
+
- **Inline arrays**: `[a, b, "c d"]`.
|
|
207
|
+
- **Block arrays**: `- item` lines.
|
|
208
|
+
- **Nested maps**: indented `key: value` blocks.
|
|
209
|
+
- **Arrays of maps**: `- key: value` entries whose further keys are indented
|
|
210
|
+
under the first. A map entry may itself hold an inline array.
|
|
211
|
+
- **Folded strings**: `>` and `>-` block scalars.
|
|
212
|
+
|
|
213
|
+
Anchors, aliases, tags, and multi-document streams are outside the subset.
|
|
214
|
+
Flow maps (`{a: 1}`) and JSON-style inline values (`[{"a": 1}]`) are also
|
|
215
|
+
outside it, but the reference parser reads them because other tools write them
|
|
216
|
+
(for example Total Recall's JSON-shaped metadata). A host MAY accept any of
|
|
217
|
+
these but MUST NOT require them, and authors SHOULD NOT use them in documents
|
|
218
|
+
that leave the host.
|
|
219
|
+
|
|
220
|
+
Where the reference parser reads a form YAML 1.2 also defines, it follows YAML:
|
|
221
|
+
keys may contain `-`, `.` and `@`; a `:` only splits a key when whitespace or
|
|
222
|
+
end of line follows, so `context:universal` is a value; plain and quoted
|
|
223
|
+
scalars may wrap onto indented continuation lines; a trailing ` # comment` on
|
|
224
|
+
an unquoted value is dropped; and a block list may sit at its key's indent.
|
|
225
|
+
Two deliberate deviations keep values lossless: an integer with a leading zero
|
|
226
|
+
(`07885`) stays a **string**, since it is a code and not a number, and
|
|
227
|
+
timestamps stay strings, as above.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## 5. Primitive Type Registry
|
|
232
|
+
|
|
233
|
+
SSSS defines two families of primitive: **document primitives** (addressable files)
|
|
234
|
+
and **contract primitives** (protocol structures). A host need not implement every
|
|
235
|
+
primitive — it implements the subset its product requires — but any primitive it
|
|
236
|
+
*does* implement MUST conform to the schema below.
|
|
237
|
+
|
|
238
|
+
### 5.1 Document Primitives
|
|
239
|
+
|
|
240
|
+
| `type` | Family | Portability | Purpose |
|
|
241
|
+
|--------|--------|-------------|---------|
|
|
242
|
+
| `memory` | knowledge | structural | A single unit of agent knowledge (rule, pattern, fact, preference). |
|
|
243
|
+
| `skill` | capability | structural | A skill package manifest. |
|
|
244
|
+
| `asset` | capability | structural | A text-encoded code, vector, or binary asset payload or pointer. |
|
|
245
|
+
| `resource` | capability | resource_bound | An external infrastructure, cloud asset, domain, database, or API dependency. |
|
|
246
|
+
| `rule` | governance | structural | A workspace-scoped behavior rule applied to assistants/agents. |
|
|
247
|
+
| `role` | governance | structural | A security role definition for role-based access control (RBAC). |
|
|
248
|
+
| `security_role` | governance | structural | A security role definition for role-based access control (RBAC). Legacy alias of `role`. |
|
|
249
|
+
| `task` | work | tenant_private | A unit of submitted work — one step or many. The universal work primitive. |
|
|
250
|
+
| `assistant` | actor | structural | The definition of an AI assistant/persona. |
|
|
251
|
+
| `workflow` | work | structural | A reusable **task template**: a defined procedure. Firing a workflow submits a `task`. |
|
|
252
|
+
| `trigger` | work | structural | A standalone schedule or event-driven trigger that fires a workflow. |
|
|
253
|
+
| `model` | catalog | structural | The definition of an inference model. |
|
|
254
|
+
| `thread` | transcript | tenant_private | An append-only chat transcript. |
|
|
255
|
+
| `conversation` | transcript | tenant_private | An append-only chat transcript. Legacy alias of `thread`. |
|
|
256
|
+
| `run` | transcript | tenant_private | An append-only workflow execution record. |
|
|
257
|
+
| `conflict` | meta | tenant_private | A record of two contradicting primitives, blocking promotion. |
|
|
258
|
+
| `page` | capability | structural | A VFS-native sandboxed custom workspace page. |
|
|
259
|
+
| `migration` | meta | structural | An SSSS schema migration state record. |
|
|
260
|
+
| `release` | meta | structural | An SSSS system schema version release record. |
|
|
261
|
+
| `primitive` | meta | structural | A governed, versioned definition for a namespaced SSSS primitive. |
|
|
262
|
+
|
|
263
|
+
The **Portability** column is the primitive's default portability class (§5.5); a host
|
|
264
|
+
resolves it from `registry/core.json`. Extension registries assign their own primitives a
|
|
265
|
+
class — notably `resource_bound` for things like domains and phone numbers.
|
|
266
|
+
|
|
267
|
+
### 5.2 Contract Primitives
|
|
268
|
+
|
|
269
|
+
| `type` | Purpose |
|
|
270
|
+
|--------|---------|
|
|
271
|
+
| `operation` | A full atomic file write (create or full replace) — see §6. |
|
|
272
|
+
| `patch` | A partial merge into an existing file — see §6. |
|
|
273
|
+
| `event` | An append-only immutable log entry — see §6, §8. |
|
|
274
|
+
| `delete` | A tombstoning removal of a replace-type file — see §6. |
|
|
275
|
+
| `lease` | A file-level write lock — see §7. |
|
|
276
|
+
|
|
277
|
+
### 5.3 Append-Type vs. Replace-Type Documents
|
|
278
|
+
|
|
279
|
+
Document primitives are either:
|
|
280
|
+
|
|
281
|
+
- **Replace-type** — the whole file represents current state; a write replaces it
|
|
282
|
+
entirely (`memory`, `skill`, `rule`, `security_role`, `task`, `assistant`,
|
|
283
|
+
`workflow`, `model`, `conflict`, `page`, `migration`, `release`, `primitive`).
|
|
284
|
+
- **Append-type** — the file is an ordered, append-only log; writes add records to
|
|
285
|
+
the body and MUST NOT rewrite prior records (`conversation`, `run`).
|
|
286
|
+
|
|
287
|
+
Append-type documents MUST be mutated only by appending. A host MUST reject an
|
|
288
|
+
operation that would rewrite or remove existing records of an append-type document.
|
|
289
|
+
|
|
290
|
+
### 5.4 Per-Type Schemas
|
|
291
|
+
|
|
292
|
+
Each schema lists the type's REQUIRED fields. These are in addition to the
|
|
293
|
+
universal fields of §4.2 (`type`, `title`, `description`, `timestamp`), which
|
|
294
|
+
every example carries. All other fields are OPTIONAL. Examples are minimal, and
|
|
295
|
+
each one passes the reference validator (§12).
|
|
296
|
+
|
|
297
|
+
#### `memory`
|
|
298
|
+
|
|
299
|
+
The knowledge primitive. Categories: `invariants`, `patterns`, `anti-patterns`,
|
|
300
|
+
`preferences`, `decisions`, `concepts`, `facts`, `lore`.
|
|
301
|
+
|
|
302
|
+
REQUIRED: `type`, `slug`, `category`, `title`, `status`, `schema_version`.
|
|
303
|
+
|
|
304
|
+
Knowledge-graph fields, REQUIRED when `schema_version: 2`: `confidence` (0..1),
|
|
305
|
+
`importance` (1..5), `modality` (`must|must_not|should|should_not|descriptive|preference`),
|
|
306
|
+
`subject`, `predicate`, `object`, `sentiment_polarity`
|
|
307
|
+
(`directive_must|directive_must_not|descriptive|preference`).
|
|
308
|
+
|
|
309
|
+
The `subject`/`predicate`/`object` triple SHOULD use stable, language-neutral
|
|
310
|
+
concept identifiers (not localized prose), so the triple is a semantic anchor
|
|
311
|
+
independent of the document's authoring language — see §11.
|
|
312
|
+
|
|
313
|
+
```markdown
|
|
314
|
+
---
|
|
315
|
+
type: memory
|
|
316
|
+
slug: prefer-atomic-writes
|
|
317
|
+
category: patterns
|
|
318
|
+
title: "Always write files atomically (write-then-rename)"
|
|
319
|
+
description: "Write to a temporary file, then rename it over the target."
|
|
320
|
+
timestamp: 2026-05-16T14:00:00Z
|
|
321
|
+
status: active
|
|
322
|
+
schema_version: 2
|
|
323
|
+
confidence: 0.92
|
|
324
|
+
importance: 4
|
|
325
|
+
modality: must
|
|
326
|
+
subject: agent
|
|
327
|
+
predicate: use_atomic_write
|
|
328
|
+
object: file_system
|
|
329
|
+
sentiment_polarity: directive_must
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
Write to a temporary file, then `rename()` to the target. `rename()` is atomic on
|
|
333
|
+
POSIX filesystems; direct writes risk partial-file corruption on crash.
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Memory nodes in the `invariants` category additionally carry `priority: absolute`
|
|
337
|
+
and `immutable: true`. The full `priority` enum is `absolute|high|normal|low`;
|
|
338
|
+
`priority` is OPTIONAL on non-invariant categories (defaults to `normal`).
|
|
339
|
+
|
|
340
|
+
#### `skill`
|
|
341
|
+
|
|
342
|
+
A capability package manifest. SSSS skill manifests are compatible with the open
|
|
343
|
+
Agent Skills standard: the minimal REQUIRED frontmatter is `name` and `description`.
|
|
344
|
+
The `type: skill` discriminator is REQUIRED for SSSS-managed manifests so the
|
|
345
|
+
registry can route them.
|
|
346
|
+
|
|
347
|
+
REQUIRED: `type`, `name`, `description`.
|
|
348
|
+
|
|
349
|
+
```markdown
|
|
350
|
+
---
|
|
351
|
+
type: skill
|
|
352
|
+
name: deploy
|
|
353
|
+
title: "Deploy"
|
|
354
|
+
description: >-
|
|
355
|
+
Deploy services with zero downtime. Use when the user mentions deploy,
|
|
356
|
+
release, ship, or production push.
|
|
357
|
+
timestamp: 2026-05-16T14:00:00Z
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
# Deploy
|
|
361
|
+
...
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
#### `asset`
|
|
365
|
+
|
|
366
|
+
A text-encoded asset payload: source code, vector graphics, or base64-encoded binary content, or pointer metadata for large files.
|
|
367
|
+
|
|
368
|
+
REQUIRED: `type`, `name`, `mime_type`, `encoding` (`base64|utf-8|none`).
|
|
369
|
+
OPTIONAL: `slug`, `description`, `hash`, `size_bytes`, `storage_uri`, `resource_ref`.
|
|
370
|
+
|
|
371
|
+
```markdown
|
|
372
|
+
---
|
|
373
|
+
type: asset
|
|
374
|
+
name: "logo-icon"
|
|
375
|
+
title: "Brand Logo Icon"
|
|
376
|
+
description: "A 1x1 pixel PNG icon encoded in base64."
|
|
377
|
+
timestamp: 2026-05-16T14:00:00Z
|
|
378
|
+
mime_type: "image/png"
|
|
379
|
+
encoding: "base64"
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
A file too large to carry in the vault is stored as a **pointer**: `encoding: none`
|
|
386
|
+
and an empty body. A pointer REQUIRES `storage_uri`, the URI of the stored object,
|
|
387
|
+
and `x_portability` set to `resource_bound` or `tenant_private` (§5.5), so a
|
|
388
|
+
template or sale export never ships the seller's storage location: the exporter
|
|
389
|
+
replaces `storage_uri` with `REQUIREMENT` and the buyer binds their own copy at
|
|
390
|
+
provision. `resource_ref`, when present, is the vault path of the `resource`
|
|
391
|
+
document (bucket, object store) that holds the object. `hash` (`sha256:<hex>`) and
|
|
392
|
+
`size_bytes` pin the object's content and survive export, so the buyer can verify
|
|
393
|
+
the file they supply.
|
|
394
|
+
|
|
395
|
+
```markdown
|
|
396
|
+
---
|
|
397
|
+
type: asset
|
|
398
|
+
name: "training-video"
|
|
399
|
+
title: "Onboarding Video"
|
|
400
|
+
description: "Staff onboarding video, stored in object storage."
|
|
401
|
+
timestamp: 2026-09-28T00:00:00Z
|
|
402
|
+
mime_type: "video/mp4"
|
|
403
|
+
encoding: "none"
|
|
404
|
+
storage_uri: "s3://media-bucket/training/onboarding.mp4"
|
|
405
|
+
resource_ref: "resources/media-bucket.md"
|
|
406
|
+
hash: "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
|
|
407
|
+
size_bytes: 734003200
|
|
408
|
+
x_portability: resource_bound
|
|
409
|
+
---
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
#### `resource`
|
|
413
|
+
|
|
414
|
+
An external infrastructure, cloud asset, domain, database, or API dependency bound to the workspace. Carries `portability: resource_bound`.
|
|
415
|
+
|
|
416
|
+
REQUIRED: `type`, `name`, `kind`, `status` (`bound|unbound|pending|error`).
|
|
417
|
+
|
|
418
|
+
```markdown
|
|
419
|
+
---
|
|
420
|
+
type: resource
|
|
421
|
+
name: "Primary Database"
|
|
422
|
+
title: "Primary Database"
|
|
423
|
+
description: "Application production PostgreSQL database."
|
|
424
|
+
timestamp: 2026-05-16T14:00:00Z
|
|
425
|
+
kind: postgres
|
|
426
|
+
status: bound
|
|
427
|
+
binds:
|
|
428
|
+
host: "db.internal.net"
|
|
429
|
+
port: 5432
|
|
430
|
+
database: "app_prod"
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
Production PostgreSQL database connection.
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
#### `task`
|
|
437
|
+
|
|
438
|
+
A unit of submitted work — the universal work primitive. A `task` MAY be ad-hoc or
|
|
439
|
+
instantiated from a `workflow` template; either way it executes as a `run`.
|
|
440
|
+
|
|
441
|
+
REQUIRED: `type`, `priority` (integer), `category`, `status`
|
|
442
|
+
(`pending|in_progress|done|failed`). A task instantiated from a template carries an
|
|
443
|
+
OPTIONAL `workflow_id` referencing it.
|
|
444
|
+
|
|
445
|
+
Runtime-created tasks SHOULD additionally carry `workflow_path`, `trigger_id`,
|
|
446
|
+
`trigger_type`, `trigger_event_id`, `scheduled_for`, and `dedupe_key`. These fields
|
|
447
|
+
make the task reconstructible from the workflow definition plus the trigger event
|
|
448
|
+
and prevent duplicate daemon ticks from creating duplicate work (§11.8).
|
|
449
|
+
|
|
450
|
+
```markdown
|
|
451
|
+
---
|
|
452
|
+
type: task
|
|
453
|
+
title: "Research the payments API"
|
|
454
|
+
description: "Research the payments API and write a reference skill."
|
|
455
|
+
timestamp: 2026-05-16T08:00:00Z
|
|
456
|
+
priority: 85
|
|
457
|
+
category: skill-engineering
|
|
458
|
+
status: pending
|
|
459
|
+
workflow_id: "research-skill"
|
|
460
|
+
trigger_id: "manual"
|
|
461
|
+
scheduled_for: 2026-05-16T08:00:00Z
|
|
462
|
+
dedupe_key: "runtime:abc123"
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Objective
|
|
466
|
+
Research the payments API and write a reference skill.
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
#### `assistant`
|
|
470
|
+
|
|
471
|
+
The definition of an AI assistant.
|
|
472
|
+
|
|
473
|
+
REQUIRED: `type`, `name`.
|
|
474
|
+
|
|
475
|
+
```markdown
|
|
476
|
+
---
|
|
477
|
+
type: assistant
|
|
478
|
+
name: "Support Bot"
|
|
479
|
+
title: "Support Bot"
|
|
480
|
+
description: "Front-line customer support assistant."
|
|
481
|
+
timestamp: 2026-05-16T14:00:00Z
|
|
482
|
+
model: "example/large-model"
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
## Instructions
|
|
486
|
+
You are a helpful support assistant.
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
#### `workflow`
|
|
490
|
+
|
|
491
|
+
A reusable **task template** — a defined multi-step procedure plus triggers. A
|
|
492
|
+
workflow is not executed directly: when a trigger fires (or it is invoked), the host
|
|
493
|
+
**submits a `task`** from the template, and that task executes as a `run`. Workflow
|
|
494
|
+
and task are one work model — the workflow is the reusable definition, the task is
|
|
495
|
+
the submitted instance.
|
|
496
|
+
|
|
497
|
+
REQUIRED: `type`, `name`. `triggers` (array) is OPTIONAL — a workflow with no
|
|
498
|
+
triggers is valid and may be invoked manually or by another workflow.
|
|
499
|
+
|
|
500
|
+
When present, `triggers[]` is the canonical schedule source. OS cron, hosted
|
|
501
|
+
schedulers, queues, and daemon timers are wake-up mechanisms only; they MUST NOT
|
|
502
|
+
become the record of what the workflow schedule means. A trigger entry SHOULD
|
|
503
|
+
include:
|
|
504
|
+
|
|
505
|
+
| Field | Description |
|
|
506
|
+
|-------|-------------|
|
|
507
|
+
| `type` | `manual`, `cron`, `interval`, `event`, `webhook`, `file_change`, or `condition`. |
|
|
508
|
+
| `id` / `trigger_id` | Stable trigger identity within the workflow. |
|
|
509
|
+
| `cron` / `interval` / `event_type` | Type-specific selector. |
|
|
510
|
+
| `timezone` | Required for wall-clock schedules; UTC is implied if omitted. |
|
|
511
|
+
| `misfire_policy` | `skip`, `run_once`, or `catch_up`; default `run_once`. |
|
|
512
|
+
| `concurrency` | `allow_parallel`, `skip_if_running`, `enqueue`, or `replace`; default `skip_if_running`. |
|
|
513
|
+
|
|
514
|
+
```markdown
|
|
515
|
+
---
|
|
516
|
+
type: workflow
|
|
517
|
+
name: "Daily Digest"
|
|
518
|
+
title: "Daily Digest"
|
|
519
|
+
description: "Sends a daily summary email."
|
|
520
|
+
timestamp: 2026-05-16T14:00:00Z
|
|
521
|
+
triggers:
|
|
522
|
+
- type: cron
|
|
523
|
+
id: daily-0800
|
|
524
|
+
cron: "0 8 * * *"
|
|
525
|
+
timezone: "America/Denver"
|
|
526
|
+
misfire_policy: run_once
|
|
527
|
+
concurrency: skip_if_running
|
|
528
|
+
isActive: true
|
|
529
|
+
---
|
|
530
|
+
|
|
531
|
+
## Steps
|
|
532
|
+
1. Gather unread messages.
|
|
533
|
+
2. Summarize.
|
|
534
|
+
3. Send digest.
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
#### `trigger`
|
|
538
|
+
|
|
539
|
+
A standalone schedule or event-driven trigger that fires a task from a workflow template.
|
|
540
|
+
|
|
541
|
+
REQUIRED: `type`, `name`, `source` (`cron|interval|webhook|event|file_change|condition|manual`), `status` (`active|paused|disabled`).
|
|
542
|
+
|
|
543
|
+
```markdown
|
|
544
|
+
---
|
|
545
|
+
type: trigger
|
|
546
|
+
name: "Daily Sync"
|
|
547
|
+
title: "Daily Sync Trigger"
|
|
548
|
+
description: "Fires the daily synchronization workflow at 08:00 UTC."
|
|
549
|
+
timestamp: 2026-05-16T14:00:00Z
|
|
550
|
+
source: cron
|
|
551
|
+
status: active
|
|
552
|
+
cron: "0 8 * * *"
|
|
553
|
+
target_workflow: "workflows/sync.md"
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
Triggers daily synchronization.
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
#### `rule`
|
|
560
|
+
|
|
561
|
+
A workspace-scoped behavior rule that constrains how assistants/agents act.
|
|
562
|
+
Distinct from `memory` (which is agent-private knowledge): a `rule` is workspace
|
|
563
|
+
governance, authored deliberately.
|
|
564
|
+
|
|
565
|
+
REQUIRED: `type`, `name`. OPTIONAL: `description`, `scope`.
|
|
566
|
+
|
|
567
|
+
```markdown
|
|
568
|
+
---
|
|
569
|
+
type: rule
|
|
570
|
+
name: "No external links in replies"
|
|
571
|
+
title: "No external links in replies"
|
|
572
|
+
description: "Customer-facing replies must not contain outbound URLs."
|
|
573
|
+
timestamp: 2026-05-16T14:00:00Z
|
|
574
|
+
scope: support
|
|
575
|
+
---
|
|
576
|
+
|
|
577
|
+
Outbound links are stripped from any assistant reply on a support thread.
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
#### `security_role`
|
|
581
|
+
|
|
582
|
+
A named set of capabilities used for role-based access control (RBAC). It maps
|
|
583
|
+
a role name to the capabilities an agent or user holding that role is granted
|
|
584
|
+
within the workspace.
|
|
585
|
+
|
|
586
|
+
REQUIRED: `type`, `name`, `permissions` (array). OPTIONAL: `description`.
|
|
587
|
+
|
|
588
|
+
```markdown
|
|
589
|
+
---
|
|
590
|
+
type: security_role
|
|
591
|
+
name: "support-agent"
|
|
592
|
+
title: "Support agent"
|
|
593
|
+
description: "Can write assistants and events, nothing else."
|
|
594
|
+
timestamp: 2026-05-16T14:00:00Z
|
|
595
|
+
permissions:
|
|
596
|
+
- "ssss:assistant:*"
|
|
597
|
+
- "write:event"
|
|
598
|
+
---
|
|
599
|
+
|
|
600
|
+
Maintains support assistants and records support events.
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
`permissions` holds capability strings (§6.6). A capability names a scope and an
|
|
604
|
+
action; `*` in the action position grants every action on that scope, and `*:*`
|
|
605
|
+
grants everything. A `security_role` is one way for a host to derive a
|
|
606
|
+
principal's capabilities. The kernel only ever sees the resulting capability
|
|
607
|
+
list, never the role name.
|
|
608
|
+
|
|
609
|
+
#### `role`
|
|
610
|
+
|
|
611
|
+
A named set of capabilities used for role-based access control (RBAC). Canonical form of `security_role`.
|
|
612
|
+
|
|
613
|
+
REQUIRED: `type`, `name`, `permissions` (array). OPTIONAL: `description`.
|
|
614
|
+
|
|
615
|
+
```markdown
|
|
616
|
+
---
|
|
617
|
+
type: role
|
|
618
|
+
name: "support-agent"
|
|
619
|
+
title: "Support agent"
|
|
620
|
+
description: "Can write assistants and events, nothing else."
|
|
621
|
+
timestamp: 2026-05-16T14:00:00Z
|
|
622
|
+
permissions:
|
|
623
|
+
- "ssss:assistant:*"
|
|
624
|
+
- "write:event"
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
Maintains support assistants and records support events.
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
#### `model`
|
|
631
|
+
|
|
632
|
+
The definition of an inference model.
|
|
633
|
+
|
|
634
|
+
REQUIRED: `type`, `model_id`, `provider`.
|
|
635
|
+
|
|
636
|
+
```markdown
|
|
637
|
+
---
|
|
638
|
+
type: model
|
|
639
|
+
title: "Example Large Model"
|
|
640
|
+
description: "A general-purpose long-context model."
|
|
641
|
+
timestamp: 2026-05-16T14:00:00Z
|
|
642
|
+
model_id: "example/large-model"
|
|
643
|
+
provider: example
|
|
644
|
+
display_name: "Example Large Model"
|
|
645
|
+
---
|
|
646
|
+
|
|
647
|
+
## Capabilities
|
|
648
|
+
Long-context reasoning, tool use, vision.
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
#### `conversation`
|
|
652
|
+
|
|
653
|
+
An append-only chat transcript. Append-type.
|
|
654
|
+
|
|
655
|
+
REQUIRED: `type`, `thread_id`. Typical fields: `workspace_id`, `user_id`, `status`,
|
|
656
|
+
`turn_count`, `created_at`.
|
|
657
|
+
|
|
658
|
+
```markdown
|
|
659
|
+
---
|
|
660
|
+
type: conversation
|
|
661
|
+
title: "Greeting"
|
|
662
|
+
description: "A two-turn support conversation."
|
|
663
|
+
timestamp: 2026-05-16T14:00:05Z
|
|
664
|
+
thread_id: "7f3a2b1c-5d6e-4f70-8a91-b2c3d4e5f607"
|
|
665
|
+
workspace_id: "ws-acme"
|
|
666
|
+
user_id: "user-42"
|
|
667
|
+
status: active
|
|
668
|
+
created_at: 2026-05-16T14:00:00Z
|
|
669
|
+
---
|
|
670
|
+
|
|
671
|
+
### turn 1 — user — 2026-05-16T14:00:00Z
|
|
672
|
+
Hello.
|
|
673
|
+
|
|
674
|
+
### turn 2 — assistant — 2026-05-16T14:00:05Z
|
|
675
|
+
Hi — how can I help?
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
#### `thread`
|
|
679
|
+
|
|
680
|
+
An append-only chat transcript. Append-type. Canonical form of `conversation`.
|
|
681
|
+
|
|
682
|
+
REQUIRED: `type`, `thread_id`. Typical fields: `workspace_id`, `user_id`, `status`,
|
|
683
|
+
`turn_count`, `created_at`.
|
|
684
|
+
|
|
685
|
+
```markdown
|
|
686
|
+
---
|
|
687
|
+
type: thread
|
|
688
|
+
title: "Support Thread"
|
|
689
|
+
description: "A two-turn customer support thread."
|
|
690
|
+
timestamp: 2026-05-16T14:00:05Z
|
|
691
|
+
thread_id: "7f3a2b1c-5d6e-4f70-8a91-b2c3d4e5f607"
|
|
692
|
+
workspace_id: "ws-acme"
|
|
693
|
+
user_id: "user-42"
|
|
694
|
+
status: active
|
|
695
|
+
created_at: 2026-05-16T14:00:00Z
|
|
696
|
+
---
|
|
697
|
+
|
|
698
|
+
### turn 1 — user — 2026-05-16T14:00:00Z
|
|
699
|
+
Hello.
|
|
700
|
+
|
|
701
|
+
### turn 2 — assistant — 2026-05-16T14:00:05Z
|
|
702
|
+
Hi — how can I help?
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
#### `run`
|
|
706
|
+
|
|
707
|
+
An append-only workflow execution record. Append-type.
|
|
708
|
+
|
|
709
|
+
REQUIRED: `type`, `run_id`, `workflow_id`. Typical fields: `workspace_id`, `status`,
|
|
710
|
+
`task_id`, `task_path`, `step_count`, `started_at`.
|
|
711
|
+
|
|
712
|
+
`status` SHOULD be one of: `queued`, `claimed`, `running`, `waiting_for_human`,
|
|
713
|
+
`succeeded`, `failed`, `canceled`, `retrying`, `dead_letter`.
|
|
714
|
+
|
|
715
|
+
```markdown
|
|
716
|
+
---
|
|
717
|
+
type: run
|
|
718
|
+
title: "Daily Digest run 2026-05-16"
|
|
719
|
+
description: "Execution record for the 08:00 daily digest."
|
|
720
|
+
timestamp: 2026-05-16T08:00:01Z
|
|
721
|
+
run_id: "run-001"
|
|
722
|
+
workflow_id: "daily-digest"
|
|
723
|
+
task_path: "tasks/daily-digest/daily-0800-20260516T080000Z.md"
|
|
724
|
+
status: queued
|
|
725
|
+
started_at: 2026-05-16T08:00:00Z
|
|
726
|
+
---
|
|
727
|
+
|
|
728
|
+
### step 1 — gather — 2026-05-16T08:00:01Z — ok
|
|
729
|
+
Collected 12 messages.
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
#### `conflict`
|
|
733
|
+
|
|
734
|
+
A record of two contradicting primitives. Blocks promotion until resolved.
|
|
735
|
+
|
|
736
|
+
REQUIRED: `type`, `conflict_id`, `status` (`pending|resolved`), `new_slug`,
|
|
737
|
+
`existing_slug`, `detected_at`.
|
|
738
|
+
|
|
739
|
+
```markdown
|
|
740
|
+
---
|
|
741
|
+
type: conflict
|
|
742
|
+
title: "HTML vs plaintext email"
|
|
743
|
+
description: "A new memory contradicts an existing one about email format."
|
|
744
|
+
timestamp: 2026-05-16T18:30:00Z
|
|
745
|
+
conflict_id: conflict-2026-05-16-001
|
|
746
|
+
status: pending
|
|
747
|
+
new_slug: use-html-email
|
|
748
|
+
existing_slug: use-plaintext-email
|
|
749
|
+
detected_at: 2026-05-16T18:30:00Z
|
|
750
|
+
---
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
#### `page`
|
|
754
|
+
|
|
755
|
+
A VFS-native sandboxed custom workspace page.
|
|
756
|
+
|
|
757
|
+
REQUIRED: `type`, `slug`, `name`, `sandbox_entry`.
|
|
758
|
+
|
|
759
|
+
```markdown
|
|
760
|
+
---
|
|
761
|
+
type: page
|
|
762
|
+
title: "Leads Dashboard"
|
|
763
|
+
description: "A sandboxed dashboard of inbound leads."
|
|
764
|
+
timestamp: 2026-05-16T14:00:00Z
|
|
765
|
+
slug: "leads-portal"
|
|
766
|
+
name: "Leads Dashboard"
|
|
767
|
+
icon: "users"
|
|
768
|
+
layout: "split-chat"
|
|
769
|
+
sandbox_entry: "index.html"
|
|
770
|
+
---
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
#### `migration`
|
|
774
|
+
|
|
775
|
+
An SSSS schema migration record.
|
|
776
|
+
|
|
777
|
+
REQUIRED: `type`, `migration_id`, `from_version`, `to_version`, `status`, `description`.
|
|
778
|
+
|
|
779
|
+
```markdown
|
|
780
|
+
---
|
|
781
|
+
type: migration
|
|
782
|
+
title: "Add vector_embedding field"
|
|
783
|
+
timestamp: 2026-05-16T14:00:00Z
|
|
784
|
+
migration_id: "mig-v2-to-v3"
|
|
785
|
+
from_version: 2
|
|
786
|
+
to_version: 3
|
|
787
|
+
status: pending
|
|
788
|
+
description: "Add vector_embedding field"
|
|
789
|
+
---
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
#### `release`
|
|
793
|
+
|
|
794
|
+
An SSSS system schema version release.
|
|
795
|
+
|
|
796
|
+
REQUIRED: `type`, `release_id`, `version`, `schema_version`, `summary`, `released_at`.
|
|
797
|
+
|
|
798
|
+
```markdown
|
|
799
|
+
---
|
|
800
|
+
type: release
|
|
801
|
+
title: "Release 3.1.0"
|
|
802
|
+
description: "Adds proposal and migration file types."
|
|
803
|
+
timestamp: 2026-05-16T12:00:00Z
|
|
804
|
+
release_id: "rel-3.1.0"
|
|
805
|
+
version: "3.1.0"
|
|
806
|
+
schema_version: 3
|
|
807
|
+
summary: "Added proposal and migration file types"
|
|
808
|
+
released_at: 2026-05-16T12:00:00Z
|
|
809
|
+
---
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
#### `primitive`
|
|
813
|
+
|
|
814
|
+
A governed definition for a namespaced primitive that can be authored in any human
|
|
815
|
+
language without changing the core registry. REQUIRED: `type`, `primitive_id`,
|
|
816
|
+
`namespace`, `version`, `name`, `mutation`, `portability`, `scopes`, and `fields`.
|
|
817
|
+
Stable primitive, field, enum, capability, and action identifiers are symbolic and
|
|
818
|
+
language-independent. Labels and descriptions are multilingual presentation data.
|
|
819
|
+
|
|
820
|
+
The definition is validated against a meta-schema on write:
|
|
821
|
+
|
|
822
|
+
| Field | Rule |
|
|
823
|
+
|-------|------|
|
|
824
|
+
| `primitive_id` | Qualified (`<namespace>:<id>`), prefix equal to `namespace`. A host MAY derive it from `namespace` + `name` so an author never types one. |
|
|
825
|
+
| `namespace` | Lowercase, `[a-z][a-z0-9_-]{0,63}`. |
|
|
826
|
+
| `version` | Positive integer. `revision`, if present, is a positive integer. |
|
|
827
|
+
| `mutation` | `replace` or `append` (§5.3). |
|
|
828
|
+
| `portability` | A §5.5 class. |
|
|
829
|
+
| `scopes` | Non-empty unique subset of `system`, `account`, `workspace`, `user`. |
|
|
830
|
+
| `fields[]` | Each has a unique symbolic `id`, a non-empty `name` in any language, and a `kind`: `string`, `text`, `number`, `integer`, `boolean`, `datetime`, `date`, `enum` (with unique `values`), `object`, `array`, `reference`, `secret_reference`, or `resource_reference`. |
|
|
831
|
+
| `capabilities` | Optional map of action → list of capability strings. |
|
|
832
|
+
| `aliases` | Optional list of unique safe identifiers that resolve to this primitive. |
|
|
833
|
+
|
|
834
|
+
```markdown
|
|
835
|
+
---
|
|
836
|
+
type: primitive
|
|
837
|
+
title: "Booking"
|
|
838
|
+
description: "A customer booking, authored in Japanese."
|
|
839
|
+
timestamp: 2026-05-16T14:00:00Z
|
|
840
|
+
primitive_id: "acme:booking"
|
|
841
|
+
namespace: acme
|
|
842
|
+
version: 1
|
|
843
|
+
name: "顧客予約"
|
|
844
|
+
language: ja
|
|
845
|
+
mutation: replace
|
|
846
|
+
portability: tenant_private
|
|
847
|
+
scopes: [workspace]
|
|
848
|
+
fields:
|
|
849
|
+
- id: reservation_date
|
|
850
|
+
name: "予約日"
|
|
851
|
+
kind: datetime
|
|
852
|
+
required: true
|
|
853
|
+
- id: status
|
|
854
|
+
name: "状態"
|
|
855
|
+
kind: enum
|
|
856
|
+
values: [pending, confirmed]
|
|
857
|
+
capabilities:
|
|
858
|
+
create: ["acme.booking:create"]
|
|
859
|
+
---
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
### 5.5 Portability Classification
|
|
863
|
+
|
|
864
|
+
A workspace is not one undifferentiated blob. Some of it is the **reusable business
|
|
865
|
+
model** (the workflows, assistants, pages, and rules that make the business run); some is
|
|
866
|
+
the operator's **private operational data** (customer transcripts, runtime work); and some
|
|
867
|
+
is **bound to a real external resource** (a domain, a phone number, a mailbox). Selling,
|
|
868
|
+
templating, and backing up a workspace each need a different slice. SSSS makes that slice
|
|
869
|
+
declarable instead of ad-hoc.
|
|
870
|
+
|
|
871
|
+
Every **document** primitive declares one **portability class** in its registry entry.
|
|
872
|
+
(Contract primitives — `operation`/`patch`/`event`/`lease` — are protocol envelopes, not
|
|
873
|
+
stored documents, and have no portability class.)
|
|
874
|
+
|
|
875
|
+
| Class | Meaning | May appear in template/sale? |
|
|
876
|
+
|-------|---------|------------------------------|
|
|
877
|
+
| `structural` | The reusable business model — carries no operator- or customer-specific data. | ✅ verbatim |
|
|
878
|
+
| `tenant_private` | The operator's private operational data (customers, transcripts, runtime work). | ❌ never — backup only, encrypted at rest |
|
|
879
|
+
| `resource_bound` | Requires a real external resource bound at provision time. | ⚠️ as a **requirement declaration** only — the seller's resource value is stripped |
|
|
880
|
+
|
|
881
|
+
A file MAY override its type's default with an `x_portability` frontmatter field. A host
|
|
882
|
+
MUST honor the **most restrictive** of (type default, instance override), ordered
|
|
883
|
+
`structural` < `resource_bound` < `tenant_private`. A file may make itself more private
|
|
884
|
+
than its type, never less. An unrecognized override value is ignored.
|
|
885
|
+
|
|
886
|
+
An **export profile** is simply a filter over portability classes:
|
|
887
|
+
|
|
888
|
+
- **`backup`** — all three classes. `tenant_private` MUST be encrypted at rest.
|
|
889
|
+
- **`template`** / **`sale`** — `structural` verbatim; `resource_bound` reduced to a
|
|
890
|
+
requirement declaration; `tenant_private` **dropped entirely**.
|
|
891
|
+
|
|
892
|
+
A requirement declaration is produced mechanically. The type's registry entry MUST
|
|
893
|
+
declare `resource.binds`, the list of fields that hold the bound value. Each of
|
|
894
|
+
those fields is replaced with the literal string `REQUIREMENT`, and the file is
|
|
895
|
+
marked `x_portability: resource_bound`. An exporter MUST fail rather than emit a
|
|
896
|
+
`resource_bound` file whose type declares no `resource.binds`, because it cannot
|
|
897
|
+
know which fields to strip.
|
|
898
|
+
|
|
899
|
+
A host MUST reject a `template`/`sale` export that would emit a `tenant_private` primitive.
|
|
900
|
+
This single rule is what lets an operator sell a proven business model without ever
|
|
901
|
+
shipping a customer's data — the data is `tenant_private` by classification, so it
|
|
902
|
+
physically cannot enter a sale bundle.
|
|
903
|
+
|
|
904
|
+
### 5.6 Registries, Qualified Identities & Extensions
|
|
905
|
+
|
|
906
|
+
The type registry is data: `registry/core.json` defines the core primitives, and an
|
|
907
|
+
**extension registry** adds a namespace of its own. Every primitive has a
|
|
908
|
+
**qualified identity**: `ssss:<type>` for core types and `<registry>:<type>` for an
|
|
909
|
+
extension's. A document MAY declare either the bare or the qualified name in
|
|
910
|
+
`type`. Hosts resolve both, and canonical events always record the qualified one
|
|
911
|
+
(§8.2).
|
|
912
|
+
|
|
913
|
+
A registry entry declares the rules that validation (§9) enforces:
|
|
914
|
+
|
|
915
|
+
| Key | Effect |
|
|
916
|
+
|-----|--------|
|
|
917
|
+
| `required_fields` | Fields that MUST be present and non-empty. |
|
|
918
|
+
| `required_when` | `{ "<field>==<value>": [fields] }`: fields required only when the condition holds. |
|
|
919
|
+
| `enums` | Allowed values per field. Array values are checked element by element. |
|
|
920
|
+
| `patterns` | A Unicode regular expression each value of the field MUST match. |
|
|
921
|
+
| `immutable_fields` | Fields a `patch` MUST NOT change. |
|
|
922
|
+
| `references` | Fields holding vault paths, with `allowed_types`, `disallowed_types`, `allowed_portability`, `must_exist`, and an optional `hash_field` pinning the target's content hash. |
|
|
923
|
+
| `append_only` | `true` makes the type append-type (§5.3). |
|
|
924
|
+
| `portability` | The default class (§5.5). |
|
|
925
|
+
| `capabilities` | Per-action capability requirements (§6.6). |
|
|
926
|
+
| `lease_required` | `true` makes every write to the type require a lease (§7). |
|
|
927
|
+
| `resource.binds` | Fields stripped to a requirement declaration on sale export (§5.5). |
|
|
928
|
+
|
|
929
|
+
Composition rules:
|
|
930
|
+
|
|
931
|
+
- An extension MUST NOT redefine or shadow a core type, a sibling extension's type,
|
|
932
|
+
or an existing alias. A collision is a load-time error, not a runtime preference.
|
|
933
|
+
- An extension MAY declare `requires: { "<extension>": "<semver range>" }`. A host
|
|
934
|
+
MUST refuse to load an extension whose dependencies are missing or out of range.
|
|
935
|
+
- A host MAY pin a composed registry set with an **integrity lock**: a SHA-256 over
|
|
936
|
+
the canonical composition. A lock that no longer matches is drift, and a host
|
|
937
|
+
SHOULD refuse to start on it.
|
|
938
|
+
- Extension registry files MUST be regular files. A host MUST refuse symlinked
|
|
939
|
+
registry files.
|
|
940
|
+
|
|
941
|
+
---
|
|
942
|
+
|
|
943
|
+
## 6. The Operation Contract
|
|
944
|
+
|
|
945
|
+
All agent-generated mutations to a vault MUST flow through the Operation Contract.
|
|
946
|
+
An agent MUST NOT write vault files directly.
|
|
947
|
+
|
|
948
|
+
### 6.1 The Envelope
|
|
949
|
+
|
|
950
|
+
An envelope is a JSON object:
|
|
951
|
+
|
|
952
|
+
```jsonc
|
|
953
|
+
{
|
|
954
|
+
"type": "operation", // "operation" | "patch" | "event" | "delete" (§6.2)
|
|
955
|
+
"workspace_id": "ws-acme", // the vault/workspace scope
|
|
956
|
+
"idempotency_key": "b3c1…", // §6.4
|
|
957
|
+
"path": "assistants/bot/ASSISTANT.md", // vault-relative, no leading "/", no "." or ".." segments
|
|
958
|
+
"content": "---\ntype: …", // operation: full document; event: JSON payload string
|
|
959
|
+
"patches": { }, // patch only
|
|
960
|
+
"primitive_type": "assistant", // OPTIONAL — asserts the target's type
|
|
961
|
+
"operation_id": "uuid", // OPTIONAL — client-chosen; generated if absent
|
|
962
|
+
"lease_id": "uuid", // OPTIONAL — §7
|
|
963
|
+
"dry_run": false, // OPTIONAL — validate and authorize without committing
|
|
964
|
+
"intent": "human description" // OPTIONAL — audit annotation, not interpreted
|
|
965
|
+
}
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
The envelope carries **no identity**. Who is acting is supplied by the host as a
|
|
969
|
+
verified principal, next to the envelope and never inside it (§6.6). A kernel
|
|
970
|
+
MUST ignore any identity-bearing field a client places in the envelope, including
|
|
971
|
+
the pre-0.9 `actor` object.
|
|
972
|
+
|
|
973
|
+
A `path` MUST be rejected with `invalid_request` if it is empty, absolute, contains
|
|
974
|
+
a backslash or NUL, or has an empty, `.`, or `..` segment. A host MUST also refuse
|
|
975
|
+
to follow a symbolic link out of the vault.
|
|
976
|
+
|
|
977
|
+
### 6.2 Envelope Types
|
|
978
|
+
|
|
979
|
+
| `type` | Semantics | Required fields beyond `type`, `workspace_id`, `idempotency_key`, `path` |
|
|
980
|
+
|--------|-----------|------------|
|
|
981
|
+
| `operation` | Create a document, or fully replace an existing one. | `content` (string) |
|
|
982
|
+
| `patch` | Merge into an existing document's frontmatter and/or body. | `patches` (object) |
|
|
983
|
+
| `event` | Append an immutable entry to the event log (§8). Never touches a document. | none; `content`, if present, MUST be a JSON string |
|
|
984
|
+
| `delete` | Remove a replace-type document. | none |
|
|
985
|
+
|
|
986
|
+
**`operation`** — `content` is the complete document. An `operation` MUST NOT
|
|
987
|
+
change the `type` of an existing document; changing type is a migration
|
|
988
|
+
(§5.4 `migration`). Writing a directory `index.md` (§4.3) is permitted and is
|
|
989
|
+
not validated as a primitive.
|
|
990
|
+
|
|
991
|
+
**`patch`** — keys of `patches` are merged shallowly into the frontmatter: a
|
|
992
|
+
supplied key replaces that key's whole value, and absent keys are untouched. The
|
|
993
|
+
reserved key `__body__` replaces the Markdown body of a replace-type document, or
|
|
994
|
+
is appended to the body of an append-type document (§5.3). A `patch` MUST NOT
|
|
995
|
+
change `type` or any of the type's `immutable_fields`. The merged document is
|
|
996
|
+
validated as a whole before commit.
|
|
997
|
+
|
|
998
|
+
**`event`** — the parsed `content` becomes the event's `payload` (§8.2). The target
|
|
999
|
+
`path` is recorded as the event's `subject` and need not exist.
|
|
1000
|
+
|
|
1001
|
+
**`delete`** — the host removes the document and records the deletion as an event,
|
|
1002
|
+
so history is never lost. A `delete` targeting an append-type document MUST be
|
|
1003
|
+
rejected with `validation_failed`. A `delete` of a path that does not exist MUST be
|
|
1004
|
+
rejected with `not_found`, unless it is an idempotent replay of an earlier delete
|
|
1005
|
+
with the same key, in which case the original result is returned (§6.4). `delete`
|
|
1006
|
+
is the only envelope that removes vault state. Export and provision (§16, §17)
|
|
1007
|
+
never delete.
|
|
1008
|
+
|
|
1009
|
+
### 6.3 The Processing Pipeline
|
|
1010
|
+
|
|
1011
|
+
A host MUST process every envelope through these stages, in this order. A failure
|
|
1012
|
+
at any stage before the commit point aborts the envelope with nothing committed.
|
|
1013
|
+
Kernels MUST NOT reorder or skip stages. Adapters (§6.7) implement mechanics only.
|
|
1014
|
+
|
|
1015
|
+
| # | Stage | On failure |
|
|
1016
|
+
|---|-------|------------|
|
|
1017
|
+
| 1 | **Envelope & identity** — known `type`, required fields present and well-typed (§6.2); a principal with an `id` and `kind` is attached. Nothing is read before this passes, so an anonymous caller learns nothing about the vault. | `invalid_request`, `unauthorized` |
|
|
1018
|
+
| 2 | **Idempotency** — look up (`workspace_id`, `idempotency_key`). Same request hash: return the stored response with `replay: true` and stop. Different hash: stop (§6.4). | `idempotency_conflict` |
|
|
1019
|
+
| 3 | **Read current state** — load the target, if any, and its version. An unsafe path fails here. | `invalid_request`, or `internal_error` for unreadable stored state |
|
|
1020
|
+
| 4 | **Content validation** — resolve the primitive and validate the resulting document (§9). Enforce type immutability, `immutable_fields`, patch/delete target existence, and append-only rules. | `not_found`, `validation_failed` |
|
|
1021
|
+
| 5 | **Authorization** — the verified principal must be complete (§6.6), scoped to `workspace_id`, and hold every capability the primitive and action require. | `unauthorized`, `forbidden` |
|
|
1022
|
+
| 6 | **Lease** — if the primitive is `lease_required` or the host requires a lease for this path, verify it (§7). | `lease_conflict` |
|
|
1023
|
+
| 7 | **Resource prepare** — if the host coordinates an external resource (a domain, a phone number), reserve it. | `internal_error` |
|
|
1024
|
+
| 8 | **Dry-run exit** — if `dry_run`, release anything prepared in stage 7 and return success with `committed_at: null`. | — |
|
|
1025
|
+
| 9 | **Commit** — under the path's commit intent (below), write the document with a compare-and-swap on the version read in stage 3 (create-if-absent for a new document), or remove it for `delete`. `event` envelopes write nothing here. | `version_conflict`, `internal_error` |
|
|
1026
|
+
| 10 | **Event append — the commit point.** Append the canonical event (§8.2). If the append fails, undo stage 9 and release stage 7. | `internal_error` |
|
|
1027
|
+
| 11 | **Resource finalize** — complete the external effect. A failure here MUST NOT undo the commit. The host reconciles and the response carries a warning. | warning only |
|
|
1028
|
+
| 12 | **Projections** — dispatch the event to projections (§10). A failure is a warning, never a rollback. | warning only |
|
|
1029
|
+
| 13 | **Record idempotency** — store the response under the key (§6.4). | warning only |
|
|
1030
|
+
|
|
1031
|
+
**The commit point.** A mutation is committed exactly when its canonical event is
|
|
1032
|
+
durably appended. Before that, every failure leaves no trace in the vault. After
|
|
1033
|
+
it, nothing may undo the mutation, because the append-only log (§8) already says it
|
|
1034
|
+
happened. Work after the commit point is compensated forward, never rolled back.
|
|
1035
|
+
|
|
1036
|
+
**Commit intents.** A process can die between stage 9 and stage 10, leaving the
|
|
1037
|
+
vault ahead of the log. A host whose vault outlives the process MUST therefore,
|
|
1038
|
+
before stage 9, durably record a *commit intent* for the path and hold the path's
|
|
1039
|
+
commit lock until stage 10 ends. The intent names the `event_id` the commit will
|
|
1040
|
+
append, the prior content (or its absence), and a digest of the intended content.
|
|
1041
|
+
Durably means the intent, then the stage-9 write, then the stage-10 append each
|
|
1042
|
+
reach stable storage before the next begins.
|
|
1043
|
+
|
|
1044
|
+
An intent whose lock is free belongs to a writer that died. Before the next commit
|
|
1045
|
+
on that path, and before a kernel's first operation, the host resolves it:
|
|
1046
|
+
|
|
1047
|
+
| The log holds the intent's `event_id` | The document now | Outcome |
|
|
1048
|
+
|---|---|---|
|
|
1049
|
+
| yes | anything | `committed` — the commit stands |
|
|
1050
|
+
| no | has the intended digest (absent, for `delete`) | `rolled_back` — the prior content is restored |
|
|
1051
|
+
| no | has the prior digest (absent, if it had none) | `discarded` — stage 9 never landed |
|
|
1052
|
+
| no | anything else | `conflict` — written outside the kernel; the intent is kept aside and reported |
|
|
1053
|
+
|
|
1054
|
+
An intent whose writer is alive is never resolved. A writer that read uncommitted
|
|
1055
|
+
state fails its stage-9 compare-and-swap after the rollback and retries on the real
|
|
1056
|
+
state, so nothing is ever committed on top of a write that did not commit. The
|
|
1057
|
+
reference `FileSystemVfs` supplies a journal under the reserved `.ssss-intents/`
|
|
1058
|
+
directory; `kernel.recover()` runs resolution on demand.
|
|
1059
|
+
|
|
1060
|
+
**Resource lifecycle.** If a host coordinates external resources, every successful
|
|
1061
|
+
`prepare` MUST be followed by exactly one `finalize` or `reconcile`. `reconcile`
|
|
1062
|
+
receives the phase it runs in (`dry_run`, `commit`, or `finalize`), so the host
|
|
1063
|
+
can tell a released reservation from a compensation after a committed intent.
|
|
1064
|
+
|
|
1065
|
+
**Concurrency.** Two envelopes racing on one path are serialized by the stage-9
|
|
1066
|
+
compare-and-swap. The loser either finds a winner with the same key and returns
|
|
1067
|
+
that winner's result as a replay, or fails with `version_conflict`. Two `event`
|
|
1068
|
+
envelopes with the same key are serialized by the event store's uniqueness on
|
|
1069
|
+
`event_id` (§8.1).
|
|
1070
|
+
|
|
1071
|
+
### 6.4 Idempotency
|
|
1072
|
+
|
|
1073
|
+
Every envelope carries an `idempotency_key`, scoped to its `workspace_id`.
|
|
1074
|
+
|
|
1075
|
+
- The **request hash** is SHA-256 over the canonical JSON (keys sorted
|
|
1076
|
+
recursively, no insignificant whitespace) of `type`, `workspace_id`,
|
|
1077
|
+
`idempotency_key`, `path`, `primitive_type`, `content`, `patches`, and the
|
|
1078
|
+
verified principal's `id`. `dry_run`, `lease_id`, `operation_id`, and `intent`
|
|
1079
|
+
are excluded, so retrying with a fresh lease is still the same request.
|
|
1080
|
+
- Only **successful commits** are recorded. A failed or dry-run envelope records
|
|
1081
|
+
nothing, so it may be retried under the same key after the cause is fixed.
|
|
1082
|
+
- A recorded key replays: the same request hash returns the original response with
|
|
1083
|
+
`replay: true` and changes nothing. A different request hash under the same key
|
|
1084
|
+
fails with `idempotency_conflict`.
|
|
1085
|
+
- Because the principal is part of the hash, a key replayed by a *different*
|
|
1086
|
+
principal is a conflict, never a disclosure of another principal's result.
|
|
1087
|
+
- **The event log is the durable record.** Every committed envelope's event
|
|
1088
|
+
carries its `idempotency_key` and `request_hash` (§8.2), written at the commit
|
|
1089
|
+
point, so it survives anything the commit survives. The idempotency store is a
|
|
1090
|
+
cache of full responses in front of it. When the store has no record, a kernel
|
|
1091
|
+
MUST look the key up in the log if its event store supports that lookup
|
|
1092
|
+
(§6.7): the first event under the key decides. A matching hash replays a
|
|
1093
|
+
response rebuilt from the event, identical to the original except that
|
|
1094
|
+
post-commit warnings (stages 11–13) are absent; any other hash, including a
|
|
1095
|
+
missing one, is `idempotency_conflict`.
|
|
1096
|
+
- **Retention.** A host whose event store answers key lookups MAY expire store
|
|
1097
|
+
records; the reference `FileIdempotencyStore` keeps them 24 hours by default.
|
|
1098
|
+
A host whose event store cannot answer them MUST retain records indefinitely.
|
|
1099
|
+
- An `unchanged` response (an `operation` identical to the stored document)
|
|
1100
|
+
commits nothing and appends no event, so only the store holds it.
|
|
1101
|
+
|
|
1102
|
+
Keys are opaque strings. Use a random UUID for ad-hoc requests. Use a
|
|
1103
|
+
deterministic derivation where retries must converge, as with runtime-created work
|
|
1104
|
+
(§11.8) and bundle import (§17). A committed key is never reusable for a different
|
|
1105
|
+
request, so a derivation MUST cover everything that distinguishes one intended
|
|
1106
|
+
request from another: the reference bundle import derives its key from the
|
|
1107
|
+
workspace, the target path, and the file's content digest.
|
|
1108
|
+
|
|
1109
|
+
### 6.5 The Response & Error Codes
|
|
1110
|
+
|
|
1111
|
+
```jsonc
|
|
1112
|
+
{
|
|
1113
|
+
"success": true,
|
|
1114
|
+
"type": "operation",
|
|
1115
|
+
"operation_id": "uuid",
|
|
1116
|
+
"path": "assistants/bot/ASSISTANT.md",
|
|
1117
|
+
"committed_at": "2026-05-16T14:00:00Z", // null for dry_run and failures
|
|
1118
|
+
"dry_run": false,
|
|
1119
|
+
"event_id": "uuid", // the canonical event (§8.2); absent unless committed
|
|
1120
|
+
"replay": true, // present only on an idempotent replay
|
|
1121
|
+
"validation": {
|
|
1122
|
+
"valid": true,
|
|
1123
|
+
"type": "ssss:assistant", // resolved qualified type (§5.6)
|
|
1124
|
+
"errors": [],
|
|
1125
|
+
"warnings": [] // e.g. a projection or finalize failure
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
```
|
|
1129
|
+
|
|
1130
|
+
A failed response has `success: false`, `committed_at: null`, `validation.valid:
|
|
1131
|
+
false`, the `repair` block of §9, and an `error` object:
|
|
1132
|
+
|
|
1133
|
+
```jsonc
|
|
1134
|
+
{ "success": false, "error": { "code": "validation_failed", "message": "name: Missing required field 'name'." }, "…": "…" }
|
|
1135
|
+
```
|
|
1136
|
+
|
|
1137
|
+
`error.code` is exactly one of the symbolic codes below. Clients MUST branch on
|
|
1138
|
+
`error.code`, never on message text. A host MAY use any transport. If it uses HTTP,
|
|
1139
|
+
it MUST use these statuses, `201` for a commit, and `200` for a replay or dry run:
|
|
1140
|
+
|
|
1141
|
+
| Code | HTTP | Meaning |
|
|
1142
|
+
|------|------|---------|
|
|
1143
|
+
| `invalid_request` | 400 | Malformed envelope, non-JSON event content, or an unsafe path. |
|
|
1144
|
+
| `unauthorized` | 401 | No valid verified principal reached the kernel. |
|
|
1145
|
+
| `forbidden` | 403 | The principal is not scoped to the workspace or lacks a required capability. |
|
|
1146
|
+
| `not_found` | 404 | The `patch` or `delete` target does not exist. |
|
|
1147
|
+
| `lease_conflict` | 409 | A required lease is missing, mismatched, or expired. |
|
|
1148
|
+
| `version_conflict` | 409 | The document changed between read and commit, and no same-key winner exists. |
|
|
1149
|
+
| `idempotency_conflict` | 409 | The key was already used for a different request. |
|
|
1150
|
+
| `validation_failed` | 422 | The document fails §9, or the envelope would change a type, an immutable field, or an append-only document. |
|
|
1151
|
+
| `internal_error` | 500 | Unreadable stored state, a failed resource prepare, or a failed commit. Nothing was committed. |
|
|
1152
|
+
|
|
1153
|
+
### 6.6 Identity & Authorization
|
|
1154
|
+
|
|
1155
|
+
Authorization acts on a **verified principal** that the host attaches *outside* the
|
|
1156
|
+
envelope, after authenticating the caller:
|
|
1157
|
+
|
|
1158
|
+
```jsonc
|
|
1159
|
+
{
|
|
1160
|
+
"id": "user-42", // stable, recorded on every event
|
|
1161
|
+
"kind": "human", // "human" | "agent" | "service" | "system"
|
|
1162
|
+
"workspaceIds": ["ws-acme"], // workspaces this principal may act in
|
|
1163
|
+
"capabilities": ["ssss:assistant:*"], // §6.6 capability strings
|
|
1164
|
+
"authentication": { "provider": "oidc", "assurance": "verified" }
|
|
1165
|
+
}
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
- A host that exposes the Operation Contract to untrusted callers MUST derive the
|
|
1169
|
+
principal from its own authentication, never from the request body. This is why
|
|
1170
|
+
the envelope carries no identity (§6.1).
|
|
1171
|
+
- A missing or malformed principal MUST be denied as `unauthorized`. There is no
|
|
1172
|
+
default principal and no anonymous write.
|
|
1173
|
+
- A non-`system` principal MUST list the envelope's `workspace_id` in
|
|
1174
|
+
`workspaceIds`.
|
|
1175
|
+
- The required capabilities are the primitive's registry `capabilities[<action>]`,
|
|
1176
|
+
or `<qualified_type>:<action>` if none are declared. The action is one of
|
|
1177
|
+
`create`, `replace`, `patch`, `append`, `event`, or `delete`. It is derived from
|
|
1178
|
+
the envelope and the target's existence, never supplied by the client. `event`
|
|
1179
|
+
envelopes require `write:event`.
|
|
1180
|
+
- A granted capability covers a required one if it is equal, if it is `*:*`, or if
|
|
1181
|
+
it ends in `:*` and the required capability starts with everything before that
|
|
1182
|
+
`*`. So `ssss:*` covers every core primitive and `ssss:assistant:*` covers every
|
|
1183
|
+
action on assistants.
|
|
1184
|
+
- A `system` principal bypasses capability checks. It exists for trusted internal
|
|
1185
|
+
callers such as import (§17). A host MUST NOT grant `system` to anything a remote
|
|
1186
|
+
caller controls.
|
|
1187
|
+
- A host MAY layer **policy floors** (extra capabilities per primitive and action),
|
|
1188
|
+
**step-up** (a minimum `authentication.assurance`), and **human confirmation**
|
|
1189
|
+
(a `human` principal plus an explicit confirmation) on top of this. These only
|
|
1190
|
+
ever add requirements.
|
|
1191
|
+
|
|
1192
|
+
**Pre-0.9 role compatibility.** Hosts that still accept the pre-0.9 envelope field
|
|
1193
|
+
`actor.role` MUST convert it to a principal before stage 5. `system` becomes a
|
|
1194
|
+
`system` principal, `admin` receives `*:*`, and any other role receives the
|
|
1195
|
+
`permissions` of its `security_role` document at `roles/<role>/ROLE.md`, with
|
|
1196
|
+
`write:<type>` and `*:<type>` expanded to `<qualified_type>:*`, and `write:*`
|
|
1197
|
+
expanded to `*:*`. A missing role still MUST be denied, never promoted.
|
|
1198
|
+
|
|
1199
|
+
### 6.7 Adapter Contracts
|
|
1200
|
+
|
|
1201
|
+
A kernel owns the stages above. A host supplies the mechanics through adapters,
|
|
1202
|
+
each with a shared contract test in the conformance suite (§12):
|
|
1203
|
+
|
|
1204
|
+
| Adapter | Contract |
|
|
1205
|
+
|---------|----------|
|
|
1206
|
+
| **VFS** | `read(path) → {bytes, version, hash} \| null`; `writeAtomic(path, bytes, {version \| ifAbsent})` and `remove(path, {version})`, both compare-and-swap, failing with a version conflict and never a partial write. Rejects unsafe paths and symlink traversal. A VFS whose state outlives the process SHOULD provide `intentJournal()`, its co-located intent journal. |
|
|
1207
|
+
| **Event store** | `append(event)` durably and in order, rejecting a duplicate `event_id` across every writer; `replay({cursor, workspaceId})` in append order. SHOULD provide `findByIdempotencyKey(workspaceId, key) → event \| null`, the first event recorded under the key, across every writer and every rotated segment (§6.4). |
|
|
1208
|
+
| **Intent journal** | `run(path, intent, commit, {resolve})` holds the path's commit lock across `commit`, first passing any leftover intent for the path to `resolve`, and removes the intent afterward unless `commit` asks to keep it; `recover(resolve)` resolves every intent whose writer is gone and reports live ones as `in_flight` (§6.3). |
|
|
1209
|
+
| **Idempotency store** | `get(workspace, key)`; `put(workspace, key, record)`, which rejects a second `put` for the same key across every writer. |
|
|
1210
|
+
| **Lease store** | `acquire`, `verify`, `renew`, `release` per §7, with at most one live lease per target across every writer. |
|
|
1211
|
+
| **Authorizer** | `authorize({principal, workspaceId, definition, action}) → {allowed, reason}`, per §6.6. Fails closed. |
|
|
1212
|
+
| **Projection coordinator** | `dispatch(event)`, `replay(id, {rebuild})`, `detectDrift(id, hash)` per §10. |
|
|
1213
|
+
| **Resource coordinator** | Optional `prepare`, `finalize`, `reconcile`, per the §6.3 resource lifecycle. |
|
|
1214
|
+
|
|
1215
|
+
"Across every writer" means across threads, processes, and hosts sharing the same
|
|
1216
|
+
storage, not just within one instance.
|
|
1217
|
+
|
|
1218
|
+
---
|
|
1219
|
+
|
|
1220
|
+
## 7. Leases — Concurrency Control
|
|
1221
|
+
|
|
1222
|
+
A **lease** is a time-bound write claim on one `(workspace_id, target)` pair. It
|
|
1223
|
+
lets an agent do long work (such as a workflow run, §11.8) without another agent
|
|
1224
|
+
writing the same path underneath it.
|
|
1225
|
+
|
|
1226
|
+
A lease record carries `lease_id`, `workspace_id`, `target`, `principal_id`,
|
|
1227
|
+
`operation_id`, `issued_at`, and `expires_at`.
|
|
1228
|
+
|
|
1229
|
+
- **acquire** — grants a lease if no live lease exists on the target; otherwise it
|
|
1230
|
+
fails. At most one live lease MAY exist per target, across every writer.
|
|
1231
|
+
- **verify** — succeeds only if a live lease exists and its `lease_id`,
|
|
1232
|
+
`principal_id`, and `operation_id` all match the caller. A lease is bound to the
|
|
1233
|
+
principal and the unit of work that took it. Presenting someone else's
|
|
1234
|
+
`lease_id` is not enough. An envelope that writes under a lease therefore
|
|
1235
|
+
carries both `lease_id` and the `operation_id` the lease was acquired with.
|
|
1236
|
+
- **renew** — extends `expires_at` for the verified holder.
|
|
1237
|
+
- **release** — deletes the lease for the verified holder.
|
|
1238
|
+
- An expired lease is treated as absent. A host SHOULD reclaim expired leases.
|
|
1239
|
+
|
|
1240
|
+
Whether a write *requires* a lease is decided by the primitive's `lease_required`
|
|
1241
|
+
flag or by host policy. Unleased writes are otherwise protected by the stage-9
|
|
1242
|
+
compare-and-swap (§6.3). Leases are coordination, not security. Authorization
|
|
1243
|
+
(§6.6) is the security boundary.
|
|
1244
|
+
|
|
1245
|
+
---
|
|
1246
|
+
|
|
1247
|
+
## 8. The Event Log
|
|
1248
|
+
|
|
1249
|
+
The event log is an **append-only, immutable** record. It is two things at once: a
|
|
1250
|
+
flat physical *log*, and a relational *event graph* layered on top of it.
|
|
1251
|
+
|
|
1252
|
+
### 8.1 The Log
|
|
1253
|
+
|
|
1254
|
+
- Every committed envelope appends exactly one canonical event (§6.3 stage 10). The
|
|
1255
|
+
event *is* the audit record. There is no separate audit write.
|
|
1256
|
+
- An entry, once written, MUST NOT be updated or deleted.
|
|
1257
|
+
- `event_id` is unique within the log, across every writer. For `event` envelopes,
|
|
1258
|
+
a kernel SHOULD derive `event_id` deterministically from (`workspace_id`,
|
|
1259
|
+
`idempotency_key`). The reference uses UUIDv5 over their JSON array. That way,
|
|
1260
|
+
concurrent duplicates of one event collide on the uniqueness check instead of
|
|
1261
|
+
both landing. Other envelopes get their `event_id` before stage 9, so a commit
|
|
1262
|
+
intent (§6.3) can name the event that decides it.
|
|
1263
|
+
- The log is also the durable idempotency record (§6.4): the first event recorded
|
|
1264
|
+
under an `idempotency_key` decides every later request with that key.
|
|
1265
|
+
- Order is append order within a log.
|
|
1266
|
+
- The event log is the canonical history of *what happened*; the vault is the
|
|
1267
|
+
canonical state of *what is true now*. Both are source-of-truth; projections are
|
|
1268
|
+
not.
|
|
1269
|
+
|
|
1270
|
+
### 8.2 The Event Record
|
|
1271
|
+
|
|
1272
|
+
An event is a typed record, **not** a document primitive. There is no `EVENT.md`;
|
|
1273
|
+
events exist only in the log.
|
|
1274
|
+
|
|
1275
|
+
| Field | Description |
|
|
1276
|
+
|-------|-------------|
|
|
1277
|
+
| `event_id` | Unique identity (§8.1). |
|
|
1278
|
+
| `event_type` | The kind of event. Kernel mutations use `ssss.mutation`. |
|
|
1279
|
+
| `schema_version` | Version of this record shape; currently `1`. |
|
|
1280
|
+
| `timestamp` | ISO 8601 commit time. |
|
|
1281
|
+
| `workspace_id` | The workspace. |
|
|
1282
|
+
| `primitive_id`, `primitive_version` | The qualified type (§5.6) and its version. `ssss:event` for `event` envelopes. |
|
|
1283
|
+
| `action` | `create`, `replace`, `patch`, `append`, `event`, or `delete`. |
|
|
1284
|
+
| `subject` | The vault path the event is about. |
|
|
1285
|
+
| `principal` | The verified principal (§6.6) that caused it. |
|
|
1286
|
+
| `operation_id`, `idempotency_key`, `request_hash` | Ties the event to its envelope (§6.4). |
|
|
1287
|
+
| `correlation_id` | Groups every event of one logical flow ("saga"). Defaults to `operation_id`. |
|
|
1288
|
+
| `causation_id` | The `event_id` that directly caused this one, or `null`. |
|
|
1289
|
+
| `before_hash`, `after_hash` | Content hashes of the target before and after, so the log alone can detect vault drift. |
|
|
1290
|
+
| `changed_fields` | For `patch`, the sorted keys that were patched. |
|
|
1291
|
+
| `resource_status` | `prepared` if a resource coordinator took part (§6.3), else `null`. |
|
|
1292
|
+
| `payload` | The event-type-specific body (for `event` envelopes, the parsed `content`). |
|
|
1293
|
+
|
|
1294
|
+
### 8.3 The Event Graph
|
|
1295
|
+
|
|
1296
|
+
Events relate to **non-adjacent** events: a feedback event refers to the completion
|
|
1297
|
+
it rated, an investigation to the feedback that triggered it, a fix to the
|
|
1298
|
+
investigation. These references form a causal **graph** over the flat log.
|
|
1299
|
+
|
|
1300
|
+
The graph is preserved without violating append-only because **edges point backward
|
|
1301
|
+
only**: a new event records its `causation_id` parent; an existing event is never
|
|
1302
|
+
mutated to record a child. The forward view (an event's children, a full saga tree)
|
|
1303
|
+
is reconstructed by scanning — so the **event graph is a derived artifact** (§10),
|
|
1304
|
+
disposable and rebuildable. Relationships are canonical (backward `event_id`
|
|
1305
|
+
references stored in the log); the graph index is only a cache.
|
|
1306
|
+
|
|
1307
|
+
A **session** is not a separate primitive — it is a named, `correlation_id`-scoped
|
|
1308
|
+
slice of the event graph: the saga tree of one coherent unit of work.
|
|
1309
|
+
|
|
1310
|
+
---
|
|
1311
|
+
|
|
1312
|
+
## 9. Validation & Repair
|
|
1313
|
+
|
|
1314
|
+
Validation is **deterministic**: identical input always yields an identical verdict.
|
|
1315
|
+
It is a pure function of the document and the composed registry (§5.6).
|
|
1316
|
+
|
|
1317
|
+
A document is valid if and only if:
|
|
1318
|
+
|
|
1319
|
+
1. Its frontmatter parses (§4.5) to an object.
|
|
1320
|
+
2. `type` resolves, bare or qualified, to a primitive in the registry.
|
|
1321
|
+
3. Every universal field (§4.2) and every `required_fields` entry is present and
|
|
1322
|
+
non-empty. `null`, `""`, and `[]` count as empty.
|
|
1323
|
+
4. Every `required_when` condition that holds has its fields present and non-empty.
|
|
1324
|
+
5. Every present field with an `enums` entry takes allowed values only.
|
|
1325
|
+
6. Every present field with a `patterns` entry matches it.
|
|
1326
|
+
7. Every `references` field names a safe vault path to an existing document (unless
|
|
1327
|
+
`must_exist: false`) of an allowed type and portability. If a `hash_field` is
|
|
1328
|
+
declared, it equals the target's current content hash.
|
|
1329
|
+
8. A `primitive` document also satisfies the §5.4 `primitive` meta-schema.
|
|
1330
|
+
|
|
1331
|
+
On top of the document rules, stage 4 (§6.3) rejects an envelope that would change
|
|
1332
|
+
a document's `type`, change an `immutable_fields` value, rewrite an append-type
|
|
1333
|
+
document, or delete one.
|
|
1334
|
+
|
|
1335
|
+
Hosts MUST NOT reject a document because of unknown frontmatter keys. Unknown keys
|
|
1336
|
+
MUST be preserved to support agent scratchpads and forward compatibility with OKF
|
|
1337
|
+
extensions.
|
|
1338
|
+
|
|
1339
|
+
On failure, the host MUST return structured **repair feedback** so an agent can
|
|
1340
|
+
self-correct without guesswork:
|
|
1341
|
+
|
|
1342
|
+
```jsonc
|
|
1343
|
+
{
|
|
1344
|
+
"repair": {
|
|
1345
|
+
"field_errors": [
|
|
1346
|
+
{ "field": "name", "issue": "Missing required field 'name'." }
|
|
1347
|
+
]
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
```
|
|
1351
|
+
|
|
1352
|
+
A host MAY also emit non-blocking `warnings` (deprecated fields, low-confidence
|
|
1353
|
+
content, a failed projection). Warnings MUST NOT block a commit.
|
|
1354
|
+
|
|
1355
|
+
---
|
|
1356
|
+
|
|
1357
|
+
## 10. Derived Artifacts
|
|
1358
|
+
|
|
1359
|
+
Hosts MAY maintain derived artifacts for performance: search indexes, embedding
|
|
1360
|
+
indexes, graph indexes, the **event-graph index** (the forward/causal view of the
|
|
1361
|
+
event log — see §8.3), routing logs, and relational **projections** of vault data.
|
|
1362
|
+
|
|
1363
|
+
All derived artifacts are **disposable**:
|
|
1364
|
+
|
|
1365
|
+
- They MUST be fully rebuildable from the vault alone.
|
|
1366
|
+
- They MUST NOT be treated as source-of-truth for product meaning.
|
|
1367
|
+
- Deleting them MUST NOT lose information — only force a rebuild.
|
|
1368
|
+
|
|
1369
|
+
A host that maintains projections MUST provide a means to (a) rebuild a projection
|
|
1370
|
+
from a vault scan, and (b) detect and repair drift between a projection and the
|
|
1371
|
+
vault.
|
|
1372
|
+
|
|
1373
|
+
### 10.1 Projection Manifest
|
|
1374
|
+
|
|
1375
|
+
A host SHOULD maintain a `MANIFEST.json` (or equivalent) in the derived-artifacts
|
|
1376
|
+
directory declaring each projection as disposable and recording provenance:
|
|
1377
|
+
|
|
1378
|
+
```jsonc
|
|
1379
|
+
{
|
|
1380
|
+
"type": "projection-manifest",
|
|
1381
|
+
"generated_at": "2026-05-30T06:35:00Z",
|
|
1382
|
+
"vault_hash": "sha256:abc123...", // content hash of the vault at build time
|
|
1383
|
+
"projections": [
|
|
1384
|
+
{ "file": "graph-index.jsonl", "disposable": true },
|
|
1385
|
+
{ "file": "memory-layers.jsonl", "disposable": true },
|
|
1386
|
+
{ "file": "embeddings.json", "disposable": true }
|
|
1387
|
+
],
|
|
1388
|
+
"rebuild_command": "<host-defined rebuild command>"
|
|
1389
|
+
}
|
|
1390
|
+
```
|
|
1391
|
+
|
|
1392
|
+
The `vault_hash` field enables **staleness detection**: if the current vault hash
|
|
1393
|
+
differs from the manifest's hash, the projections are stale and SHOULD be rebuilt.
|
|
1394
|
+
This supports **incremental compilation** — a host MAY skip recompilation when the
|
|
1395
|
+
hash matches, avoiding redundant I/O on unchanged vaults.
|
|
1396
|
+
|
|
1397
|
+
---
|
|
1398
|
+
|
|
1399
|
+
## 11. The Semantic Layer
|
|
1400
|
+
|
|
1401
|
+
SSSS operates as two layers with deliberately opposite requirements. A conformant
|
|
1402
|
+
host MUST keep them separate.
|
|
1403
|
+
|
|
1404
|
+
### 11.1 The Two Layers
|
|
1405
|
+
|
|
1406
|
+
**The deterministic layer** — the file format (§4), type registry (§5), Operation
|
|
1407
|
+
Contract (§6), leases (§7), event log (§8), and validation (§9). It is exact and
|
|
1408
|
+
reproducible: identical input yields an identical verdict. It is language-independent
|
|
1409
|
+
*by construction*: its control vocabulary — every frontmatter **key** and every
|
|
1410
|
+
enumerated **value** (`type`, `modality`, `status`, the primitive type names, …) —
|
|
1411
|
+
consists of **stable symbolic identifiers**, never localized words. They are never
|
|
1412
|
+
translated, exactly as a JSON key or an HTTP method is never translated. Only
|
|
1413
|
+
natural-language *content* (§11.2) carries language. A host MUST NOT make this layer
|
|
1414
|
+
fuzzy.
|
|
1415
|
+
|
|
1416
|
+
**The semantic layer** — retrieval, routing, deduplication, conflict detection, and
|
|
1417
|
+
memory surfacing. It operates on **meaning**, not on shared surface tokens. This is
|
|
1418
|
+
where natural-language content lives, and where SSSS becomes genuinely
|
|
1419
|
+
language-independent. A host MUST NOT make this layer depend on lexical token overlap
|
|
1420
|
+
alone.
|
|
1421
|
+
|
|
1422
|
+
### 11.2 Language Independence
|
|
1423
|
+
|
|
1424
|
+
Natural-language fields — `title`, `description`, document bodies, feedback comments —
|
|
1425
|
+
MAY be authored in ANY language. The semantic layer MUST treat documents by meaning,
|
|
1426
|
+
so a node authored in one language remains retrievable, routable, and
|
|
1427
|
+
conflict-checkable against a query or node in any other. Implementations SHOULD use a
|
|
1428
|
+
multilingual embedding model so that all languages share a single vector space.
|
|
1429
|
+
|
|
1430
|
+
### 11.3 The Embedding Index
|
|
1431
|
+
|
|
1432
|
+
The semantic layer is backed by an **embedding index** — a derived artifact (§10):
|
|
1433
|
+
disposable, never source-of-truth, fully rebuildable from the vault.
|
|
1434
|
+
|
|
1435
|
+
- It MUST record the `embedding_model` and vector `dim` that produced it. Embeddings
|
|
1436
|
+
are not comparable across models; a model change REQUIRES a full reindex.
|
|
1437
|
+
- A host MUST maintain exactly **one canonical embedding implementation**. Parallel
|
|
1438
|
+
or duplicate embedding systems (e.g. one in JSON format and another in JSONL)
|
|
1439
|
+
create consistency risks where a node is indexed in one but not the other.
|
|
1440
|
+
- Retrieval SHOULD be **hybrid**: an exact/lexical pass (strong for slugs,
|
|
1441
|
+
identifiers, and code) fused with a dense semantic pass (strong for meaning and
|
|
1442
|
+
cross-lingual matches).
|
|
1443
|
+
|
|
1444
|
+
### 11.4 Provenance
|
|
1445
|
+
|
|
1446
|
+
So that outcomes can be attributed to their causes, append-type records (conversation
|
|
1447
|
+
turns, run steps) SHOULD capture **provenance**: the set of primitives that produced
|
|
1448
|
+
the record — e.g. the assistant, model, skills, and memory nodes in play. Provenance
|
|
1449
|
+
is what lets a quality signal on an outcome propagate back to the primitives
|
|
1450
|
+
responsible for it.
|
|
1451
|
+
|
|
1452
|
+
### 11.5 The `feedback` Block
|
|
1453
|
+
|
|
1454
|
+
Any document primitive MAY carry an OPTIONAL `feedback` frontmatter block — a
|
|
1455
|
+
derived, language-neutral rollup of feedback signal:
|
|
1456
|
+
|
|
1457
|
+
```yaml
|
|
1458
|
+
feedback:
|
|
1459
|
+
score: 0.82 # 0..1, normalized
|
|
1460
|
+
positive: 14
|
|
1461
|
+
negative: 3
|
|
1462
|
+
samples: 17
|
|
1463
|
+
last_feedback: 2026-05-16T09:00:00Z
|
|
1464
|
+
```
|
|
1465
|
+
|
|
1466
|
+
- Raw feedback MUST be recorded as `type: event` entries in the append-only event
|
|
1467
|
+
log (§8) — never written as raw events directly into a document's frontmatter.
|
|
1468
|
+
- The `feedback` block is a **rollup** of those events, recomputed periodically — the
|
|
1469
|
+
same derived-cache pattern as `confidence`. It lives in the canonical file but is a
|
|
1470
|
+
deliberately-lagged projection of the log.
|
|
1471
|
+
- The block MUST be language-neutral structured signal (scores, counts, polarity),
|
|
1472
|
+
never prose. Free-text feedback lives in the event payload and the embedding index.
|
|
1473
|
+
|
|
1474
|
+
### 11.6 Feedback as an Enhancement Layer
|
|
1475
|
+
|
|
1476
|
+
A host MAY use feedback to adjust `confidence`, routing weight, and to trigger
|
|
1477
|
+
investigation of low-scored outcomes. Feedback is strictly an **enhancement** layer:
|
|
1478
|
+
a host MUST remain fully functional with feedback collection disabled, and the
|
|
1479
|
+
semantic layer MUST degrade gracefully to its other signals (access frequency, decay,
|
|
1480
|
+
embedding similarity).
|
|
1481
|
+
|
|
1482
|
+
### 11.7 The `language_convention` Primitive
|
|
1483
|
+
|
|
1484
|
+
Because the control vocabulary is symbolic and never translated (§11.1), a workspace
|
|
1485
|
+
that wishes to *present* itself in a given human language does so with data, not by
|
|
1486
|
+
translating keys. The `language_convention` primitive (a `structural`, extension-owned
|
|
1487
|
+
type) records a workspace's presentation conventions: its default
|
|
1488
|
+
language/locale, formality, date and currency formatting, and terminology preferences.
|
|
1489
|
+
|
|
1490
|
+
It is `structural` and therefore travels in `template`/`sale` bundles (§5.5): a sold
|
|
1491
|
+
business carries its voice and locale conventions, but never the operator's private
|
|
1492
|
+
content. A host applies a `language_convention` as a **presentation overlay** at the
|
|
1493
|
+
semantic/rendering layer; it MUST NOT alter the deterministic layer's symbolic keys or
|
|
1494
|
+
enumerated values.
|
|
1495
|
+
|
|
1496
|
+
### 11.8 Workflow Runtime & Daemon Contract
|
|
1497
|
+
|
|
1498
|
+
SSSS defines the source of truth for workflow runtime behavior, but it does not
|
|
1499
|
+
mandate one resident process manager. A host MAY use OS cron, systemd timers,
|
|
1500
|
+
serverless schedules, webhooks, message queues, browser alarms, or an always-on
|
|
1501
|
+
daemon to wake the runtime. Those systems are **wake-up mechanisms** only. They MUST
|
|
1502
|
+
NOT become the canonical schedule, queue, or task history.
|
|
1503
|
+
|
|
1504
|
+
The canonical runtime sources are:
|
|
1505
|
+
|
|
1506
|
+
| Concern | Source of truth |
|
|
1507
|
+
|---------|-----------------|
|
|
1508
|
+
| Schedule and trigger meaning | `workflow` frontmatter `triggers[]`. |
|
|
1509
|
+
| Submitted work | `task` documents. |
|
|
1510
|
+
| Execution transcript | append-only `run` documents. |
|
|
1511
|
+
| What happened | append-only event log (§8). |
|
|
1512
|
+
| Claim/concurrency safety | leases (§7) plus deterministic idempotency keys (§6.4). |
|
|
1513
|
+
| Dashboards, task queues, cursors | projections (§10), rebuildable from the vault and event log. |
|
|
1514
|
+
|
|
1515
|
+
A conformant daemon loop is:
|
|
1516
|
+
|
|
1517
|
+
1. **Scan** active `workflow` primitives whose `triggers[]` might fire.
|
|
1518
|
+
2. **Evaluate** the trigger against wall-clock time, incoming event, webhook, file
|
|
1519
|
+
change, condition, or explicit manual invocation.
|
|
1520
|
+
3. **Append** a `workflow_triggered` event via an `event` envelope.
|
|
1521
|
+
4. **Instantiate** one `task` via an `operation` envelope.
|
|
1522
|
+
5. **Claim** the task using a time-bound lease before execution.
|
|
1523
|
+
6. **Create or append** a `run` record for execution steps.
|
|
1524
|
+
7. **Patch** task status to `done` or `failed`, or to `pending` with retry metadata.
|
|
1525
|
+
8. **Rebuild** derived task queues, run timelines, graph indexes, or UI projections
|
|
1526
|
+
as needed.
|
|
1527
|
+
|
|
1528
|
+
Every step that mutates vault state MUST use the Operation Contract (§6). A daemon
|
|
1529
|
+
MUST NOT write task, run, cursor, or workflow files directly.
|
|
1530
|
+
|
|
1531
|
+
#### Trigger Types
|
|
1532
|
+
|
|
1533
|
+
Core trigger types are:
|
|
1534
|
+
|
|
1535
|
+
| Type | Fires when |
|
|
1536
|
+
|------|------------|
|
|
1537
|
+
| `manual` | A human, agent, or another workflow invokes it. |
|
|
1538
|
+
| `cron` | A wall-clock cron expression reaches a scheduled instant. |
|
|
1539
|
+
| `interval` | A fixed duration elapses after the previous scheduled instant. |
|
|
1540
|
+
| `event` | A matching event-log entry appears. |
|
|
1541
|
+
| `webhook` | A verified external request arrives. |
|
|
1542
|
+
| `file_change` | A matching vault path is created, patched, or deleted. |
|
|
1543
|
+
| `condition` | A host-defined predicate over vault/projection state becomes true. |
|
|
1544
|
+
|
|
1545
|
+
An `event` trigger selects canonical events (§8) with two optional fields on the
|
|
1546
|
+
`trigger` primitive, evaluated as a pure function of the trigger and the event:
|
|
1547
|
+
|
|
1548
|
+
- `event_subject` is a glob over the event `subject`. `*` matches within one `/`
|
|
1549
|
+
segment and `**` matches across segments.
|
|
1550
|
+
- `event_match` maps dotted event paths (for example `principal.kind`) to a literal
|
|
1551
|
+
or an array of allowed literals. Every entry MUST hold.
|
|
1552
|
+
|
|
1553
|
+
A trigger with neither field matches nothing. An event nobody matches is only
|
|
1554
|
+
recorded. Work an event causes MUST carry the event's `event_id` as `causation_id`,
|
|
1555
|
+
and its idempotency basis uses `event_id` in place of `scheduled_for`, so a replayed
|
|
1556
|
+
event creates no second task and two events at the same instant create two.
|
|
1557
|
+
|
|
1558
|
+
An `assistant` MAY declare `owns`, a list of subject globs it is responsible for.
|
|
1559
|
+
The owner of a subject is the assistant whose claim has the longest literal prefix.
|
|
1560
|
+
Equal best claims are a conflict that a host MUST surface rather than resolve
|
|
1561
|
+
silently. Ownership says who is responsible; a lease (§7) says who is acting now.
|
|
1562
|
+
|
|
1563
|
+
Trigger entries SHOULD use the fields documented in §5.4 `workflow`. Hosts MAY add
|
|
1564
|
+
type-specific `x_` fields, but MUST preserve unknown fields when rewriting the
|
|
1565
|
+
workflow document (§4.2).
|
|
1566
|
+
|
|
1567
|
+
#### Idempotency & Duplicate Daemons
|
|
1568
|
+
|
|
1569
|
+
The idempotency basis for runtime-created work is:
|
|
1570
|
+
|
|
1571
|
+
```text
|
|
1572
|
+
workspace_id + workflow_id + trigger_id + scheduled_for
|
|
1573
|
+
workspace_id + workflow_id + trigger_id + event_id (event-driven work)
|
|
1574
|
+
```
|
|
1575
|
+
|
|
1576
|
+
Two daemon ticks evaluating the same trigger for the same scheduled instant MUST
|
|
1577
|
+
produce the same task idempotency key. Replaying the same envelope MUST NOT create a
|
|
1578
|
+
second task. This rule is what lets an SSSS host run multiple scheduler processes
|
|
1579
|
+
without a scheduler database becoming the source of truth.
|
|
1580
|
+
|
|
1581
|
+
For `event`, `webhook`, and `file_change` triggers, `scheduled_for` SHOULD be the
|
|
1582
|
+
canonical event timestamp or the host's normalized receipt timestamp, and the task
|
|
1583
|
+
SHOULD also record `trigger_event_id` when one exists.
|
|
1584
|
+
|
|
1585
|
+
#### Concurrency, Misfires & Retries
|
|
1586
|
+
|
|
1587
|
+
`misfire_policy` determines what happens when a daemon wakes after one or more missed
|
|
1588
|
+
schedule instants:
|
|
1589
|
+
|
|
1590
|
+
| Policy | Meaning |
|
|
1591
|
+
|--------|---------|
|
|
1592
|
+
| `skip` | Do not create work for missed instants. |
|
|
1593
|
+
| `run_once` | Create one task for the most recent missed instant. |
|
|
1594
|
+
| `catch_up` | Create one task per missed instant, each with its own idempotency key. |
|
|
1595
|
+
|
|
1596
|
+
`concurrency` determines what happens when a trigger fires while earlier work from
|
|
1597
|
+
the same workflow/trigger is still active:
|
|
1598
|
+
|
|
1599
|
+
| Policy | Meaning |
|
|
1600
|
+
|--------|---------|
|
|
1601
|
+
| `allow_parallel` | Create the new task regardless of active runs. |
|
|
1602
|
+
| `skip_if_running` | Do not create a new task while a prior run is active. |
|
|
1603
|
+
| `enqueue` | Create a pending task, but allow a worker to claim it only after prior work finishes. |
|
|
1604
|
+
| `replace` | Cancel or fail the previous task/run before creating the replacement. |
|
|
1605
|
+
|
|
1606
|
+
Retries MUST be represented on the `task` and/or `run` (`attempts`,
|
|
1607
|
+
`next_attempt_at`, `status: retrying`) and by events. A dead-letter outcome SHOULD be
|
|
1608
|
+
represented as `status: failed` on the task and `status: dead_letter` on the final
|
|
1609
|
+
run, plus an event explaining the terminal failure.
|
|
1610
|
+
|
|
1611
|
+
#### Runtime Helpers
|
|
1612
|
+
|
|
1613
|
+
The reference package exposes `@gregiteen/ssss-cli/runtime`, including deterministic helpers
|
|
1614
|
+
for planning a workflow trigger into a `workflow_triggered` event envelope and a
|
|
1615
|
+
task `operation` envelope. Hosts MAY implement their own daemon, but SHOULD pass the
|
|
1616
|
+
runtime conformance checks to prove the same source-of-truth and idempotency rules.
|
|
1617
|
+
|
|
1618
|
+
### 11.9 Multilingual Semantic Runtime Contract
|
|
1619
|
+
|
|
1620
|
+
The semantic layer is a **derived projection** over validated vault documents. It
|
|
1621
|
+
MUST be deterministic for the same vault bytes, registry set, adapter identity, and projection
|
|
1622
|
+
options. It MUST NOT become a second source of truth.
|
|
1623
|
+
|
|
1624
|
+
Each projected record SHOULD expose a stable identity, source path and content hash,
|
|
1625
|
+
primitive type, portability class, surface text, normalized search tokens, and graph
|
|
1626
|
+
edges. Identity is resolved from `semantic_id`, then `resource`, then the source path.
|
|
1627
|
+
Edges are derived from explicit `relations`, wiki links, and Markdown links. Hosts MAY
|
|
1628
|
+
add embeddings or richer ranking, but the exact lexical/graph projection MUST remain
|
|
1629
|
+
available as a dependency-free interoperability baseline.
|
|
1630
|
+
|
|
1631
|
+
The safe default projection contains only `structural` documents. It MUST exclude
|
|
1632
|
+
`tenant_private` and `resource_bound` documents unless the caller explicitly enables
|
|
1633
|
+
private indexing in an authorized context. Language selection MUST NOT widen scope or
|
|
1634
|
+
implicitly disclose conversations, runs, tasks, credentials, customer data, or bound
|
|
1635
|
+
resource values.
|
|
1636
|
+
|
|
1637
|
+
A host MAY inject a multilingual embedding adapter. Every enriched record MUST carry
|
|
1638
|
+
the embedding model identity and vector dimension. Search SHOULD report lexical and
|
|
1639
|
+
semantic evidence separately before computing a hybrid score.
|
|
1640
|
+
|
|
1641
|
+
A host MAY inject an LLM render adapter at presentation time. The render request MUST
|
|
1642
|
+
carry an invariant-control block. Rendering MAY change natural-language title,
|
|
1643
|
+
description, body, formatting, dates, and units, but MUST NOT change primitive IDs,
|
|
1644
|
+
field IDs, enum codes, actions, permissions, paths, versions, hashes, or relations.
|
|
1645
|
+
Canonical documents are authored once in any language; SSSS 0.9 neither requires nor
|
|
1646
|
+
materializes translation documents or localized vault trees.
|
|
1647
|
+
|
|
1648
|
+
---
|
|
1649
|
+
|
|
1650
|
+
## 12. Conformance
|
|
1651
|
+
|
|
1652
|
+
Conformance is defined by a shared set of **fixtures** — canonical
|
|
1653
|
+
request/response pairs for the Operation Contract. A host is conformant if, for
|
|
1654
|
+
every fixture, it produces the expected response and status code.
|
|
1655
|
+
|
|
1656
|
+
Fixtures are distributed as a JSON document carrying:
|
|
1657
|
+
|
|
1658
|
+
- `operation_types` — the envelope-type schemas.
|
|
1659
|
+
- `idempotency` — TTL and replay behavior.
|
|
1660
|
+
- `validation_rules` — envelope and content rules.
|
|
1661
|
+
- `runtime_contract` — trigger vocabulary and daemon idempotency rules (§11.8).
|
|
1662
|
+
- registry-extension checks — schema shape, regex validity, symlink rejection, and
|
|
1663
|
+
core/sibling collision rejection.
|
|
1664
|
+
- multilingual semantic checks — deterministic indexing, privacy defaults, graph
|
|
1665
|
+
edges, embedding provenance, cross-language retrieval, and invariant rendering
|
|
1666
|
+
(§11.9).
|
|
1667
|
+
- `fixtures[]` — each with a `request`, an `expected_response`, and an
|
|
1668
|
+
OPTIONAL `expected_http_status`.
|
|
1669
|
+
- `error_codes` — the canonical code table.
|
|
1670
|
+
|
|
1671
|
+
The conformance fixture set is the shared test contract between all SSSS
|
|
1672
|
+
implementations. A host MUST NOT claim SSSS conformance without passing the current
|
|
1673
|
+
fixture set, including each fixture's `expected_http_status` if it speaks HTTP
|
|
1674
|
+
(§6.5). Beyond the fixtures:
|
|
1675
|
+
|
|
1676
|
+
- Hosts implementing workflow daemons SHOULD run the runtime checks exposed by
|
|
1677
|
+
`@gregiteen/ssss-cli/runtime` (§11.8).
|
|
1678
|
+
- Hosts exposing semantic search or runtime rendering MUST pass the §11.9 checks.
|
|
1679
|
+
- Hosts supplying their own adapters (§6.7) MUST pass the shared adapter
|
|
1680
|
+
contracts: VFS, event store, idempotency store, and lease store, including
|
|
1681
|
+
their multi-process races.
|
|
1682
|
+
- Kernel implementations MUST pass the resource-lifecycle, error-code,
|
|
1683
|
+
append-only, capability, and frontmatter-subset checks.
|
|
1684
|
+
|
|
1685
|
+
The reference suite also checks **this document**: every example document in it
|
|
1686
|
+
must validate, §5.1 must list every core primitive with its registry portability,
|
|
1687
|
+
and the §6.5 table must match the reference error codes. A spec edit that breaks
|
|
1688
|
+
an example fails the build, just like a code change would.
|
|
1689
|
+
|
|
1690
|
+
---
|
|
1691
|
+
|
|
1692
|
+
## 13. Naming Conventions
|
|
1693
|
+
|
|
1694
|
+
| Thing | Convention | Example |
|
|
1695
|
+
|-------|-----------|---------|
|
|
1696
|
+
| Memory / task slug | `kebab-case`, unique, matches filename | `prefer-atomic-writes` |
|
|
1697
|
+
| Memory category | lowercase, matches directory | `patterns` |
|
|
1698
|
+
| Skill name | `kebab-case`, matches directory | `deploy` |
|
|
1699
|
+
| Canonical filename | `UPPERCASE.md` | `ASSISTANT.md` |
|
|
1700
|
+
| VFS path | relative, no leading `/`, `/`-separated | `assistants/bot/ASSISTANT.md` |
|
|
1701
|
+
| Conflict ID | `conflict-YYYY-MM-DD-NNN` | `conflict-2026-05-16-001` |
|
|
1702
|
+
| Idempotency key | Opaque string; random UUID for ad-hoc requests, deterministic derivation for runtime work (§6.4, §11.8) | `runtime-run-3f9a…` |
|
|
1703
|
+
| Timestamp | ISO 8601, `Z` suffix (UTC) | `2026-05-16T14:03:00Z` |
|
|
1704
|
+
|
|
1705
|
+
Slugs and names MAY contain non-ASCII Unicode letters so that non-Latin scripts are
|
|
1706
|
+
first-class; they remain lowercase and hyphen-separated. A host MAY instead use an
|
|
1707
|
+
opaque ID as the slug and keep the human-readable label in `title` / `name`.
|
|
1708
|
+
|
|
1709
|
+
---
|
|
1710
|
+
|
|
1711
|
+
## 14. Spec Versioning
|
|
1712
|
+
|
|
1713
|
+
This document is versioned independently of any host and of the conformance
|
|
1714
|
+
fixture set.
|
|
1715
|
+
|
|
1716
|
+
- The spec version is stated in the document header (currently **v0.9**).
|
|
1717
|
+
- Breaking changes to the file format, the type registry, or the Operation
|
|
1718
|
+
Contract increment the spec version.
|
|
1719
|
+
- Until **v1.0**, any version MAY introduce breaking changes.
|
|
1720
|
+
- A host SHOULD declare which spec version it targets.
|
|
1721
|
+
|
|
1722
|
+
---
|
|
1723
|
+
|
|
1724
|
+
## 15. Schema Evolution
|
|
1725
|
+
|
|
1726
|
+
SSSS is **self-describing and self-mutable**. The type registry (§5), the field
|
|
1727
|
+
schemas, and the contract rules are themselves SSSS-governed data — not a frozen
|
|
1728
|
+
external artifact. The protocol can evolve. But evolution is **governed**, never
|
|
1729
|
+
ad-hoc, so the deterministic layer (§11.1) stays exact at every moment.
|
|
1730
|
+
|
|
1731
|
+
### 15.1 Plain-Language Proposals
|
|
1732
|
+
|
|
1733
|
+
A change to SSSS is proposed in **plain natural language**, in any language — e.g.
|
|
1734
|
+
*"add an optional `priority` field to the task primitive."* A proposal is an ordinary
|
|
1735
|
+
SSSS work item (a `task`); authoring one does NOT require writing formal schema
|
|
1736
|
+
syntax by hand.
|
|
1737
|
+
|
|
1738
|
+
The host interprets the proposal **semantically** (§11), so a proposal written in any
|
|
1739
|
+
language is understood identically. The plain-language text is the *authoring
|
|
1740
|
+
interface* — it is not itself the schema.
|
|
1741
|
+
|
|
1742
|
+
### 15.2 The Governed Path
|
|
1743
|
+
|
|
1744
|
+
A proposal becomes part of SSSS only through a fixed pipeline:
|
|
1745
|
+
|
|
1746
|
+
1. **Interpret** — the plain-language proposal is resolved into a formal schema delta.
|
|
1747
|
+
2. **Validate** — the delta is checked against the current spec for consistency
|
|
1748
|
+
(no contradiction; no existing REQUIRED field removed without a migration).
|
|
1749
|
+
3. **Review** — acceptance is gated by human/admin approval and/or eval gates.
|
|
1750
|
+
4. **Version** — on acceptance the spec version (§14) is incremented.
|
|
1751
|
+
5. **Migrate** — a migration is recorded so existing vault data conforms to the new
|
|
1752
|
+
version.
|
|
1753
|
+
|
|
1754
|
+
### 15.3 Mutability Without Fuzziness
|
|
1755
|
+
|
|
1756
|
+
At any instant the *active* schema is a single, fixed, exact, versioned artifact —
|
|
1757
|
+
validation never becomes fuzzy. Mutability happens **between** versions, through the
|
|
1758
|
+
gate of §15.2. SSSS is therefore a *sequence of exact schemas*, not a fluid one. The
|
|
1759
|
+
plain-language interface lowers the authoring barrier; the governed path preserves
|
|
1760
|
+
the determinism the contract layer depends on.
|
|
1761
|
+
|
|
1762
|
+
---
|
|
1763
|
+
|
|
1764
|
+
## 16. The `.ucw` Bundle Format
|
|
1765
|
+
|
|
1766
|
+
A **bundle** is a single transportable file that packages a portion of a vault — its
|
|
1767
|
+
files plus a manifest describing what they are and what they need to run. A bundle is
|
|
1768
|
+
how an SSSS business or template is **backed up, shared, sold, and re-provisioned**. The
|
|
1769
|
+
canonical container is `package.ucw.json` (a "universal containerized workspace"); a host
|
|
1770
|
+
MAY additionally wrap it in a `.ucw` archive (the JSON plus any binary branding assets),
|
|
1771
|
+
but the JSON document is the normative artifact and is self-contained.
|
|
1772
|
+
|
|
1773
|
+
A bundle is produced by an **export** (§17) under one of the export profiles of §5.5
|
|
1774
|
+
(`backup | template | sale`). The profile a bundle was built under is recorded in its
|
|
1775
|
+
manifest and governs which portability classes (§5.5) its files may contain. A conformant
|
|
1776
|
+
exporter MUST NOT emit a `tenant_private` file into a `template` or `sale` bundle.
|
|
1777
|
+
|
|
1778
|
+
### 16.1 Structure
|
|
1779
|
+
|
|
1780
|
+
A bundle is a JSON object with three top-level members:
|
|
1781
|
+
|
|
1782
|
+
```jsonc
|
|
1783
|
+
{
|
|
1784
|
+
"manifest": { /* §16.2 — what this bundle is and needs */ },
|
|
1785
|
+
"branding": { /* OPTIONAL — binary assets, base64 (logo, favicon, colors, fonts) */ },
|
|
1786
|
+
"files": [ /* §16.4 — the SSSS files, in deterministic path order */ ]
|
|
1787
|
+
}
|
|
1788
|
+
```
|
|
1789
|
+
|
|
1790
|
+
`files` MUST be sorted by `path` (§16.3 order) so that two exports of the same vault state
|
|
1791
|
+
are identical and content-hashable (§16.3, `provenance.content_hash`).
|
|
1792
|
+
|
|
1793
|
+
### 16.2 The Manifest
|
|
1794
|
+
|
|
1795
|
+
The manifest is the bundle's contract. It declares the bundle's identity, the SSSS version
|
|
1796
|
+
and extensions a host needs to consume it, the export profile, an inventory of what is
|
|
1797
|
+
inside, the resources it must bind at provision time, and provenance.
|
|
1798
|
+
|
|
1799
|
+
| Field | Required | Meaning |
|
|
1800
|
+
|-------|----------|---------|
|
|
1801
|
+
| `name` | yes | Human name of the bundle. |
|
|
1802
|
+
| `description` | yes | One-line summary. |
|
|
1803
|
+
| `version` | yes | The bundle's own semver, independent of the spec. |
|
|
1804
|
+
| `exported_at` | yes | ISO-8601 timestamp of export. |
|
|
1805
|
+
| `ssss_core_version` | yes | The `registry/core.json` `spec_version` the bundle targets (e.g. `"0.9"`). A host MUST refuse a bundle whose core version it does not support. |
|
|
1806
|
+
| `required_extensions` | yes | Array of extension registry ids the files rely on (e.g. `["acme"]`). Empty array if the bundle uses only core primitives. A host MUST refuse a bundle naming an extension it has not loaded. |
|
|
1807
|
+
| `export_profile` | yes | `backup` \| `template` \| `sale` — the §5.5 profile this bundle was built under. Determines the allowed portability classes. |
|
|
1808
|
+
| `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). |
|
|
1809
|
+
| `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. |
|
|
1810
|
+
| `parameters` | no | Array of parameter definitions (§16.5) the importer must resolve (e.g. business name, domain). |
|
|
1811
|
+
| `source_workspace_id` | no | Origin workspace; OMITTED or nulled in `template`/`sale` profiles (it is operator-identifying). |
|
|
1812
|
+
| `dependencies` | no | `{ primitives, extensions, migrations, integrity }`: the sorted primitive types and extension ids the files use, the `migration` ids they carry, and `integrity: { content_hash, ssss_core_version }`. If present, `integrity.content_hash` MUST equal `provenance.content_hash`. |
|
|
1813
|
+
| `file_count` | yes | Length of `files`; a cheap integrity check. |
|
|
1814
|
+
| `provenance` | yes | `{ content_hash, exporter, signature? }` (§16.3). |
|
|
1815
|
+
|
|
1816
|
+
`primitive_inventory` supersedes the legacy fixed `categories` object and `provisioning`
|
|
1817
|
+
supersedes its `capabilities` booleans: both are now open and registry-driven rather than a
|
|
1818
|
+
closed enum, so a bundle full of extension primitives inventories and provisions them
|
|
1819
|
+
without a spec change.
|
|
1820
|
+
|
|
1821
|
+
### 16.3 Provenance & Integrity
|
|
1822
|
+
|
|
1823
|
+
`provenance` makes a bundle verifiable and attributable:
|
|
1824
|
+
|
|
1825
|
+
- `content_hash` — `sha256:` followed by the lowercase hex SHA-256 of the UTF-8 bytes of
|
|
1826
|
+
the **canonical file serialization**, defined as:
|
|
1827
|
+
1. Take each file as `{ "path": …, "content": … }`, in that key order, dropping any
|
|
1828
|
+
other member (such as a cached `frontmatter`).
|
|
1829
|
+
2. Sort by `path`, comparing strings by UTF-16 code units. This equals byte order
|
|
1830
|
+
for ASCII paths.
|
|
1831
|
+
3. Serialize the array as JSON with no insignificant whitespace, escaping strings
|
|
1832
|
+
exactly as ECMAScript `JSON.stringify` does: `"`, `\`, and control characters
|
|
1833
|
+
are escaped, and all other characters, including non-ASCII, are emitted
|
|
1834
|
+
literally.
|
|
1835
|
+
|
|
1836
|
+
An importer MUST recompute it and reject a bundle whose files do not match. This is
|
|
1837
|
+
what makes a bundle tamper-evident and what a marketplace lists against.
|
|
1838
|
+
- `exporter` — an identifier for the tool/host that produced the bundle (`"@gregiteen/ssss-cli@0.9.3"`).
|
|
1839
|
+
- `signature` — OPTIONAL detached signature over `content_hash` for a sold bundle, so a
|
|
1840
|
+
buyer can verify authorship. Unsigned bundles are valid; signing is a marketplace concern.
|
|
1841
|
+
|
|
1842
|
+
### 16.4 Files
|
|
1843
|
+
|
|
1844
|
+
Each entry of `files` is `{ path, content }`, where `content` is the full SSSS file text
|
|
1845
|
+
(frontmatter + body, §4). A host MAY also carry a parsed `frontmatter` object for
|
|
1846
|
+
convenience, but `content` is normative — on import the host re-parses `content`, so a
|
|
1847
|
+
divergent cached `frontmatter` is ignored. Every file's resolved portability class (§5.5,
|
|
1848
|
+
honoring `x_portability`) MUST be permitted by `export_profile`.
|
|
1849
|
+
|
|
1850
|
+
### 16.5 Parameters
|
|
1851
|
+
|
|
1852
|
+
`parameters` are the values an importer must supply to turn a template into a running
|
|
1853
|
+
instance — the difference between "a festival in a box" and "*this* festival." The schema is
|
|
1854
|
+
adopted verbatim from the legacy `WorkspaceTemplateVariableDefinition`:
|
|
1855
|
+
|
|
1856
|
+
| Field | Meaning |
|
|
1857
|
+
|-------|---------|
|
|
1858
|
+
| `key` | Stable identifier, referenced by provisioning steps and link rewriting. |
|
|
1859
|
+
| `label` | Human prompt. |
|
|
1860
|
+
| `type` | Value type (`string`, `boolean`, `surfaces`, …). |
|
|
1861
|
+
| `scope` | Where the value applies (`workspace`, `automation`, `data`, …). |
|
|
1862
|
+
| `source` | Who supplies it: `user` \| `llm` \| `graph` \| `system`. |
|
|
1863
|
+
| `required` | Whether provisioning may proceed without it. |
|
|
1864
|
+
| `defaultValue`, `options`, `dependsOn` | Optional default, enumerated choices, and dependency keys. |
|
|
1865
|
+
|
|
1866
|
+
A `sale`/`template` bundle declares its `resource_bound` requirements (§5.5) as `parameters`
|
|
1867
|
+
of `source: user` or `source: system` — e.g. the phone number, domain, and mailbox a buyer
|
|
1868
|
+
must bind. This is how the standard guarantees a sold business says exactly what real-world
|
|
1869
|
+
resources it needs without shipping the seller's own.
|
|
1870
|
+
|
|
1871
|
+
---
|
|
1872
|
+
|
|
1873
|
+
## 17. The Provisioning Contract
|
|
1874
|
+
|
|
1875
|
+
Export, provision, and import are the three verbs that move a bundle (§16) between vaults.
|
|
1876
|
+
They are specified to be **deterministic** and **idempotent** so that selling, cloning, and
|
|
1877
|
+
restoring an SSSS business are reliable operations rather than bespoke migrations.
|
|
1878
|
+
|
|
1879
|
+
### 17.1 The Three Verbs
|
|
1880
|
+
|
|
1881
|
+
- **export(vault, profile) → bundle** — Walk the vault, filter by the §5.5 portability rules
|
|
1882
|
+
for `profile`, sort files by path, compute `provenance.content_hash`, and emit a §16
|
|
1883
|
+
bundle. Export is **pure**: the same vault state and profile yield a byte-identical bundle.
|
|
1884
|
+
- **provision(bundle, parameters, target) → plan** — Resolve `parameters` (§16.5), bind the
|
|
1885
|
+
bundle's `resource_bound` requirements to real resources in `target`, and produce an
|
|
1886
|
+
ordered, replayable plan of Operation Contract envelopes (§6) plus resource bindings. A
|
|
1887
|
+
`dry_run` provision (mirroring §6 `dry_run`) validates and plans without committing.
|
|
1888
|
+
- **import(plan, target)** — Replay the plan's envelopes through the target's engine (§6.3).
|
|
1889
|
+
Because each envelope carries an `idempotency_key`, a re-run is a safe no-op: import is
|
|
1890
|
+
idempotent.
|
|
1891
|
+
|
|
1892
|
+
### 17.2 Provisioning Steps & Binding Vocabulary
|
|
1893
|
+
|
|
1894
|
+
A manifest's `provisioning` array is an ordered list of steps. The step schema and its
|
|
1895
|
+
controlled vocabularies are adopted from the legacy `WorkspaceProvisioningStep` and
|
|
1896
|
+
`WorkspaceGraphEdge`:
|
|
1897
|
+
|
|
1898
|
+
A **step** is `{ id, label, system, mode, required, notes? }`:
|
|
1899
|
+
|
|
1900
|
+
- `system` — the subsystem the step acts on: `workspace | branding | email | phone |
|
|
1901
|
+
domains | accounts | deployments | marketplace | tabs | automation`.
|
|
1902
|
+
- `mode` — the canonical **binding verb**: `existing` (bind to a resource the target
|
|
1903
|
+
already has), `provision` (allocate a new real resource — a domain, a phone number),
|
|
1904
|
+
`install` (add a dependency bundle), `generate` (synthesize content), `configure` (set
|
|
1905
|
+
values). `mode` is how a bundle distinguishes "needs a *new* phone number" from "route to
|
|
1906
|
+
the operator's existing line."
|
|
1907
|
+
|
|
1908
|
+
Dependency and resource relationships between primitives are expressed with the canonical
|
|
1909
|
+
**edge relations**: `uses_brand | owns_domain | routes_calls_to | deploys_to | has_surface
|
|
1910
|
+
| contains_page | installs_pack | authenticates_account | runs_workflow`. The
|
|
1911
|
+
`resource_bound` primitives of §5.5 each declare which relation binds them (for example,
|
|
1912
|
+
an extension's `domain` → `owns_domain`, `phone_number` → `routes_calls_to`, and
|
|
1913
|
+
`integration_connection` → `connects_to`), so an importer knows exactly what real-world
|
|
1914
|
+
binding each requires.
|
|
1915
|
+
|
|
1916
|
+
### 17.3 Determinism, Id-Remap & Link Integrity
|
|
1917
|
+
|
|
1918
|
+
When a bundle is provisioned into a target vault, identifiers (slugs, workspace ids,
|
|
1919
|
+
cross-file links) are **remapped** so the imported business is a fresh, self-consistent
|
|
1920
|
+
instance rather than a clone aliasing the seller's ids. Remapping MUST preserve link
|
|
1921
|
+
integrity: every internal `[[link]]` / relative reference that resolved in the source bundle
|
|
1922
|
+
MUST resolve to the remapped target after import. This is the same rewrite engine the
|
|
1923
|
+
semantic layer's autolink (§11) uses, run with a bundle-relative id map. An importer that
|
|
1924
|
+
cannot resolve a link MUST fail the provision rather than emit a dangling reference.
|
|
1925
|
+
|
|
1926
|
+
### 17.4 Composition & Upgrade
|
|
1927
|
+
|
|
1928
|
+
- **Composition** — a bundle MAY depend on other bundles. A dependency carries an
|
|
1929
|
+
`installMode` of `optional | recommended | required` (adopted from the legacy
|
|
1930
|
+
`WorkspaceMarketplaceRecommendation`). `provision` installs `required` dependencies before
|
|
1931
|
+
the bundle itself; `optional`/`recommended` are surfaced to the operator.
|
|
1932
|
+
- **Upgrade** — moving an installed bundle from one version to the next is performed
|
|
1933
|
+
through a `migration` primitive (§5.4) plus **structural-only** `patch` envelopes (§6.2). An upgrade MUST NOT touch `tenant_private` data: it rewrites the
|
|
1934
|
+
structural model, never the operator's private records. This is what lets a sold business
|
|
1935
|
+
receive standard updates without the vendor reaching into customer data.
|
|
1936
|
+
|
|
1937
|
+
---
|
|
1938
|
+
|
|
1939
|
+
## Appendix A — Reserved Frontmatter Keys
|
|
1940
|
+
|
|
1941
|
+
The following frontmatter keys are reserved by this spec across all primitives and
|
|
1942
|
+
MUST NOT be repurposed by hosts:
|
|
1943
|
+
|
|
1944
|
+
- Universal (§4.2): `type`, `title`, `description`, `timestamp`.
|
|
1945
|
+
- Semantic (§4.2, §11): `semantic_id`, `relations`, `language`, `resource`, `tags`,
|
|
1946
|
+
`aliases`, `feedback`, `confidence`.
|
|
1947
|
+
- Lifecycle: `slug`, `schema_version`, `status`.
|
|
1948
|
+
- Portability (§5.5): `x_portability`.
|
|
1949
|
+
|
|
1950
|
+
The patch key `__body__` (§6.2) is reserved and can never be a frontmatter key.
|
|
1951
|
+
|
|
1952
|
+
Hosts adding their own frontmatter fields SHOULD prefix them `x_` to remain
|
|
1953
|
+
forward-compatible with future spec revisions.
|
|
1954
|
+
|
|
1955
|
+
---
|
|
1956
|
+
|
|
1957
|
+
*SSSS is a portable standard. This specification is the ground truth; every SSSS
|
|
1958
|
+
implementation is a conformant consumer of it. To learn how a specific product
|
|
1959
|
+
implements SSSS, read that product's `SKILL.md`, not this file.*
|