skillscript-runtime 0.13.8 → 0.14.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.
Files changed (100) hide show
  1. package/README.md +11 -11
  2. package/dist/bootstrap.d.ts +13 -13
  3. package/dist/bootstrap.d.ts.map +1 -1
  4. package/dist/bootstrap.js +31 -31
  5. package/dist/bootstrap.js.map +1 -1
  6. package/dist/cli.js +6 -6
  7. package/dist/cli.js.map +1 -1
  8. package/dist/compile.js +2 -2
  9. package/dist/compile.js.map +1 -1
  10. package/dist/connectors/config.d.ts +4 -4
  11. package/dist/connectors/config.d.ts.map +1 -1
  12. package/dist/connectors/config.js +7 -7
  13. package/dist/connectors/config.js.map +1 -1
  14. package/dist/connectors/{memory-store-mcp.d.ts → data-store-mcp.d.ts} +8 -8
  15. package/dist/connectors/data-store-mcp.d.ts.map +1 -0
  16. package/dist/connectors/{memory-store-mcp.js → data-store-mcp.js} +31 -31
  17. package/dist/connectors/data-store-mcp.js.map +1 -0
  18. package/dist/connectors/{memory-store.d.ts → data-store.d.ts} +12 -12
  19. package/dist/connectors/data-store.d.ts.map +1 -0
  20. package/dist/connectors/{memory-store.js → data-store.js} +7 -7
  21. package/dist/connectors/data-store.js.map +1 -0
  22. package/dist/connectors/index.d.ts +5 -5
  23. package/dist/connectors/index.d.ts.map +1 -1
  24. package/dist/connectors/index.js +7 -7
  25. package/dist/connectors/index.js.map +1 -1
  26. package/dist/connectors/local-model-mcp.js +1 -1
  27. package/dist/connectors/local-model-mcp.js.map +1 -1
  28. package/dist/connectors/registry.d.ts +10 -10
  29. package/dist/connectors/registry.d.ts.map +1 -1
  30. package/dist/connectors/registry.js +11 -11
  31. package/dist/connectors/registry.js.map +1 -1
  32. package/dist/connectors/sqlite-skill-store.js +1 -1
  33. package/dist/connectors/sqlite-skill-store.js.map +1 -1
  34. package/dist/connectors/types.d.ts +23 -23
  35. package/dist/connectors/types.d.ts.map +1 -1
  36. package/dist/connectors/types.js +2 -2
  37. package/dist/connectors/types.js.map +1 -1
  38. package/dist/dashboard/spa/app.js +2 -2
  39. package/dist/errors.d.ts +1 -1
  40. package/dist/errors.js +4 -4
  41. package/dist/errors.js.map +1 -1
  42. package/dist/help-content.js +27 -27
  43. package/dist/help-content.js.map +1 -1
  44. package/dist/index.d.ts +4 -4
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +3 -3
  47. package/dist/index.js.map +1 -1
  48. package/dist/lint.js +18 -18
  49. package/dist/lint.js.map +1 -1
  50. package/dist/mcp-server.d.ts +1 -1
  51. package/dist/mcp-server.js +10 -10
  52. package/dist/mcp-server.js.map +1 -1
  53. package/dist/runtime-config.d.ts +2 -2
  54. package/dist/runtime-config.d.ts.map +1 -1
  55. package/dist/runtime-config.js +1 -1
  56. package/dist/runtime-config.js.map +1 -1
  57. package/dist/runtime.js +6 -6
  58. package/dist/runtime.js.map +1 -1
  59. package/dist/skill-manager.js +1 -1
  60. package/dist/skill-manager.js.map +1 -1
  61. package/dist/testing/conformance.d.ts +8 -8
  62. package/dist/testing/conformance.d.ts.map +1 -1
  63. package/dist/testing/conformance.js +8 -8
  64. package/dist/testing/conformance.js.map +1 -1
  65. package/dist/testing/index.d.ts +2 -2
  66. package/dist/testing/index.d.ts.map +1 -1
  67. package/dist/testing/index.js +1 -1
  68. package/dist/testing/index.js.map +1 -1
  69. package/dist/trace.d.ts +3 -3
  70. package/docs/adopter-playbook.md +18 -18
  71. package/docs/configuration.md +8 -8
  72. package/docs/connector-contract-reference.md +9 -9
  73. package/docs/language-reference.md +38 -63
  74. package/docs/sqlite-skill-store.md +1 -1
  75. package/examples/README.md +5 -5
  76. package/examples/connectors/{MemoryStoreTemplate/MemoryStoreTemplate.ts → DataStoreTemplate/DataStoreTemplate.ts} +34 -34
  77. package/examples/connectors/{MemoryStoreTemplate → DataStoreTemplate}/README.md +36 -36
  78. package/examples/connectors/LocalModelTemplate/README.md +1 -1
  79. package/examples/connectors/McpConnectorTemplate/McpConnectorTemplate.ts +2 -2
  80. package/examples/connectors/McpConnectorTemplate/README.md +5 -5
  81. package/examples/connectors/README.md +3 -3
  82. package/examples/connectors/SkillStoreTemplate/README.md +1 -1
  83. package/examples/connectors/SkillStoreTemplate/SkillStoreTemplate.ts +1 -1
  84. package/examples/custom-bootstrap.example.ts +8 -8
  85. package/examples/onboarding-scaffold/README.md +7 -7
  86. package/examples/onboarding-scaffold/bootstrap.ts +10 -10
  87. package/examples/onboarding-scaffold/{file-memory-store.ts → file-data-store.ts} +18 -18
  88. package/examples/skillscripts/classify-support-ticket.skill.md +4 -4
  89. package/examples/skillscripts/doc-qa-with-citations.skill.md +1 -1
  90. package/examples/skillscripts/feedback-sentiment-scan.skill.md +3 -3
  91. package/examples/skillscripts/hello.skill.provenance.json +1 -1
  92. package/examples/skillscripts/morning-brief.skill.md +3 -3
  93. package/examples/skillscripts/service-health-watch.skill.md +1 -1
  94. package/package.json +1 -1
  95. package/scaffold/config.toml +2 -2
  96. package/scaffold/connectors.json +3 -3
  97. package/dist/connectors/memory-store-mcp.d.ts.map +0 -1
  98. package/dist/connectors/memory-store-mcp.js.map +0 -1
  99. package/dist/connectors/memory-store.d.ts.map +0 -1
  100. package/dist/connectors/memory-store.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  # Adopter playbook
2
2
 
3
- How to wire skillscript-runtime into your deployment. Written for Joe-Programmer: you have your own substrate stack (memory system, agent harness, LLM endpoint, filesystem), and you want skillscript to slot in rather than dictate.
3
+ How to wire skillscript-runtime into your deployment. Written for Joe-Programmer: you have your own substrate stack (data store, agent harness, LLM endpoint, filesystem), and you want skillscript to slot in rather than dictate.
4
4
 
5
5
  This playbook covers the load-bearing decisions, the two wiring patterns, and the conventions that keep your local modifications upstream-merge-friendly.
6
6
 
@@ -9,11 +9,11 @@ This playbook covers the load-bearing decisions, the two wiring patterns, and th
9
9
  Skillscript-runtime is substrate-neutral and assumes you have (or will choose):
10
10
 
11
11
  1. **A filesystem** — for skill source files (`.skill.md`), trace records, possibly a memory database. Sandbox via container, chroot, or limited-privilege process — operator's call.
12
- 2. **A memory system** — for knowledge retrieval and memory writes. Could be SQLite-FTS (bundled), a vector database, an in-house store, an Obsidian-style notes system — whatever you already have.
12
+ 2. **A data store** — for knowledge retrieval and data writes. Could be SQLite-FTS (bundled), a vector database, an in-house store, an Obsidian-style notes system, a memory broker — whatever you already have.
13
13
  3. **An LLM endpoint** — Ollama running locally (bundled), a hosted API like OpenAI / Anthropic / Azure, or your own inference server.
14
14
  4. **An agent harness** — where skill output is delivered. Could be tmux sessions, a webhook receiver, an in-house agent runtime, or no harness at all (skills run for their text output only).
15
15
 
16
- Each of these maps to a typed connector contract: `SkillStore`, `MemoryStore`, `LocalModel`, `AgentConnector`. Plus `McpConnector` for any external tool you want to invoke from a skill body.
16
+ Each of these maps to a typed connector contract: `SkillStore`, `DataStore`, `LocalModel`, `AgentConnector`. Plus `McpConnector` for any external tool you want to invoke from a skill body.
17
17
 
18
18
  ## Case 1 vs Case 2 — the load-bearing wiring decision
19
19
 
@@ -21,21 +21,21 @@ This is the most important architectural choice you'll make.
21
21
 
22
22
  ### Case 1 — typed-contract wiring (substrate-portable)
23
23
 
24
- You implement the typed connector contracts (`MemoryStore`, `LocalModel`, etc.) against your substrate. The bridge classes (`MemoryStoreMcpConnector`, `LocalModelMcpConnector`) surface them as canonical `$ memory` / `$ llm` dispatch.
24
+ You implement the typed connector contracts (`DataStore`, `LocalModel`, etc.) against your substrate. The bridge classes (`DataStoreMcpConnector`, `LocalModelMcpConnector`) surface them as canonical `$ data_read` / `$ llm` dispatch.
25
25
 
26
26
  ```typescript
27
- class MyMemoryStore implements MemoryStore {
28
- async query(filters: QueryFilters): Promise<PortableMemory[]> { /* ... */ }
27
+ class MyDataStore implements DataStore {
28
+ async query(filters: QueryFilters): Promise<PortableData[]> { /* ... */ }
29
29
  async manifest(): Promise<ManifestInfo> { /* ... */ }
30
30
  }
31
31
 
32
- registry.registerMemoryStore("primary", new MyMemoryStore());
33
- registry.registerMcpConnector("memory", new MemoryStoreMcpConnector(new MyMemoryStore()));
32
+ registry.registerDataStore("primary", new MyDataStore());
33
+ registry.registerMcpConnector("data_read", new DataStoreMcpConnector(new MyDataStore()));
34
34
  ```
35
35
 
36
36
  **In skills:**
37
37
  ```
38
- $ memory mode=fts query="customer feedback" limit=10 -> CONTEXT
38
+ $ data_read mode=fts query="customer feedback" limit=10 -> CONTEXT
39
39
  ```
40
40
 
41
41
  This same skill body runs unchanged against your substrate, against SQLite-FTS (bundled), against Pinecone, against any substrate that conforms to the typed contract. **Skills are portable.**
@@ -100,7 +100,7 @@ Create `skillscript.config.json` in your `$SKILLSCRIPT_HOME`:
100
100
  {
101
101
  "skillsDir": "${SKILLSCRIPT_HOME}/skills",
102
102
  "traceDir": "${SKILLSCRIPT_HOME}/traces",
103
- "memoryDbPath": "${SKILLSCRIPT_HOME}/memory.json",
103
+ "dataDbPath": "${SKILLSCRIPT_HOME}/memory.json",
104
104
  "dashboard": { "port": 7878 }
105
105
  }
106
106
  ```
@@ -154,7 +154,7 @@ src/connectors/local/my-memory-adapter.ts ← adopter-owned
154
154
  src/connectors/local/my-llm-adapter.ts ← adopter-owned
155
155
  ```
156
156
 
157
- Upstream changes to `src/connectors/memory-store.ts` won't conflict with your `local/` files.
157
+ Upstream changes to `src/connectors/data-store.ts` won't conflict with your `local/` files.
158
158
 
159
159
  ### 2. Use the public registration API; don't edit the closed-set Map
160
160
 
@@ -182,17 +182,17 @@ See `examples/custom-bootstrap.example.ts` for a worked walkthrough.
182
182
  | Substrate | Shipped contract | Shipped impls | Shipped bridge |
183
183
  |---|---|---|---|
184
184
  | SkillStore | ✓ `load`/`query`/`store`/`update_status`/`delete`/`versions`/`metadata`/`staticCapabilities` | `FilesystemSkillStore`, `SqliteSkillStore` | n/a |
185
- | MemoryStore | ✓ `query`/`write` | `SqliteMemoryStore` | ✓ `MemoryStoreMcpConnector` |
185
+ | DataStore | ✓ `query`/`write` | `SqliteDataStore` | ✓ `DataStoreMcpConnector` |
186
186
  | LocalModel | ✓ `run` | `OllamaLocalModel` | ✓ `LocalModelMcpConnector` |
187
187
  | McpConnector | ✓ `call` | `RemoteMcpConnector`, `CallbackMcpConnector` | n/a |
188
188
  | AgentConnector | ✓ `list_agents`/`deliver`/`wake`/`health_check`/`request_response`; optional `agent_status` | `NoOpAgentConnector` (default), `HttpWebhookAgentConnector` | n/a |
189
189
 
190
190
  **Notable gaps the playbook should be honest about:**
191
191
 
192
- - **`SqliteMemoryStore` is a deliberately minimal reference impl.** It satisfies the contract (`query` / `write` / `get` / `staticCapabilities` / `manifest`) and exposes `supports_writes` + `supports_tag_filter` as true. It does NOT support semantic retrieval, pinning, decay scoring, or thread-status filtering (`supports_semantic` / `supports_pinning` / `supports_decay_model` / `supports_thread_status_filter` are all false). The bundled impl exists so the runtime works out-of-box for FTS-style tag/text retrieval. Deployments that need richer memory semantics fork `examples/connectors/MemoryStoreTemplate/` and wire their backing system (memory broker, vector DB, AMP, etc.). Check `manifest()` at runtime to confirm what your wired MemoryStore actually supports.
193
- - **SkillStore and MemoryStore have different lifecycle models — by design.** SkillStore is mutable / versioned / named CRUD (Draft→Approved→Disabled→Delete with audit trail; specific named artifacts). MemoryStore is an append-only search log (write → query/get; no per-record lifecycle beyond TTL/decay if substrate has one). Backing both onto one substrate (per [[project_memorystore_holds_skills]] — skill payloads ride as `payload_type: "skill"` memories) means serving both lifecycle models at once. The contract doesn't state the asymmetry as a design stance, but it's load-bearing: substrates that conflate "memory expiry" with "skill expiry" silently break authored code. Phase 3 cold-adopter dogfood surfaced this — AMP's 7-day TTL on `knowledge_type: common` memories would have decayed skills if AmpSkillStore had defaulted to `common`. Adopter mitigation: pick a `knowledge_type` your substrate treats as durable (e.g., `hard_won` in AMP), or wrap the SkillStore with a re-pin sweep.
194
- - **Durability is implementer's responsibility.** The typed SkillStore and MemoryStore contracts assume durable storage. Neither interface declares "writes live forever" — but the runtime + lint + dashboard all behave as if writes persist indefinitely. Substrate backends with their own GC / TTL / decay scoring surprise the skillscript layer invisibly. **If your substrate has these behaviors, build adopter-side guards** (pin-rules, retention policies, periodic re-pin sweeps) OR pick a substrate posture that satisfies "durable forever." The contract doesn't enforce — silent staleness is the failure mode. See `docs/connector-contract-reference.md` § "Storage-layer conventions" for the full discussion.
195
- - **Filter scope is advisory today; v0.14.0 will make it enforceable.** `query(filters)` accepts arbitrary fields; substrates honor what they support and silently drop the rest. For non-scope-sensitive filters (`tag`, `since`) this is fine. For scope/visibility/security-relevant filters (any future `vault` filter, tenant-id, access-control) the silent-drop is a leak waiting to happen. Until v0.14.0's `strict_filters: true`, enforce scope-relevant filters *above* the contract — adopter wraps the SkillStore/MemoryStore with a guard that asserts the substrate honors the filter, or the wrapping layer applies the filter in-memory after the substrate returns. Banked meta-pattern ([[feedback_defaults_over_knobs_security]]): defaults that protect by default > knobs that adopters opt into for security-relevant surfaces; aware adopters opt out, naive adopters get protection.
192
+ - **`SqliteDataStore` is a deliberately minimal reference impl.** It satisfies the contract (`query` / `write` / `get` / `staticCapabilities` / `manifest`) and exposes `supports_writes` + `supports_tag_filter` as true. It does NOT support semantic retrieval, pinning, decay scoring, or thread-status filtering (`supports_semantic` / `supports_pinning` / `supports_decay_model` / `supports_thread_status_filter` are all false). The bundled impl exists so the runtime works out-of-box for FTS-style tag/text retrieval. Deployments that need richer query semantics fork `examples/connectors/DataStoreTemplate/` and wire their backing system (memory broker, vector DB, AMP, etc.). Check `manifest()` at runtime to confirm what your wired DataStore actually supports.
193
+ - **SkillStore and DataStore have different lifecycle models — by design.** SkillStore is mutable / versioned / named CRUD (Draft→Approved→Disabled→Delete with audit trail; specific named artifacts). DataStore is an append-only search log (write → query/get; no per-record lifecycle beyond TTL/decay if substrate has one). Backing both onto one substrate (per [[project_memorystore_holds_skills]] — skill payloads ride as `payload_type: "skill"` memories) means serving both lifecycle models at once. The contract doesn't state the asymmetry as a design stance, but it's load-bearing: substrates that conflate "memory expiry" with "skill expiry" silently break authored code. Phase 3 cold-adopter dogfood surfaced this — AMP's 7-day TTL on `knowledge_type: common` memories would have decayed skills if AmpSkillStore had defaulted to `common`. Adopter mitigation: pick a `knowledge_type` your substrate treats as durable (e.g., `hard_won` in AMP), or wrap the SkillStore with a re-pin sweep.
194
+ - **Durability is implementer's responsibility.** The typed SkillStore and DataStore contracts assume durable storage. Neither interface declares "writes live forever" — but the runtime + lint + dashboard all behave as if writes persist indefinitely. Substrate backends with their own GC / TTL / decay scoring surprise the skillscript layer invisibly. **If your substrate has these behaviors, build adopter-side guards** (pin-rules, retention policies, periodic re-pin sweeps) OR pick a substrate posture that satisfies "durable forever." The contract doesn't enforce — silent staleness is the failure mode. See `docs/connector-contract-reference.md` § "Storage-layer conventions" for the full discussion.
195
+ - **Filter scope is advisory today; v0.14.0 will make it enforceable.** `query(filters)` accepts arbitrary fields; substrates honor what they support and silently drop the rest. For non-scope-sensitive filters (`tag`, `since`) this is fine. For scope/visibility/security-relevant filters (any future `vault` filter, tenant-id, access-control) the silent-drop is a leak waiting to happen. Until v0.14.0's `strict_filters: true`, enforce scope-relevant filters *above* the contract — adopter wraps the SkillStore/DataStore with a guard that asserts the substrate honors the filter, or the wrapping layer applies the filter in-memory after the substrate returns. Banked meta-pattern ([[feedback_defaults_over_knobs_security]]): defaults that protect by default > knobs that adopters opt into for security-relevant surfaces; aware adopters opt out, naive adopters get protection.
196
196
  - **4 of 6 trigger sources parse but don't fire.** `cron` and `session: start` work; `event`, `agent-event`, `file-watch`, `sensor` are parser-only stubs awaiting the event-bus surface.
197
197
  - **Output kinds are intentionally substrate-neutral.** `# Output:` accepts `text` / `agent: <name>` / `template: <name>` / `file: <path>` / `none`. Substrate-specific values (`slack:`, `card:`, etc.) are deliberately out of scope — adopters wanting Slack / WhatsApp / Discord / etc. delivery use either `$ slack.post ...` MCP dispatch inside the skill body OR deliver via `agent: <name>` and let the receiving agent decide.
198
198
  - **Authorization is hash-token approval.** Skills must carry `# Status: Approved vN:<token>` where the token re-computes from the body minus its `# Status:` line. Bundled `v1:` is CRC32 — discipline-barrier strength, suited to single-operator deployments. Adversarial threat models swap a stronger function:
@@ -212,10 +212,10 @@ See `examples/custom-bootstrap.example.ts` for a worked walkthrough.
212
212
 
213
213
  ## Skill discovery + cross-agent composition
214
214
 
215
- Under Case-1 wiring against a memory substrate that holds skill payloads (e.g., AMP's payload_type model where compiled `.skill.md` artifacts live alongside thread / prose / document entries), skill discovery uses the canonical `$ memory` surface:
215
+ Under Case-1 wiring against a memory substrate that holds skill payloads (e.g., AMP's payload_type model where compiled `.skill.md` artifacts live alongside thread / prose / document entries), skill discovery uses the canonical `$ data_read` surface:
216
216
 
217
217
  ```
218
- $ memory mode=fts query="incident triage" limit=5 -> SKILLS
218
+ $ data_read mode=fts query="incident triage" limit=5 -> SKILLS
219
219
  foreach S in ${SKILLS.items}:
220
220
  execute_skill(skill_name="${S.name}", ...) -> RESULT
221
221
  ```
@@ -4,7 +4,7 @@ How to configure a skillscript-runtime deployment.
4
4
 
5
5
  The single config file is **`~/.skillscript/connectors.json`** (or any path passed via `--connectors`). It has two top-level concerns:
6
6
 
7
- 1. **`substrate`** — which `SkillStore`, `MemoryStore`, and `LocalModel` the runtime hosts (MCP server + web dashboard) use.
7
+ 1. **`substrate`** — which `SkillStore`, `DataStore`, and `LocalModel` the runtime hosts (MCP server + web dashboard) use.
8
8
  2. **Named MCP connector instances** — `youtrack`, `github`, etc. — invoked via `$ <name>` in skill source.
9
9
 
10
10
  The runtime loads `connectors.json` at startup. Missing file → graceful empty config (substrate defaults to filesystem skills + conditional sqlite memories; no MCP connectors). Malformed JSON or unknown fields → structured errors surfaced at bootstrap.
@@ -22,7 +22,7 @@ Every default path the runtime computes is rooted under `SKILLSCRIPT_HOME`:
22
22
  | Triggers file | `$SKILLSCRIPT_HOME/triggers.json` |
23
23
  | Skills directory (`skillsDir`) | `$SKILLSCRIPT_HOME/skills/` |
24
24
  | Sqlite skill store dbPath | `$SKILLSCRIPT_HOME/skills/skills.db` |
25
- | Sqlite memory store dbPath | `$SKILLSCRIPT_HOME/memory.db` |
25
+ | Sqlite data store dbPath | `$SKILLSCRIPT_HOME/data.db` |
26
26
  | Trace directory | `$SKILLSCRIPT_HOME/traces/` |
27
27
 
28
28
  `SKILLSCRIPT_HOME` defaults to `~/.skillscript`. Set the env var to relocate **everything** under a different root — the cleanest multi-instance isolation primitive:
@@ -47,7 +47,7 @@ A typical out-of-the-box `~/.skillscript/connectors.json`:
47
47
  {
48
48
  "substrate": {
49
49
  "skill_store": "filesystem",
50
- "memory_store": "sqlite",
50
+ "data_store": "sqlite",
51
51
  "local_model": null
52
52
  }
53
53
  }
@@ -88,7 +88,7 @@ Valid short-form values per slot:
88
88
  | Slot | Values |
89
89
  |---|---|
90
90
  | `skill_store` | `"filesystem"` \| `"sqlite"` |
91
- | `memory_store` | `"sqlite"` |
91
+ | `data_store` | `"sqlite"` |
92
92
  | `local_model` | (none — `"ollama"` requires the object form with `defaultModelTag`; see below) |
93
93
 
94
94
  ### Null — explicit "no substrate"
@@ -116,10 +116,10 @@ The runtime doesn't register a connector for this slot. Useful for explicitly di
116
116
  |---|---|
117
117
  | `filesystem` (skill_store) | none — uses the CLI's `skillsDir` (defaults to `$SKILLSCRIPT_HOME/skills/`) |
118
118
  | `sqlite` (skill_store) | `dbPath` (default: `$SKILLSCRIPT_HOME/skills/skills.db`) |
119
- | `sqlite` (memory_store) | `dbPath` (default: `$SKILLSCRIPT_HOME/memory.db`; `MEMORY_DB` env overrides) |
119
+ | `sqlite` (data_store) | `dbPath` (default: `$SKILLSCRIPT_HOME/data.db`; `DATA_DB` env overrides) |
120
120
  | `ollama` (local_model) | `baseUrl` (default: `OLLAMA_BASE_URL` env or `http://localhost:11434`), **`defaultModelTag` (required — e.g., `"gemma2:9b"`, `"llama3.1:8b"`)** |
121
121
 
122
- > **`SqliteMemoryStore` feature surface.** The bundled `sqlite` memory_store is a deliberately minimal reference implementation: `supports_writes` + `supports_tag_filter` are true; `supports_semantic`, `supports_pinning`, `supports_decay_model`, `supports_thread_status_filter` are all false. Rich features (semantic retrieval, pinning, decay scoring, thread-status workflow) come from substrate impls — adopters fork `examples/connectors/MemoryStoreTemplate/` and wire their backing system (memory broker, vector DB, AMP, etc.). The bundled impl exists so the runtime works out-of-box; adopters with richer memory substrates write their own.
122
+ > **`SqliteDataStore` feature surface.** The bundled `sqlite` data_store is a deliberately minimal reference implementation: `supports_writes` + `supports_tag_filter` are true; `supports_semantic`, `supports_pinning`, `supports_decay_model`, `supports_thread_status_filter` are all false. Rich features (semantic retrieval, pinning, decay scoring, thread-status workflow) come from substrate impls — adopters fork `examples/connectors/DataStoreTemplate/` and wire their backing system (memory broker, vector DB, AMP, etc.). The bundled impl exists so the runtime works out-of-box; adopters with richer query semantics write their own.
123
123
 
124
124
  Worked Ollama example (because the short form isn't valid for `local_model`):
125
125
 
@@ -163,7 +163,7 @@ When multiple config sources speak:
163
163
 
164
164
  1. **Programmatic opts** (`opts.skillStore` passed to `bootstrap()`) — explicit, highest priority
165
165
  2. **`connectors.json` substrate section** — declarative, deployment-durable
166
- 3. **Built-in default** — fallback (filesystem skill_store; conditional sqlite memory_store; no local_model)
166
+ 3. **Built-in default** — fallback (filesystem skill_store; conditional sqlite data_store; no local_model)
167
167
 
168
168
  If two configs disagree, the higher-priority one wins; lower-priority is ignored without error.
169
169
 
@@ -256,7 +256,7 @@ Underscore-prefixed top-level keys (`_comment`, `_note_security`, etc.) are igno
256
256
 
257
257
  ## Adopter-custom substrate impls
258
258
 
259
- Write `class FooSkillStore implements SkillStore { ... }` (or MemoryStore, LocalModel). Wire it via either:
259
+ Write `class FooSkillStore implements SkillStore { ... }` (or DataStore, LocalModel). Wire it via either:
260
260
 
261
261
  **(a) Programmatic bootstrap (recommended today)** — write your own bootstrap script that constructs the registry directly:
262
262
 
@@ -4,7 +4,7 @@ The substrate-neutral contracts skillscript-runtime exposes for adopters to wire
4
4
 
5
5
  **Audience**: this doc is written for the agent that's implementing an adopter's AgentConnector — typically an LLM-class agent supervised by a human. If you're a human reading it directly, the same content applies; the prose is tightened for agent comprehension (literal field semantics, explicit precedence rules, worked examples).
6
6
 
7
- Other contracts (McpConnector, SkillStore, MemoryStore, LocalModel) audit + lock in subsequent v0.9.x slots; this doc grows with each lock.
7
+ Other contracts (McpConnector, SkillStore, DataStore, LocalModel) audit + lock in subsequent v0.9.x slots; this doc grows with each lock.
8
8
 
9
9
  ---
10
10
 
@@ -161,9 +161,9 @@ These are the load-bearing semantic rules. Internalize before implementing.
161
161
 
162
162
  ---
163
163
 
164
- ## Storage-layer conventions (SkillStore + MemoryStore)
164
+ ## Storage-layer conventions (SkillStore + DataStore)
165
165
 
166
- The cold-adopter Phase 3 dogfood (writing AmpSkillStore + AmpMemoryStore against AMP) surfaced several conventions that live in the bundled reference impls but aren't first-class in the typed contracts. Adopters writing their own SkillStore/MemoryStore impls need to know about these, or skills/memories misbehave silently.
166
+ The cold-adopter Phase 3 dogfood (writing AmpSkillStore + AmpDataStore against AMP) surfaced several conventions that live in the bundled reference impls but aren't first-class in the typed contracts. Adopters writing their own SkillStore/DataStore impls need to know about these, or skills/memories misbehave silently.
167
167
 
168
168
  ### SkillStore conventions
169
169
 
@@ -173,18 +173,18 @@ The cold-adopter Phase 3 dogfood (writing AmpSkillStore + AmpMemoryStore against
173
173
 
174
174
  **`store()` auto-stamps the approval token.** When a caller writes a skill with `# Status: Approved` (no `vN:<token>`), the store MUST stamp the v1 CRC32 (or whatever version is currently `setPreferredApprovalVersion`'d) onto the body before persisting. Otherwise the skill is non-executable — the approval gate refuses bodies without a valid token. This is a **runtime-semantic concern that leaked into the storage layer**: ideally the runtime would stamp at execute-time, but for forward-compat reasons the convention is store-side stamping. See `src/approval.ts` and the v0.9.x hash-token auth thread.
175
175
 
176
- ### MemoryStore conventions
176
+ ### DataStore conventions
177
177
 
178
- **`summary`/`detail` split is convention, not contract field.** The MemoryStore contract gives `write()` a single `content: string`. Bundled `SqliteMemoryStore` maps this to `summary = first line (≤200 chars)` and `detail = full content`. Adopter substrates with native summary/detail concepts (AMP's `summary` + `detail` columns) can pre-compose and pass via `metadata`, but the basic mapping convention is "first line is the preview." Diverge and the dashboard's memory rendering looks weird, but skills still work.
178
+ **`summary`/`detail` split is convention, not contract field.** The DataStore contract gives `write()` a single `content: string`. Bundled `SqliteDataStore` maps this to `summary = first line (≤200 chars)` and `detail = full content`. Adopter substrates with native summary/detail concepts (AMP's `summary` + `detail` columns) can pre-compose and pass via `metadata`, but the basic mapping convention is "first line is the preview." Diverge and the dashboard's memory rendering looks weird, but skills still work.
179
179
 
180
- **`get(id)` returns null on miss, doesn't throw.** Distinct from SkillStore's `load(name)` which throws `SkillNotFoundError`. MemoryStore's empty-set convention (`query()` returns `[]` not throws; `get()` returns `null` not throws) is **load-bearing for the runtime's control flow** — query callers branch on `result.length`, get callers branch on `result === null`. Don't change this in your impl. Per cold agent's "credit where due": *"unambiguous, and the runtime keys control flow on the specific classes."*
180
+ **`get(id)` returns null on miss, doesn't throw.** Distinct from SkillStore's `load(name)` which throws `SkillNotFoundError`. DataStore's empty-set convention (`query()` returns `[]` not throws; `get()` returns `null` not throws) is **load-bearing for the runtime's control flow** — query callers branch on `result.length`, get callers branch on `result === null`. Don't change this in your impl. Per cold agent's "credit where due": *"unambiguous, and the runtime keys control flow on the specific classes."*
181
181
 
182
182
  ### Durability stance (both contracts)
183
183
 
184
- **The typed contracts assume durable storage.** Neither SkillStore nor MemoryStore declare "writes live forever" anywhere in the interface — but the runtime + lint + dashboard all behave as if writes persist indefinitely. Substrate backends with their own GC / TTL / decay scoring surprise the skillscript layer invisibly:
184
+ **The typed contracts assume durable storage.** Neither SkillStore nor DataStore declare "writes live forever" anywhere in the interface — but the runtime + lint + dashboard all behave as if writes persist indefinitely. Substrate backends with their own GC / TTL / decay scoring surprise the skillscript layer invisibly:
185
185
 
186
186
  - A skill written to a substrate that auto-expires after N days disappears from `skill_list` without warning
187
- - A memory written with an implicit TTL gets pruned, breaking later `$ memory query` references
187
+ - A memory written with an implicit TTL gets pruned, breaking later `$ data_read query` references
188
188
  - A substrate that pin-deletes stale content silently invalidates persisted skill references
189
189
 
190
190
  Implementer responsibility: either pick a substrate posture that satisfies "durable forever," or build adopter-side guards (e.g., pin-rules, retention policies, periodic re-pin sweeps) that maintain the assumption. The contract doesn't enforce — silent staleness is the failure mode.
@@ -193,7 +193,7 @@ Implementer responsibility: either pick a substrate posture that satisfies "dura
193
193
 
194
194
  **Filters are advisory at the contract layer (v0.13.x and earlier).** `query(filters)` accepts arbitrary fields via the `[key: string]: unknown` extension; substrates honor what they support and silently drop the rest. For non-scope-sensitive filters (`tag`, `since`) this is fine. For **scope/visibility/security-relevant filters** (any future `vault` filter, tenant-id, access-control) — the silent-drop is a leak waiting to happen.
195
195
 
196
- Today's discipline: enforce scope-relevant filters *above* the contract — adopter wraps the SkillStore/MemoryStore with a guard that asserts the substrate honors the filter, or the wrapping layer applies the filter in-memory after the substrate returns.
196
+ Today's discipline: enforce scope-relevant filters *above* the contract — adopter wraps the SkillStore/DataStore with a guard that asserts the substrate honors the filter, or the wrapping layer applies the filter in-memory after the substrate returns.
197
197
 
198
198
  v0.14.0 plans `strict_filters: true` at the contract layer so the substrate must surface unsupported-filter use via `UnsupportedFilterError`. Until then, adopter-side enforcement is the only option.
199
199
 
@@ -4,9 +4,6 @@ Canonical language reference for skillscript. Audience: skill authors (human + a
4
4
 
5
5
  Implementation state is cross-referenced to commit hashes; pending items mark v2/v3 work.
6
6
 
7
- Companion docs under the Skillscript project anchor:
8
- - `skillscript-prd` — product positioning, value prop, roadmap
9
- - `skillscript-erd` — engineering requirements, system architecture, runtime mechanics
10
7
 
11
8
  ## Not yet implemented, but planned
12
9
 
@@ -103,7 +100,7 @@ Skillscript is a constrained domain-specific language for authoring agent workfl
103
100
  Every skill follows the same shape:
104
101
 
105
102
  1. **Trigger** — what fires the skill: cron, command, session-start, agent-event, file-watch, webhook, etc.
106
- 2. **Process** — pull data (MCP / memory / file), classify or compose via sub-LLM + iteration, build the deliverable.
103
+ 2. **Process** — pull data (MCP / data store / file), classify or compose via sub-LLM + iteration, build the deliverable.
107
104
  3. **Deliver** — emit the result via one or more delivery channels (see below).
108
105
 
109
106
  Skillscript's job is to express this pipeline declaratively. When there is an agent above the skill, the agent's job is to act on the delivered artifact. When there isn't (autonomous fires), the delivery channel IS the outcome.
@@ -128,9 +125,9 @@ A skill delivers its work via one or more of three channels. Delivery channel is
128
125
  |---|---|---|
129
126
  | **Augmenting (context to agent)** | `emit(text="...")` + `# Output: agent: <name>` | Skill output is augment-kind payload for the receiving agent's next turn; joined emit stream becomes the delivered context. Pattern: agent-augmenting skills (briefing skills, session-start prepared context). |
130
127
  | **Template (playbook to agent)** | `emit(text="...")` + `# Output: template: <name>` | Skill output is a template-kind payload (recipe/playbook) the receiving agent executes. Pattern: instructional skills, reusable recipes. |
131
- | **Memory handoff** | `$ memory_write content="..." recipients=[<agent>] -> R` | Skill writes a memory the target agent picks up via mailbox at next session. Pattern: async carrier skills, autonomous fires that hand off to a future session. |
128
+ | **Data handoff** | `$ data_write content="..." recipients=[<agent>] -> R` | Skill writes data the target agent picks up via mailbox at next session. Pattern: async carrier skills, autonomous fires that hand off to a future session. |
132
129
 
133
- A single skill can use any combination. An autonomous cron-fired sweep might write a memory to one agent AND emit augment-kind context to another. The combinations are unconstrained — the per-op gating model governs which mutating ops are authorized, not which channels a skill uses.
130
+ A single skill can use any combination. An autonomous cron-fired sweep might write data to one agent AND emit augment-kind context to another. The combinations are unconstrained — the per-op gating model governs which mutating ops are authorized, not which channels a skill uses.
134
131
 
135
132
  ## Three op classes
136
133
 
@@ -148,19 +145,19 @@ Full op catalog and per-op semantics in Ops Reference.
148
145
 
149
146
  ## Substrate portability
150
147
 
151
- The language doesn't privilege any backend. `$ llm`, `$ memory`, `$ ticketing_search` are not language built-ins — they're connector names resolved at runtime through the registered MCP connector instances. The same skill source runs against any conforming substrate.
148
+ The language doesn't privilege any backend. `$ llm`, `$ data_read`, `$ ticketing_search` are not language built-ins — they're connector names resolved at runtime through the registered MCP connector instances. The same skill source runs against any conforming substrate.
152
149
 
153
150
  The runtime ships bundled bridges for two common patterns:
154
151
 
155
152
  - `$ llm` routes through whichever LocalModel is wired (via `substrate.local_model` in `connectors.json`)
156
- - `$ memory` / `$ memory_write` route through whichever MemoryStore is wired (via `substrate.memory_store`)
153
+ - `$ data_read` / `$ data_write` route through whichever DataStore is wired (via `substrate.data_store`)
157
154
 
158
155
  Adopters wire OpenAI instead of Ollama, Pinecone instead of SQLite, etc. Configuration lives outside the skill body; the language remains agnostic.
159
156
 
160
157
  | Connector slot | Adopter A wires | Adopter B wires |
161
158
  |---|---|---|
162
159
  | `llm` | LocalModel: Ollama (default) | LocalModel: OpenAI |
163
- | `memory` / `memory_write` | MemoryStore: SqliteMemoryStore (default) | MemoryStore: Pinecone |
160
+ | `data_read` / `data_write` | DataStore: SqliteDataStore (default) | DataStore: Pinecone |
164
161
  | `ticketing_search` | YouTrack MCP | Jira MCP |
165
162
 
166
163
  Substrate config syntax + the three-form configuration shape lives in the adopter playbook, not in this reference. Skill authors don't typically need to touch it — they author against the canonical `$ tool` surfaces and adopters wire whatever's underneath.
@@ -201,7 +198,7 @@ The joined `emit()` stream becomes the augment-kind payload delivered to the on-
201
198
  2. **Targets** — named blocks of typed ops, optionally with `needs:` dependencies
202
199
  3. **`default:`** — names the goal target the runtime walks toward
203
200
 
204
- Other delivery channels for the same shape: swap `# Output: agent: oncall` + the `emit()` calls for `$ memory_write content="..." recipients=["oncall"] approved="cron-fired" -> R` (one summary memory handoff per fire) or `file_write(path="/var/log/showstoppers-${EVENT.fired_at_unix}.md", content="...", approved="cron-fired")` (one file deliverable per fire).
201
+ Other delivery channels for the same shape: swap `# Output: agent: oncall` + the `emit()` calls for `$ data_write content="..." recipients=["oncall"] approved="cron-fired" -> R` (one summary data handoff per fire) or `file_write(path="/var/log/showstoppers-${EVENT.fired_at_unix}.md", content="...", approved="cron-fired")` (one file deliverable per fire).
205
202
 
206
203
  ## Lexical conventions
207
204
 
@@ -476,14 +473,14 @@ Both forms are first-class. Neither is canonical; choose by what makes the call
476
473
  ```
477
474
  $ ticketing_search query="project:INFRA" -> R
478
475
  $ llm prompt="${INPUT}" -> V
479
- $ memory mode=fts query="..." limit=5 -> M
476
+ $ data_read mode=fts query="..." limit=5 -> M
480
477
  ```
481
478
 
482
479
  **Dotted-prefix dispatch** is the explicit-routing escape hatch — useful when multiple connectors expose tools with overlapping names, or when an adopter wants the connector identity visible at the call site for audit clarity:
483
480
 
484
481
  ```
485
482
  $ ticketing.search query="project:INFRA" -> R
486
- $ memory.query_memories query="..." -> M
483
+ $ data_read.query query="..." -> M
487
484
  ```
488
485
 
489
486
  Parser rule: the text before the dot is the connector name (must match an entry in `connectors.json`); the rest is the tool + args.
@@ -493,13 +490,13 @@ Parser rule: the text before the dot is the connector name (must match an entry
493
490
  ```
494
491
  $ ticketing_search query="project:INFRA state:Open" limit=20 -> ISSUES
495
492
  $ llm prompt="Classify: ${INPUT}" -> VERDICT
496
- $ memory mode=fts query="${TOPIC}" limit=5 -> RESULTS
497
- $ memory_write content="${SUMMARY}" recipients=["oncall"] approved="morning roundup, 2026-05-25" -> ACK
493
+ $ data_read mode=fts query="${TOPIC}" limit=5 -> RESULTS
494
+ $ data_write content="${SUMMARY}" recipients=["oncall"] approved="morning roundup, 2026-05-25" -> ACK
498
495
  ```
499
496
 
500
497
  Tool args are unconstrained `key=value` pairs — the connector forwards them to the underlying MCP tool. If a dispatched call returns `isError: true`, the executor throws via `makeOpError`, which routes through `else:` / `# OnError:` machinery. The inner tool's error text is preserved in `result.errors[]`.
501
498
 
502
- **Substrate-neutrality.** Connector names like `$ llm`, `$ memory`, `$ ticketing_search` are NOT reserved or built-in — they're whatever the adopter declares in `connectors.json` (substrate config). Bridges for `$ llm` and `$ memory` / `$ memory_write` auto-wire only when the adopter's substrate config sets `substrate.local_model` / `substrate.memory_store` respectively. See the adopter playbook for the full substrate config reference.
499
+ **Substrate-neutrality.** Connector names like `$ llm`, `$ data_read`, `$ ticketing_search` are NOT reserved or built-in — they're whatever the adopter declares in `connectors.json` (substrate config). Bridges for `$ llm` and `$ data_read` / `$ data_write` auto-wire only when the adopter's substrate config sets `substrate.local_model` / `substrate.data_store` respectively. See the adopter playbook for the full substrate config reference.
503
500
 
504
501
  **Unknown connector** → tier-1 `unknown-connector` lint with the list of wired connector names.
505
502
 
@@ -513,7 +510,7 @@ Mutation ops require an authorization signal. The signal is per-op, not a mode b
513
510
 
514
511
  **Mutation-classified ops:**
515
512
  - `file_write(...)` (runtime-intrinsic)
516
- - `$ memory_write ...` and any MCP connector entry declared `"mutating": true` in `connectors.json`
513
+ - `$ data_write ...` and any MCP connector entry declared `"mutating": true` in `connectors.json`
517
514
  - `shell(command=..., unsafe=true)` (always mutation-classified)
518
515
  - `shell(command=...)` with destructive verb (rm, mv, dd, mkfs, etc. — heuristic list)
519
516
  - `$ <tool>` matching the mutating-verb regex
@@ -537,7 +534,7 @@ Mutation ops require an authorization signal. The signal is per-op, not a mode b
537
534
 
538
535
  deliver:
539
536
  file_write(path="/tmp/sweep.md", content="${REPORT}") # no approved= needed
540
- $ memory_write content="${REPORT}" recipients=["oncall"] # no approved= needed
537
+ $ data_write content="${REPORT}" recipients=["oncall"] # no approved= needed
541
538
  ```
542
539
 
543
540
  ```
@@ -577,35 +574,15 @@ flush:
577
574
  | Runtime-intrinsic | `file_write` | `file_write(path="...", content="...", [approved="..."])` | none |
578
575
  | External MCP | `$ <connector>` | `$ <name>[.<tool>] kwarg=value, ... [-> R]` | optional |
579
576
 
580
- ---
581
-
582
- ## Legacy syntax (deprecated, grace period)
583
-
584
- These symbol-forms shipped in earlier versions and still compile during the grace period with tier-2 `deprecated-symbol-op` warnings. New skills should use the canonical forms.
585
-
586
- | Deprecated | Canonical replacement |
587
- |---|---|
588
- | `~ prompt="..."` | `$ llm prompt="..."` |
589
- | `> mode=... query=...` | `$ memory mode=... query=...` |
590
- | `@ <command>` | `shell(command="...")` |
591
- | `@ unsafe <command>` | `shell(command="...", unsafe=true)` |
592
- | `! <text>` | `emit(text="<text>")` |
593
- | `?? "<prompt>" -> R` | `ask(prompt="<prompt>") -> R` |
594
- | `& <data-skill-name>` | `inline(skill="<data-skill-name>")` |
595
- | `$(VAR)` (legacy substitution) | `${VAR}` |
596
- | `(approved: "reason")` trailer | `approved="reason"` kwarg |
597
-
598
- The symbol-per-op design was deprecated when verb-word ops in function-call shape proved more author-friendly (training-corpus alignment + human-reviewability). Removal lands in a future version; until then, the canonical surface is the recommended form.
599
-
600
577
  ## Variable resolution — ${VAR} canonical, substitution + ambient refs + # Requires: cascade
601
578
 
602
- Skillscript supports four tiers of variables, each with distinct resolution timing and scope. Substitution uses **`${VAR}` as the canonical form**. The legacy `$(VAR)` form (parentheses) continues to compile during the grace period; see Ops Reference legacy syntax section for the deprecation map.
579
+ Skillscript supports four tiers of variables, each with distinct resolution timing and scope. Substitution uses **`${VAR}` as the canonical form**.
603
580
 
604
581
  ## Substitution syntax — `${VAR}` canonical
605
582
 
606
583
  ```
607
584
  emit(text="Hello, ${USER.login}!")
608
- $ memory mode=fts query="${TOPIC}" limit=5 -> R
585
+ $ data_read mode=fts query="${TOPIC}" limit=5 -> R
609
586
  $set REPORT = "Triage for ${PROJECT} (${ISSUES|length} open):\n"
610
587
  ```
611
588
 
@@ -638,7 +615,7 @@ For cron and session triggers, the scheduler injects time-offset ambient fields
638
615
 
639
616
  These let skill bodies compute `expires_at` and similar bounded-lifetime values without arithmetic in op kwargs. For ISO-formatted rendering of any epoch value, see the `|isodate` filter.
640
617
 
641
- Additional ambient refs may be injected based on connector configuration (e.g., a vault-backed memory connector may expose `${VAULT_ROOT}`). Connectors section documents which ambient refs each connector contributes.
618
+ Additional ambient refs may be injected based on connector configuration. Connector documentation specifies which ambient refs each connector contributes.
642
619
 
643
620
  ## Tier 2: Input
644
621
 
@@ -662,18 +639,16 @@ Bracketed list literals supported (`# Vars: TAGS=[a, b, c]`).
662
639
 
663
640
  ## Tier 4: Local
664
641
 
665
- Bound to a previous target's output mid-execution. Two forms:
642
+ Bound to a previous target's output mid-execution. Three forms:
666
643
  - `${target.output}` — the bound output of a target
667
644
  - `${VAR}` — an explicit `-> VAR` binding from any op
668
- - `${target.output.field}` or `${MEMORY.field}` — dotted field access into structured output
645
+ - `${VAR.field}` / `${VAR.nested.field}` — dotted field access into structured output
669
646
 
670
- **Field access resolution tiers** for `${MEMORY.field}`:
671
- 1. Core `PortableMemory` fields (id, summary, detail, score)
672
- 2. Curated substrate subset (thread_status, pinned, confidence, domain_tags, payload_type, knowledge_type, recipients, expires_at, created_at, agent_id, vault)
673
- 3. `metadata.X` for everything else
674
- 4. Ambient passthrough as literal `${MEMORY.field}` if unresolved
647
+ The exact field set available on a bound variable depends on what the wired connector returns. Field access resolves against the returned object's structure. If a field doesn't resolve, the substitution errors unless `|fallback:"default"` is supplied (see Pipe filters for full `|fallback:` semantics):
675
648
 
676
- **Missing-field opt-out:** `${MEMORY.field|fallback:"-"}` coalesces to the literal when the field doesn't resolve. See Pipe filters for the full `|fallback:` semantics.
649
+ ```
650
+ emit(text="Assignee: ${ISSUE.customFields.Assignee|fallback:\"unassigned\"}")
651
+ ```
677
652
 
678
653
  ## Resolution order
679
654
 
@@ -751,7 +726,7 @@ Strings that hold JSON arrays get the same tolerance as `in`/`not in` RHS: if th
751
726
  Pairs naturally with the numeric comparison operators (see Conditionals section):
752
727
 
753
728
  ```
754
- $ memory mode=fts query="urgent" -> ITEMS
729
+ $ data_read mode=fts query="urgent" -> ITEMS
755
730
  if ${ITEMS|length} > 5:
756
731
  emit(text="Mailbox is getting crowded")
757
732
  ```
@@ -901,7 +876,7 @@ The ref-vs-ref form is the canonical change-detection pattern. Both sides resolv
901
876
  if ${M.id|trim} in ${SEEN}:
902
877
  emit(text="already processed")
903
878
  elif ${M.id} not in ${SEEN}:
904
- $ memory_write content="..." approved="dedup" -> R
879
+ $ data_write content="..." approved="dedup" -> R
905
880
  ```
906
881
 
907
882
  Both sides are explicit refs. RHS must resolve to an array at runtime; clean error otherwise. LHS-undefined evaluates to `false` for both polarities. Optional filter on LHS.
@@ -909,9 +884,9 @@ Both sides are explicit refs. RHS must resolve to an array at runtime; clean err
909
884
  **JSON-string tolerance on RHS**: if the RHS resolves to a *string* that successfully JSON-parses to an array, the parsed array is used. This accommodates the canonical pattern where the array comes from a `$ llm` call that prompted for JSON output:
910
885
 
911
886
  ```
912
- $ llm prompt="List the URGENT memory IDs as a JSON array of strings. Items: ${M|json}" -> SEEN
887
+ $ llm prompt="List the URGENT item IDs as a JSON array of strings. Items: ${M|json}" -> SEEN
913
888
 
914
- foreach M in ${MEMORIES}:
889
+ foreach M in ${ITEMS}:
915
890
  if ${M.id} in ${SEEN}:
916
891
  emit(text="flagged urgent")
917
892
  ```
@@ -932,7 +907,7 @@ elif ${COUNT} <= 0:
932
907
  Filters and dotted-field access work on either side, same as equality. The `|length` filter (see Pipe filters section) is the canonical companion — `${LIST|length} > 5` is the natural "more than five items" pattern:
933
908
 
934
909
  ```
935
- $ memory mode=fts query="urgent" -> ITEMS
910
+ $ data_read mode=fts query="urgent" -> ITEMS
936
911
  if ${ITEMS|length} > 5:
937
912
  emit(text="Mailbox is getting crowded")
938
913
  ```
@@ -972,7 +947,7 @@ classify:
972
947
 
973
948
  ```
974
949
  mailbox_check:
975
- $ memory mode=fts query="addressed:perry" limit=10 -> MAILBOX
950
+ $ data_read mode=fts query="addressed:perry" limit=10 -> MAILBOX
976
951
  if not ${MAILBOX}:
977
952
  emit(text="empty mailbox today")
978
953
  elif ${MAILBOX|length} > 5:
@@ -1019,12 +994,12 @@ Both can coexist in the same target.
1019
994
  foreach M in ${RESULTS}:
1020
995
  emit(text="Processing ${M.id} — ${M.summary}")
1021
996
  if ${M.id|trim} not in ${SEEN}:
1022
- $ memory_write content="${M.summary}" approved="dedup" -> ACK
997
+ $ data_write content="${M.summary}" approved="dedup" -> ACK
1023
998
  ```
1024
999
 
1025
1000
  ### Iterator vars
1026
1001
 
1027
- `${M}` and `${M.field}` pass through ambient at compile; runtime substitutes per iteration. Dotted field access against `PortableMemory` shape applies (core fields curated subset metadata). Indexed access (`${LIST.0}`, `${LIST.0.id}`) also works on bound results.
1002
+ `${M}` and `${M.field}` pass through ambient at compile; runtime substitutes per iteration. Dotted field access resolves against whatever structure the connector returned per item the exact field set depends on the connector. Indexed access (`${LIST.0}`, `${LIST.0.id}`) also works on bound results.
1028
1003
 
1029
1004
  ### Loop-local scope (and the accumulator exception)
1030
1005
 
@@ -1369,7 +1344,7 @@ Runs if any op in the target's primary body errors. Local to the failing target.
1369
1344
 
1370
1345
  ```
1371
1346
  fetch:
1372
- $ memory mode=fts query=${TOPIC} limit=5 -> RESULT
1347
+ $ data_read mode=fts query=${TOPIC} limit=5 -> RESULT
1373
1348
  else:
1374
1349
  emit(text="retrieval failed, falling back to empty result")
1375
1350
  $set RESULT = ""
@@ -1412,7 +1387,7 @@ Inline fallback declared on the op line. Used when the call fails or returns emp
1412
1387
 
1413
1388
  ```
1414
1389
  weather:
1415
- $ memory mode=fts query="weather ${LOCATION}" limit=1 -> CURRENT (fallback: "weather unavailable")
1390
+ $ data_read mode=fts query="weather ${LOCATION}" limit=1 -> CURRENT (fallback: "weather unavailable")
1416
1391
  $ llm prompt="Summarize: ${CURRENT}" -> SUMMARY (fallback: "summary unavailable")
1417
1392
  $ slack.post channel=${CHANNEL} text=${SUMMARY} (fallback: "post failed silently") -> ACK
1418
1393
  ```
@@ -1422,7 +1397,7 @@ Same pattern as the `# Requires:` cascade's `(fallback: ...)` syntax — consist
1422
1397
  **Fallback value parsing.** Permissive: bare identifiers, quoted strings, and bracketed array literals all accepted. Matches the `# Requires:` cascade convention.
1423
1398
 
1424
1399
  ```
1425
- $ memory mode=fts query="..." -> RESULTS (fallback: []) # array literal
1400
+ $ data_read mode=fts query="..." -> RESULTS (fallback: []) # array literal
1426
1401
  $ llm prompt="..." -> VERDICT (fallback: unknown) # bare identifier
1427
1402
  $ slack.post text="..." -> ACK (fallback: "post failed") # quoted string
1428
1403
  ```
@@ -1728,7 +1703,7 @@ Normal `prompt` / `prose` compilation ignores the `# Tests:` section entirely
1728
1703
 
1729
1704
  ### Runtime assertion sandboxing
1730
1705
 
1731
- `# Tests:` cases that exercise runtime behavior (memory writes, shell ops, LocalModel calls) need a sandbox so they don't pollute production data. Two approaches:
1706
+ `# Tests:` cases that exercise runtime behavior (data writes, shell ops, LocalModel calls) need a sandbox so they don't pollute production data. Two approaches:
1732
1707
  - Scratch DB / scratch connector overrides for tests
1733
1708
  - Skip-and-warn for non-deterministic ops, only assert deterministic compile-time properties
1734
1709
 
@@ -1832,7 +1807,7 @@ $set NAME = value scope=session
1832
1807
 
1833
1808
  Backed by a configured data-records connector (the same surface `# Requires:` reads from) with conventionally-namespaced keys (e.g., `state:skill-local:<skill-name>:<key>`).
1834
1809
 
1835
- **Rationale:** Most interesting skills need memory across firings — change-detection, windowing, dedup-against-recent. Without lifecycle, every skill rebuilds state tracking via raw memory-write / memory-query calls.
1810
+ **Rationale:** Most interesting skills need persistent state across firings — change-detection, windowing, dedup-against-recent. Without lifecycle, every skill rebuilds state tracking via raw `$ data_write` / `$ data_read` calls.
1836
1811
 
1837
1812
  ## Cross-skill pub-sub
1838
1813
 
@@ -1905,7 +1880,7 @@ ${SELF.confidence-trend}
1905
1880
  Skill declares its required surfaces:
1906
1881
 
1907
1882
  ```
1908
- # Requires-Capabilities: sensors=[mic, camera], tools=[memorystore.write, slack.post]
1883
+ # Requires-Capabilities: sensors=[mic, camera], tools=[datastore.write, slack.post]
1909
1884
  # Requires-Privacy: private-channel-only
1910
1885
  ```
1911
1886
 
@@ -1968,5 +1943,5 @@ Hung dispatches hang the skill without explicit timeout configuration. Lean: ski
1968
1943
 
1969
1944
  ---
1970
1945
 
1971
- *Rendered from `skillscript/skillscript-language-reference` — 2026-05-29 10:20 EDT*
1946
+ *Rendered from `skillscript/skillscript-language-reference` — 2026-05-30 09:52 EDT*
1972
1947
  *Source of truth: AMP (`amp_render_document("skillscript/skillscript-language-reference")`)*
@@ -159,7 +159,7 @@ registerApprovalFn("v2", (body) => hmacSha256(SECRET, body));
159
159
  When forking into your codebase:
160
160
 
161
161
  1. Rename the class (e.g., `PostgresSkillStore`, `AmpSkillStore`)
162
- 2. Replace the SQL with your substrate's API (HTTP, MemoryStore, vector DB, etc.)
162
+ 2. Replace the SQL with your substrate's API (HTTP, DataStore, vector DB, etc.)
163
163
  3. Update `staticCapabilities()` to match what your substrate actually supports — drop `supports_versioning` if you can't track history, drop `supports_tag_filter` if querying tags isn't tractable
164
164
  4. Update `manifest()` to describe your substrate (`kind: "amp"` or whatever)
165
165
  5. Tests: copy `tests/SqliteSkillStore.test.ts` as a starting point + run the conformance suite (`SkillStoreConformance.buildTests()` from `skillscript-runtime/testing`)
@@ -9,9 +9,9 @@ For full language semantics, see [`../docs/language-reference.md`](../docs/langu
9
9
  | [`hello.skill.md`](./skillscripts/hello.skill.md) | Three-command first run; `! emit`; default target; `# Vars:` declared inputs |
10
10
  | [`morning-brief.skill.md`](./skillscripts/morning-brief.skill.md) | Multi-target with `needs:`; cron-fired with `EVENT.fired_at_*`; `# OnError:` fallback; dual `# Output:` (slack + prompt-context); `# Requires:` user-var cascade with fallback; LocalModel + retrieval composition |
11
11
  | [`doc-qa-with-citations.skill.md`](./skillscripts/doc-qa-with-citations.skill.md) | Single-target retrieval; `(fallback: [])` op-level fallback; LLM-with-citation pattern; pipe filter `\|json` for prompt embedding |
12
- | [`classify-support-ticket.skill.md`](./skillscripts/classify-support-ticket.skill.md) | `if`/`elif`/`else` multi-branch routing; classifier-cascade pattern; MemoryStore `$` writes with structured `domain_tags`; `$set` literal binding |
12
+ | [`classify-support-ticket.skill.md`](./skillscripts/classify-support-ticket.skill.md) | `if`/`elif`/`else` multi-branch routing; classifier-cascade pattern; DataStore `$` writes with structured `domain_tags`; `$set` literal binding |
13
13
  | [`cut-release-tag.skill.md`](./skillscripts/cut-release-tag.skill.md) | `??` interactive ask-for-input; `else:` short-circuit on multiple targets; `@` shell ops with per-op error fallback; rollback-on-failure flow |
14
- | [`service-health-watch.skill.md`](./skillscripts/service-health-watch.skill.md) | `foreach` iteration; cron-fired with `expires_at=$(EVENT.fired_at_plus_1d_unix)` TTL math; classifier-gated MemoryStore writes; pipe filter `\|url` for path-safe interpolation |
14
+ | [`service-health-watch.skill.md`](./skillscripts/service-health-watch.skill.md) | `foreach` iteration; cron-fired with `expires_at=$(EVENT.fired_at_plus_1d_unix)` TTL math; classifier-gated DataStore writes; pipe filter `\|url` for path-safe interpolation |
15
15
  | [`feedback-sentiment-scan.skill.md`](./skillscripts/feedback-sentiment-scan.skill.md) | `in` / `not in` set-membership ops; dedupe-via-seen-markers idiom; nested `if`/`elif` classification cascade; TTL on bookkeeping writes |
16
16
 
17
17
  ## Connector requirements
@@ -19,9 +19,9 @@ For full language semantics, see [`../docs/language-reference.md`](../docs/langu
19
19
  Most examples assume the bundled-default connectors:
20
20
 
21
21
  - `hello.skill.md` — zero deps (runs cold)
22
- - `doc-qa-with-citations.skill.md` — Ollama (LocalModel) + MemoryStore for the retrieval cache
23
- - `morning-brief.skill.md`, `feedback-sentiment-scan.skill.md` — Ollama + MemoryStore + a calendar MCP connector (the `$ calendar.list_events` op)
24
- - `classify-support-ticket.skill.md` — Ollama + MemoryStore
22
+ - `doc-qa-with-citations.skill.md` — Ollama (LocalModel) + DataStore for the retrieval cache
23
+ - `morning-brief.skill.md`, `feedback-sentiment-scan.skill.md` — Ollama + DataStore + a calendar MCP connector (the `$ calendar.list_events` op)
24
+ - `classify-support-ticket.skill.md` — Ollama + DataStore
25
25
  - `cut-release-tag.skill.md` — git CLI on `PATH`; expects to run inside a git repo
26
26
  - `service-health-watch.skill.md` — `curl` on `PATH`; network egress to `status.internal/*`
27
27