ltcai 1.7.0 → 2.1.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.
@@ -0,0 +1,540 @@
1
+ # Lattice AI v2 Architecture — Agentic Workspace Platform
2
+
3
+ Lattice AI v2.0.0 turned the local-first Workspace OS into a full **Agentic
4
+ Workspace Platform**. v2.1.0 keeps that architecture and matures the operational
5
+ layer: explicit handoffs, context packets, review/retry loops, memory snapshots,
6
+ planning records, replay, marketplace templates, and realtime execution
7
+ observability all compose over the same local-first JSON store and Knowledge
8
+ Graph.
9
+
10
+ This document describes how the four v2.0 pillars fit together, the small set of
11
+ **additive integration seams** that wire them, the cross-integration matrix that
12
+ results, and the compatibility surfaces that v1.x callers and data keep relying
13
+ on. Every claim below is grounded in the shipping source:
14
+
15
+ - App assembly: `latticeai/server_app.py`
16
+ - Cross-system wiring: `latticeai/services/platform_runtime.py`
17
+ - State + persistence: `latticeai/core/workspace_os.py`
18
+ - Plugin SDK: `latticeai/core/plugins.py`, `latticeai/api/plugins.py`
19
+ - Workflow engine: `latticeai/core/workflow_engine.py`, `latticeai/api/workflow_designer.py`
20
+ - Multi-Agent runtime: `latticeai/core/multi_agent.py`, `latticeai/api/agents.py`
21
+ - Realtime bus: `latticeai/core/realtime.py`, `latticeai/api/realtime.py`
22
+ - Marketplace foundation: `latticeai/core/marketplace.py`, `latticeai/api/marketplace.py`
23
+ - Project conventions: `AGENTS.md`
24
+
25
+ All four subsystems share the same design rules from `AGENTS.md`: dependency
26
+ injection, explicit interfaces, small focused modules, registry-based dispatch,
27
+ and composition over global state. None of them import the FastAPI app; each is
28
+ constructed by `server_app.py` and exposed through a router factory.
29
+
30
+ ---
31
+
32
+ ## 1. The Four v2 Pillars
33
+
34
+ The platform version is the single source of truth `WORKSPACE_OS_VERSION =
35
+ "2.1.0"` (`latticeai/core/workspace_os.py`). Each pillar module re-declares the
36
+ same version for its own surface (`PLUGIN_SDK_VERSION`, `WORKFLOW_ENGINE_VERSION`,
37
+ `MULTI_AGENT_VERSION`, `REALTIME_VERSION`) and the marketplace foundation exposes
38
+ `MARKETPLACE_VERSION`.
39
+
40
+ ### 1.1 Plugin SDK (`latticeai.core.plugins`)
41
+
42
+ A plugin is a directory under the configured plugins root that ships a
43
+ `plugin.json` manifest and **extends** the existing Skill / Tool / Workflow
44
+ surfaces rather than replacing them. Installed standalone skills keep working
45
+ untouched; a plugin simply *bundles* skills and declares tools / workflow
46
+ templates / actions under one versioned, permissioned unit.
47
+
48
+ Design rules enforced by the module: no import-time I/O (the filesystem is only
49
+ touched on `discover()`), no FastAPI and no globals (lifecycle state lives in the
50
+ Workspace OS store), and permissions are an allow-list — the execution boundary
51
+ refuses any capability a plugin did not declare *and* was not granted.
52
+
53
+ A validated `plugin.json` manifest:
54
+
55
+ ```json
56
+ {
57
+ "id": "release-notes",
58
+ "name": "Release Notes Assistant",
59
+ "version": "1.0.0",
60
+ "description": "Bundles a release-notes skill and a packaging workflow.",
61
+ "author": "you@example.com",
62
+ "lattice_version": ">=2.0.0",
63
+ "permissions": ["read_workspace", "run_skills", "run_workflows"],
64
+ "provides": {
65
+ "skills": ["release-notes"],
66
+ "workflows": ["package-and-tag"],
67
+ "tools": [],
68
+ "actions": []
69
+ },
70
+ "entrypoint": "",
71
+ "homepage": ""
72
+ }
73
+ ```
74
+
75
+ Constraints from `validate_manifest` and the module constants:
76
+
77
+ - `id`: lowercase alphanumeric with `-`/`_`, 2–64 chars (`_ID_RE`).
78
+ - `version`: semantic version (`_SEMVER_RE`).
79
+ - `permissions` ⊆ `PLUGIN_PERMISSIONS` = `read_workspace`, `write_workspace`,
80
+ `read_graph`, `write_graph`, `run_tools`, `run_skills`, `run_workflows`,
81
+ `run_agents`, `network`, `manage_memory`.
82
+ - `provides` keys ⊆ `PLUGIN_PROVIDES` = `skills`, `tools`, `workflows`,
83
+ `actions`; each value is a list.
84
+ - `lattice_version` is a minimum (major must match host, host must be `>=`),
85
+ checked by `is_compatible(required, current=PLUGIN_SDK_VERSION)`.
86
+
87
+ The permissioned execution boundary:
88
+
89
+ ```python
90
+ def execute_action(
91
+ self,
92
+ plugin_id: str,
93
+ action: str,
94
+ args: Optional[Dict[str, Any]] = None,
95
+ *,
96
+ runners: Optional[Dict[str, Callable[..., Any]]] = None,
97
+ ) -> PluginExecutionResult: ...
98
+ ```
99
+
100
+ `execute_action` maps an action to the capability + permission it needs
101
+ (`run_tool→tools/run_tools`, `run_skill→skills/run_skills`,
102
+ `run_workflow→workflows/run_workflows`, `run_agent→agents/run_agents`). It
103
+ returns a `PluginExecutionResult` with `status` in `ok | blocked | error |
104
+ skipped`:
105
+
106
+ - `blocked` if the plugin is not enabled, did not declare the required
107
+ permission, or the permission was not granted at install time.
108
+ - `skipped` if the host injected no runner for the capability (never crashes the
109
+ caller — safe-by-default).
110
+ - `error` if the runner raised; `ok` otherwise.
111
+
112
+ ### 1.2 Workflow Designer (`latticeai.core.workflow_engine`)
113
+
114
+ A workflow is a small directed graph of typed *nodes* starting from a `trigger`.
115
+ `NODE_TYPES` = `trigger`, `tool`, `skill`, `plugin`, `agent`, `condition`,
116
+ `output`. The engine walks the graph from the trigger, dispatching each
117
+ executable node to an injected runner and recording a step-by-step timeline so a
118
+ run can be inspected, replayed, and linked into the Workspace timeline and
119
+ Knowledge Graph.
120
+
121
+ Key safety properties baked into the engine:
122
+
123
+ - **Bounded execution.** `_MAX_STEPS = 100` is a hard cap, so a mis-wired `next`
124
+ cycle can never hang a run; exceeding it records a `guard` error.
125
+ - **Graceful degradation.** A node whose runner family is not wired is recorded
126
+ as `skipped` with a reason rather than failing the whole run; the run status
127
+ becomes `partial`.
128
+ - **No `eval`.** `condition` nodes use `_evaluate_condition`, which compares a
129
+ context value to a literal via a fixed op set (`==`, `!=`, `>`, `<`, `>=`,
130
+ `<=`, `contains`, `truthy`) and fails closed onto the `false` branch.
131
+
132
+ ```python
133
+ class WorkflowEngine:
134
+ def __init__(self, runners: Optional[Dict[str, Callable[..., Any]]] = None): ...
135
+ def run(self, workflow: Dict[str, Any], *, inputs: Optional[Dict[str, Any]] = None) -> WorkflowRun: ...
136
+ ```
137
+
138
+ A run yields a `WorkflowRun` with `status` in `ok | failed | partial`, a
139
+ `timeline`, and `outputs`. `export_workflow` / `import_workflow` provide a
140
+ portable JSON representation (definition only — no run history or scope).
141
+
142
+ ### 1.3 Multi-Agent Runtime 2.0 (`latticeai.core.multi_agent`)
143
+
144
+ v1.x shipped a single-agent state machine (`latticeai.core.agent.AgentRuntime`:
145
+ PLAN → EXECUTE → VERIFY → DONE). v2.0 adds the **orchestration** layer above it:
146
+ a pipeline of named roles that hand off to one another, retry on a failing
147
+ review, and emit a structured timeline that drops straight into the Workspace
148
+ timeline / Knowledge Graph.
149
+
150
+ `AGENT_ROLES` = `researcher`, `planner`, `executor`, `reviewer`, `release`. The
151
+ `CORE_PIPELINE` is `planner → executor → reviewer`; `researcher` and `release`
152
+ are optional stages. Role ids match `DEFAULT_AGENTS` in
153
+ `latticeai/core/workspace_os.py` (`agent:planner`, `agent:executor`,
154
+ `agent:reviewer`, `agent:researcher`, `agent:release`).
155
+
156
+ ```python
157
+ class MultiAgentOrchestrator:
158
+ def __init__(self, role_runner: Optional[Callable[[str, OrchestrationContext], Dict[str, Any]]] = None): ...
159
+ def run(
160
+ self,
161
+ goal: str,
162
+ *,
163
+ user_email: Optional[str] = None,
164
+ workspace_id: Optional[str] = None,
165
+ inputs: Optional[Dict[str, Any]] = None,
166
+ roles: Optional[List[str]] = None,
167
+ max_retries: int = 2,
168
+ ) -> AgentRunResult: ...
169
+ ```
170
+
171
+ Like the v1 runtime, the orchestrator is pure logic over an injected
172
+ `role_runner` port, so it runs with no LLM and no server. `default_role_runner`
173
+ is deterministic and genuinely useful: the `researcher` pulls workspace context,
174
+ the `planner` decomposes the goal, the `executor` carries out steps (and can
175
+ drive an injected `workflow_runner` / `plugin_runner`), and the `reviewer`
176
+ returns `pass` / `retry`. The reviewer can rewind the pipeline to the executor up
177
+ to `max_retries` times; the final `status` is `ok`, `retried_ok`, or `failed`.
178
+
179
+ ### 1.4 Realtime Collaboration (`latticeai.core.realtime`)
180
+
181
+ An in-process pub/sub bus, presence registry, and activity feed delivered over
182
+ Server-Sent Events (SSE). SSE is chosen deliberately: the codebase already
183
+ streams model output over SSE, it needs no extra dependency, and it works through
184
+ the existing single-port local-first deployment.
185
+
186
+ Guarantees from the module:
187
+
188
+ - **Single-user local mode keeps working.** Publishing with zero subscribers is
189
+ a no-op; a `_FEED_LIMIT`-bounded ring buffer is still maintained so a late
190
+ subscriber catches up via `recent()` / a replay tail on `stream()`.
191
+ - **Workspace isolation preserved.** Every event carries `workspace_id`; a
192
+ subscriber only receives events whose workspace is in its allowed scope set.
193
+ A `None` scope (personal / local view) sees unscoped + personal events.
194
+ - **Backpressure-safe.** Per-subscriber queues are bounded (`_QUEUE_MAX`); on
195
+ overflow the oldest event is dropped rather than blocking the publisher.
196
+
197
+ ```python
198
+ class RealtimeBus:
199
+ def publish(self, event: Dict[str, Any]) -> Dict[str, Any]: ...
200
+ def __call__(self, event: Dict[str, Any]) -> Dict[str, Any]: # stable event_sink alias
201
+ return self.publish(event)
202
+ ```
203
+
204
+ ---
205
+
206
+ ## 2. How the Pillars Compose Into One Platform
207
+
208
+ The four pillars are not parallel silos. They are stitched into one platform by
209
+ exactly **three additive seams**, all introduced without changing any existing
210
+ behavior.
211
+
212
+ ### Seam 1 — Two new state keys with deep-merge backfill
213
+
214
+ `WorkspaceOSStore._default_state()` adds two new top-level keys to the local-first
215
+ JSON state: **`plugin_registry`** (an object, mirroring `skill_registry`) and
216
+ **`workflow_runs`** (a list, alongside the existing `workflows`). The default
217
+ state also adds v2.0 feature flags (`plugin_sdk`, `workflow_designer`,
218
+ `multi_agent_runtime`, `realtime_collaboration`) and a `plugins` navigation area.
219
+
220
+ These are safe for existing data because `load_state()` runs `_deep_merge(default,
221
+ loaded)` on every load. `_deep_merge` walks the default tree and fills in any key
222
+ missing from the loaded file while preserving every value already present:
223
+
224
+ ```python
225
+ def _deep_merge(default: Any, loaded: Any) -> Any:
226
+ if isinstance(default, dict) and isinstance(loaded, dict):
227
+ merged = {key: _deep_merge(value, loaded.get(key)) for key, value in default.items()}
228
+ for key, value in loaded.items():
229
+ if key not in merged:
230
+ merged[key] = value
231
+ return merged
232
+ if loaded is None:
233
+ return default
234
+ return loaded
235
+ ```
236
+
237
+ A v1.x `workspace_os.json` that has no `plugin_registry` / `workflow_runs` is
238
+ therefore upgraded *in memory* on first load — the new keys are backfilled with
239
+ their defaults, every pre-existing snapshot, trace, memory, agent run, workflow,
240
+ and skill entry is preserved, and the file is only rewritten on the next normal
241
+ `save_state`. The Plugin SDK lifecycle helpers (`list_plugin_registry`,
242
+ `set_plugin_enabled`, `mark_plugin_installed`, `mark_plugin_uninstalled`) and
243
+ workflow-run helpers (`record_workflow_run`, `list_workflow_runs`) operate on
244
+ these keys, deliberately mirroring the existing skill-registry contract.
245
+
246
+ ### Seam 2 — A single `event_sink` on `record_timeline_event`
247
+
248
+ `WorkspaceOSStore.__init__` takes one optional `event_sink` hook. Every state
249
+ mutation in the store already funnels through `record_timeline_event(area,
250
+ event_type, payload, workspace_id)`. v2.0 adds a single best-effort call at the
251
+ end of that method:
252
+
253
+ ```python
254
+ def record_timeline_event(self, area, event_type, payload, workspace_id=None):
255
+ ...
256
+ state.setdefault("timeline", []).append(event)
257
+ state["timeline"] = state["timeline"][-500:]
258
+ self.save_state(state)
259
+ if self.event_sink is not None:
260
+ try:
261
+ self.event_sink(event)
262
+ except Exception:
263
+ # Realtime delivery is best-effort and must never break a write.
264
+ pass
265
+ return event
266
+ ```
267
+
268
+ In `server_app.py` the bus is constructed first and injected as that sink:
269
+
270
+ ```python
271
+ REALTIME_BUS = RealtimeBus()
272
+ WORKSPACE_OS = WorkspaceOSStore(DATA_DIR, event_sink=REALTIME_BUS)
273
+ ```
274
+
275
+ Because `RealtimeBus.__call__` aliases `publish`, **every** timeline event —
276
+ workspace, snapshot, memory, graph trace, agent run, workflow run, plugin
277
+ install/enable, skill change, onboarding, presence — fans into the realtime feed
278
+ automatically. There is no per-call instrumentation and no duplicated event
279
+ system. The hook defaults to `None`, so existing callers and tests see zero
280
+ behavior change.
281
+
282
+ ### Seam 3 — `PlatformRuntime` as the one cross-wiring point
283
+
284
+ `latticeai/services/platform_runtime.py` is the single place the four subsystems
285
+ cross-wire to one another and to the workspace. Keeping it out of `server_app`
286
+ honours the `AGENTS.md` preference for small, composable, independently testable
287
+ modules; `server_app` only constructs it and mounts routers.
288
+
289
+ ```python
290
+ PLATFORM = PlatformRuntime(
291
+ store=WORKSPACE_OS,
292
+ workspace_service=WORKSPACE_SERVICE,
293
+ plugin_registry=PLUGIN_REGISTRY,
294
+ get_current_user=get_current_user,
295
+ workspace_graph=_workspace_graph,
296
+ workspace_scope_from_request=_workspace_scope_from_request,
297
+ get_tool_permission=get_tool_permission,
298
+ )
299
+ ```
300
+
301
+ `PlatformRuntime` provides:
302
+
303
+ - **Request gating** — `gate_read` / `gate_write` resolve a caller's workspace
304
+ scope via `WorkspaceService` (raising `403` on `PermissionError`), and
305
+ `allowed_scopes` returns the set of workspaces a user may see (used by the
306
+ realtime router).
307
+ - **Plugin lifecycle hooks** — `register_plugin_skill` marks a bundled skill
308
+ installed in the shared skill registry (`version="plugin:<id>"`), so plugins
309
+ extend the existing skill surface instead of owning a parallel one.
310
+ - **Shared node runners** — `_tool_node_runner`, `_skill_node_runner`,
311
+ `_plugin_node_runner`, `_agent_node_runner`, plus `plugin_capability_runners`
312
+ (the runner map the Plugin SDK boundary dispatches to) and `_context_provider`
313
+ (feeds workspace memory to the agent researcher role).
314
+ - **Cross-system runs** — `run_workflow_by_id` and `run_agent`, plus the
315
+ factories `build_workflow_runners`, `build_orchestrator`, and
316
+ `plugin_capability_runners` that are handed to the routers.
317
+
318
+ The four routers are wired entirely through `PLATFORM`:
319
+
320
+ ```python
321
+ app.include_router(create_plugins_router(
322
+ registry=PLUGIN_REGISTRY,
323
+ register_skill=PLATFORM.register_plugin_skill,
324
+ plugin_runners_factory=lambda: PLATFORM.plugin_capability_runners(None, None),
325
+ ...
326
+ ))
327
+ app.include_router(create_workflow_designer_router(
328
+ store=WORKSPACE_OS,
329
+ gate_read=PLATFORM.gate_read, gate_write=PLATFORM.gate_write,
330
+ build_runners=PLATFORM.build_workflow_runners,
331
+ ...
332
+ ))
333
+ app.include_router(create_agents_router(
334
+ store=WORKSPACE_OS,
335
+ orchestrator_factory=PLATFORM.build_orchestrator,
336
+ gate_read=PLATFORM.gate_read, gate_write=PLATFORM.gate_write,
337
+ ...
338
+ ))
339
+ app.include_router(create_realtime_router(
340
+ bus=REALTIME_BUS,
341
+ allowed_scopes=PLATFORM.allowed_scopes,
342
+ ...
343
+ ))
344
+ ```
345
+
346
+ ---
347
+
348
+ ## 3. Cross-Integration Matrix
349
+
350
+ Because of the seams above, the subsystems can drive one another. The following
351
+ capabilities are all backed by `PlatformRuntime` runners and the engines:
352
+
353
+ | From | Can run | Wired by |
354
+ | --- | --- | --- |
355
+ | **Workflow** node | `tool`, `skill`, `plugin`, `agent` | `build_workflow_runners` → `_tool_node_runner`, `_skill_node_runner`, `_plugin_node_runner`, `_agent_node_runner` |
356
+ | **Agent** run (executor role) | `plugin`, `workflow` | `build_orchestrator` / `run_agent` inject `workflow_runner` + `plugin_runner` into `default_role_runner` |
357
+ | **Plugin** action | `skill`, `tool`, `workflow`, `agent` | `plugin_capability_runners` → `run_skill`, `run_tool`, `run_workflow`, `run_agent` |
358
+ | **Any timeline event** | Realtime feed | `event_sink` → `RealtimeBus.publish` (Seam 2) |
359
+ | **Graph entities** | Link to `workflow_runs`, `agent_runs` (and workflows, memories) | `record_workflow_run`, `record_agent_run`, `create_workflow` call `graph.ingest_event` and store the returned `graph_node_id` |
360
+ | **Workspace timeline** | Reflects all activity | every store mutation calls `record_timeline_event`; `timeline()` also re-projects snapshots, traces, agent runs, and workflows |
361
+
362
+ Concrete flows:
363
+
364
+ - **Workflow → tool/skill/plugin/agent.** The Workflow Designer `run` endpoint
365
+ builds the runner map from `PLATFORM.build_workflow_runners(user, scope)` and
366
+ passes it to `WorkflowEngine`. A `tool` node records the invocation plus its
367
+ governance decision (via `get_tool_permission`) but never silently executes
368
+ exec/destructive tools. A `plugin` node calls `registry.execute_action(...)`
369
+ through the permission boundary. An `agent` node calls `run_agent(...,
370
+ with_workflow=False)`.
371
+
372
+ - **Agent → plugin/workflow.** `run_agent` constructs `default_role_runner` with
373
+ a `workflow_runner` (`run_workflow_by_id(..., with_agent=False)`) and a
374
+ `plugin_runner` (`registry.execute_action(pid, "run_skill", ...)`), so the
375
+ executor role can drive both. The result is persisted with
376
+ `store.record_agent_run`.
377
+
378
+ - **Plugin → workflow/agent.** `plugin_capability_runners` exposes `run_workflow`
379
+ (delegates to `run_workflow_by_id(..., with_agent=False)`) and `run_agent`
380
+ (delegates to `run_agent(..., with_workflow=False)`), each gated by the
381
+ plugin's declared permission.
382
+
383
+ - **Graph linkage.** When the Knowledge Graph is enabled, `record_workflow_run`
384
+ ingests a `WorkflowRun` node, `record_agent_run` ingests an `AgentRun` node,
385
+ and `create_workflow` ingests a `Workflow` node — each storing the returned
386
+ `graph_node_id` on the record so runs are navigable from the graph and via the
387
+ Relationship Explorer.
388
+
389
+ - **Unified timeline.** `WorkspaceOSStore.timeline()` merges live timeline events
390
+ with re-projected snapshots, answer traces, agent runs, and workflows (and
391
+ optional audit events), all scope-filtered, giving one chronological view of
392
+ every subsystem's activity.
393
+
394
+ ---
395
+
396
+ ## 4. Recursion Bounding
397
+
398
+ Cross-system runs could in principle recurse forever (workflow runs an agent that
399
+ runs a workflow that runs an agent…). v2.0 bounds recursion **by construction** in
400
+ `PlatformRuntime`, not by a runtime depth counter:
401
+
402
+ - A workflow's **`agent` node** runs an orchestrator built *without* a workflow
403
+ runner. In `run_agent` the workflow node path calls
404
+ `run_agent(..., with_workflow=False)`, so `default_role_runner` receives
405
+ `workflow_runner=None`.
406
+ - An orchestrator's **workflow runner** runs an engine built *without* an `agent`
407
+ runner. `run_workflow_by_id(..., with_agent=False)` assembles the runner map
408
+ with only `tool`, `skill`, and `plugin` — no `agent` key — so the
409
+ `WorkflowEngine` skips any `agent` node it encounters.
410
+
411
+ The deepest possible chains are therefore:
412
+
413
+ ```text
414
+ agent → workflow → (tool | skill | plugin)
415
+ workflow → agent → plugin
416
+ ```
417
+
418
+ Independently, the `WorkflowEngine` enforces `_MAX_STEPS = 100` per run as a hard
419
+ cap against `next`-pointer cycles, and `MultiAgentOrchestrator` caps reviewer
420
+ retries via `max_retries` (clamped to 0–5 in the agents router). Together these
421
+ guarantee every cross-system run terminates.
422
+
423
+ ---
424
+
425
+ ## 5. HTTP Surface (v2 additions)
426
+
427
+ All v2 routes are namespaced so they never collide with existing paths
428
+ (`/plugins/registry` vs. the marketplace `/plugins/directory`; `/workflows` vs.
429
+ `/workspace/workflows`; `/agents` plural vs. the single-agent `/agent`).
430
+
431
+ **Plugin SDK** (`latticeai/api/plugins.py`): `GET /plugins/sdk`,
432
+ `GET /plugins/registry`, `GET /plugins/registry/{plugin_id}`,
433
+ `POST /plugins/validate`, `POST /plugins/install` (admin),
434
+ `POST /plugins/uninstall` (admin), `POST /plugins/enable`,
435
+ `POST /plugins/disable`, `POST /plugins/execute`.
436
+
437
+ **Workflow Designer** (`latticeai/api/workflow_designer.py`): `GET /workflows`,
438
+ `GET|POST /workflows/api/definitions`, `GET|PATCH
439
+ /workflows/api/definitions/{id}`, `POST /workflows/api/validate`,
440
+ `POST /workflows/api/definitions/{id}/run`,
441
+ `GET /workflows/api/definitions/{id}/runs`, `GET /workflows/api/runs`,
442
+ `GET /workflows/api/runs/{run_id}/replay`,
443
+ `GET /workflows/api/export/{id}`, `POST /workflows/api/import`.
444
+
445
+ **Multi-Agent Runtime** (`latticeai/api/agents.py`): `GET /agents`,
446
+ `GET /agents/api/roles`, `GET /agents/api/runs`,
447
+ `GET /agents/api/runs/{run_id}/replay`, `GET /agents/api/handoffs`,
448
+ `GET|POST /agents/api/memory/snapshots`, `POST /agents/api/run`.
449
+
450
+ **Marketplace foundation** (`latticeai/api/marketplace.py`):
451
+ `GET /marketplace/templates`, `GET /marketplace/templates/{kind}/{id}/export`,
452
+ `POST /marketplace/templates/import`, `POST /marketplace/templates/install`,
453
+ `GET /marketplace/templates/registry`.
454
+
455
+ **Realtime Collaboration** (`latticeai/api/realtime.py`): `GET /activity`,
456
+ `GET /realtime/stream` (SSE), `GET /realtime/feed`, `GET /realtime/presence`,
457
+ `POST /realtime/presence/join`, `POST /realtime/presence/leave`.
458
+
459
+ Representative run request/response (Workflow Designer):
460
+
461
+ ```json
462
+ // POST /workflows/api/definitions/{workflow_id}/run
463
+ { "inputs": { "steps": ["analyze", "package"] } }
464
+ ```
465
+
466
+ ```json
467
+ {
468
+ "run": { "id": "workflow-run-…", "status": "ok", "workflow_id": "workflow-…" },
469
+ "result": { "status": "ok", "step_count": 4, "timeline": [ /* per-node */ ], "outputs": {} }
470
+ }
471
+ ```
472
+
473
+ ```json
474
+ // POST /agents/api/run
475
+ { "goal": "Draft v2.1 release notes", "roles": ["planner", "executor", "reviewer"], "max_retries": 2 }
476
+ ```
477
+
478
+ ```json
479
+ {
480
+ "run": { "id": "agent-run-…", "agent_id": "agent:executor", "status": "ok" },
481
+ "result": { "status": "ok", "roles_run": ["planner", "executor", "reviewer"], "retries": 0, "output": "…" }
482
+ }
483
+ ```
484
+
485
+ ---
486
+
487
+ ## 6. Compatibility
488
+
489
+ > **Compatibility note.** v2.1.0 is **additive**. All v1.x and v2.0 data and APIs are
490
+ > preserved; the platform layers new capabilities on top of unchanged surfaces.
491
+
492
+ Preserved surfaces, verified against source:
493
+
494
+ - **ASGI entrypoints.** `server:app` and `latticeai.server_app.app` remain the
495
+ application objects. `server_app.py` still exposes the module-level `app =
496
+ FastAPI(...)` plus the `main()` / `uvicorn.run(app, ...)` entry point.
497
+ - **Version wiring.** `WORKSPACE_OS_VERSION = "2.1.0"` drives both
498
+ `APP_VERSION` (and thus the FastAPI `app.version`) and the `/health`
499
+ response — the health router is constructed with `app_version=APP_VERSION`.
500
+ - **Existing routes.** Every v1.x router (`auth`, `admin`, `security_dashboard`,
501
+ `workspace`, `health`, `models`, `chat`, `tools`, `garden`, `setup`,
502
+ `static_routes`) is still included unchanged in `server_app.py`. The v2.0
503
+ routers are *added* alongside them under non-colliding namespaces.
504
+ - **Data.** The local-first `workspace_os.json` is upgraded by deep-merge
505
+ backfill (Seam 1) and `_migrate_workspaces` (non-destructive workspace
506
+ upgrade); no migration deletes or rewrites existing snapshots, traces,
507
+ memories, agent runs, workflows, or skills.
508
+ - **Skills.** Standalone installed skills keep working; plugins reuse the same
509
+ `skill_registry` via `register_plugin_skill` rather than a parallel store.
510
+ - **Snapshots.** Snapshot files remain immutable JSON under
511
+ `workspace_snapshots/`; `snapshot["version"]` is stamped with
512
+ `WORKSPACE_OS_VERSION` but creation/compare/export behavior is unchanged.
513
+ - **Knowledge Graph.** Graph operations stay additive (`ingest_event`,
514
+ `ingest_message`); v2.0 only *adds* `WorkflowRun` / `AgentRun` linkage on top,
515
+ honoring the `AGENTS.md` rule to preserve legacy read compatibility and avoid
516
+ destructive migrations.
517
+ - **Legacy workflows.** Pre-2.0 workflows persisted as a flat `steps` list still
518
+ validate and run — `workflow_engine.normalize_definition` lifts them into a
519
+ linear `trigger → tool… → output` node chain without rewriting stored history.
520
+ - **Realtime hook is opt-in.** `WorkspaceOSStore(event_sink=...)` defaults to
521
+ `None`; without a bus the store behaves exactly as in v1.x.
522
+
523
+ ---
524
+
525
+ ## 7. Per-Subsystem Reference
526
+
527
+ Each pillar is intentionally a small, self-documenting module. For the
528
+ authoritative, code-level definition of each subsystem, see:
529
+
530
+ - **Plugin SDK** — `latticeai/core/plugins.py` (manifest schema, permissions,
531
+ execution boundary) and `latticeai/api/plugins.py` (HTTP surface).
532
+ - **Workflow Designer** — `latticeai/core/workflow_engine.py` (node types,
533
+ validation, interpreter) and `latticeai/api/workflow_designer.py`.
534
+ - **Multi-Agent Runtime 2.0** — `latticeai/core/multi_agent.py` (roles, pipeline,
535
+ retry) and `latticeai/api/agents.py`.
536
+ - **Realtime Collaboration** — `latticeai/core/realtime.py` (bus, presence, SSE)
537
+ and `latticeai/api/realtime.py`.
538
+ - **Cross-wiring** — `latticeai/services/platform_runtime.py`.
539
+ - **State + persistence** — `latticeai/core/workspace_os.py`.
540
+ - **App assembly** — `latticeai/server_app.py`.