ltcai 11.9.0 → 12.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.
Files changed (128) hide show
  1. package/README.md +74 -59
  2. package/docs/CHANGELOG.md +115 -0
  3. package/docs/CI_AND_RELEASE_GATES.md +13 -7
  4. package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
  5. package/docs/DEVELOPMENT.md +267 -119
  6. package/docs/ENTERPRISE.md +1 -1
  7. package/docs/MULTI_AGENT_RUNTIME.md +4 -4
  8. package/docs/ONBOARDING.md +13 -3
  9. package/docs/OPERATIONS.md +13 -4
  10. package/docs/REALTIME_COLLABORATION.md +1 -1
  11. package/docs/ROADMAP.md +113 -0
  12. package/docs/TRUST_MODEL.md +16 -2
  13. package/docs/WHY_LATTICE.md +8 -2
  14. package/docs/WORKFLOW_DESIGNER.md +2 -2
  15. package/docs/kg-schema.md +51 -5
  16. package/docs/mcp-tools.md +17 -0
  17. package/lattice_brain/__init__.py +1 -1
  18. package/lattice_brain/graph/_kg_common/extraction.py +459 -105
  19. package/lattice_brain/graph/_kg_common/normalize.py +305 -0
  20. package/lattice_brain/graph/_kg_common/patterns.py +275 -0
  21. package/lattice_brain/graph/_kg_common/relations.py +12 -3
  22. package/lattice_brain/graph/_kg_common/sections.py +107 -0
  23. package/lattice_brain/graph/_kg_constants.py +7 -0
  24. package/latticeai/__init__.py +1 -1
  25. package/latticeai/api/agent_worker_seam.py +32 -0
  26. package/latticeai/api/worker_compute.py +78 -3
  27. package/latticeai/core/embedding_providers/__init__.py +16 -0
  28. package/latticeai/core/embedding_providers/autodetect.py +302 -0
  29. package/latticeai/core/embedding_providers/base.py +25 -0
  30. package/latticeai/core/embedding_providers/profiles.py +44 -0
  31. package/latticeai/core/embedding_providers/text.py +74 -8
  32. package/latticeai/core/vector_index/__init__.py +61 -0
  33. package/latticeai/core/vector_index/hnsw.py +383 -0
  34. package/latticeai/core/vector_index/sidecar.py +329 -0
  35. package/latticeai/models/router/generation.py +101 -22
  36. package/latticeai/models/router/loading.py +109 -4
  37. package/latticeai/runtime/brain_runtime.py +43 -9
  38. package/latticeai/runtime/build_phases/worker_profile.py +13 -4
  39. package/latticeai/services/architecture_readiness.py +2 -2
  40. package/latticeai/services/product_readiness.py +10 -5
  41. package/latticeai/services/search_service.py +7 -0
  42. package/latticeai/tools/__init__.py +6 -1
  43. package/latticeai/tools/documents.py +12 -0
  44. package/latticeai/tools/markup.py +152 -0
  45. package/package.json +2 -1
  46. package/scripts/check_current_release_docs.mjs +1 -1
  47. package/scripts/compose_openapi.py +2 -0
  48. package/scripts/openapi_route_families.json +7 -3
  49. package/scripts/publish_release.mjs +157 -0
  50. package/scripts/release_screen_claims.json +23 -0
  51. package/src-tauri/Cargo.lock +44 -10
  52. package/src-tauri/Cargo.toml +1 -1
  53. package/src-tauri/tauri.conf.json +1 -1
  54. package/static/app/asset-manifest.json +47 -41
  55. package/static/app/assets/Act-DCRUH46a.js +2 -0
  56. package/static/app/assets/AdminConsole-BFBj6OvN.js +1 -0
  57. package/static/app/assets/Brain-BXcoxIvb.js +2 -0
  58. package/static/app/assets/BrainHome-IhcEPC3v.js +2 -0
  59. package/static/app/assets/BrainSignals-DrfYSNjR.js +1 -0
  60. package/static/app/assets/Capture-BsO_OAeh.js +1 -0
  61. package/static/app/assets/Chronicle-BUj_UH3D.js +1 -0
  62. package/static/app/assets/CommandPalette-B4SWOaR-.js +1 -0
  63. package/static/app/assets/DigitalBrainExplorer-S1GSsyIT.js +321 -0
  64. package/static/app/assets/Library-CKUR2eVb.js +1 -0
  65. package/static/app/assets/LivingBrain--2UoV3Ki.js +1 -0
  66. package/static/app/assets/ProductFlow-D7rB6Xsu.js +1 -0
  67. package/static/app/assets/QueryClientProvider-B3OjqSyJ.js +1 -0
  68. package/static/app/assets/ReviewCard-UzQZ1ULT.js +3 -0
  69. package/static/app/assets/RunsListPanel-CDGFQYMo.js +1 -0
  70. package/static/app/assets/System-0OSxZrTp.js +1 -0
  71. package/static/app/assets/WorkflowGraph-BIcWZTUQ.js +1 -0
  72. package/static/app/assets/WorkflowsPanel-BeT5fmbk.js +2 -0
  73. package/static/app/assets/actHelpers-DhutJObL.js +1 -0
  74. package/static/app/assets/arrow-left-BwJfcH8J.js +1 -0
  75. package/static/app/assets/{bot-Bc3Q27YR.js → bot-D8tZzKMD.js} +1 -1
  76. package/static/app/assets/brain-DP2JPXSV.js +1 -0
  77. package/static/app/assets/button-Tzf5D7L1.js +1 -0
  78. package/static/app/assets/circle-check-D68nHcgS.js +1 -0
  79. package/static/app/assets/{circle-pause-BGMiV8UU.js → circle-pause-Druue_tZ.js} +1 -1
  80. package/static/app/assets/{circle-play-DoanLHnd.js → circle-play-BAht4SSW.js} +1 -1
  81. package/static/app/assets/{cpu-DwzNf82m.js → cpu-BPbmZRPp.js} +1 -1
  82. package/static/app/assets/{download-Ddw49yCV.js → download-BmxPZEwU.js} +1 -1
  83. package/static/app/assets/{folder-open-Brd6Kvto.js → folder-open-DokOmFQJ.js} +1 -1
  84. package/static/app/assets/{hard-drive-Bu-DTJdB.js → hard-drive-Cw6aEA2h.js} +1 -1
  85. package/static/app/assets/{index-CGdg_aq9.css → index-CB93CZWW.css} +1 -1
  86. package/static/app/assets/index-DhszK7A3.js +13 -0
  87. package/static/app/assets/input-BCdHoZXA.js +1 -0
  88. package/static/app/assets/jsx-runtime-bzQ4Vb5N.js +1 -0
  89. package/static/app/assets/{link-2-DZ4OA5tJ.js → link-2-CCModn4z.js} +1 -1
  90. package/static/app/assets/{permissionCopy-D9TR0F8b.js → permissionCopy-BSp1jYG7.js} +1 -1
  91. package/static/app/assets/primitives-D01WcJn7.js +1 -0
  92. package/static/app/assets/search-C_zAGfvO.js +1 -0
  93. package/static/app/assets/{share-2-BC5FirFv.js → share-2-DMMkM8Iu.js} +1 -1
  94. package/static/app/assets/{shield-alert-DUbR2W2s.js → shield-alert--mrCWQko.js} +1 -1
  95. package/static/app/assets/sourceMeta-nvd3Cdz3.js +1 -0
  96. package/static/app/assets/textarea-DfayhQiX.js +1 -0
  97. package/static/app/assets/useFocusTrap-C5NsTvsI.js +1 -0
  98. package/static/app/assets/useMutation-D7IMQTeI.js +1 -0
  99. package/static/app/assets/workspace-C7xidB7c.js +1 -0
  100. package/static/app/index.html +4 -4
  101. package/static/sw.js +1 -1
  102. package/static/app/assets/Act-B4WT81kh.js +0 -1
  103. package/static/app/assets/AdminConsole--Wf71m-o.js +0 -1
  104. package/static/app/assets/Brain-CtYaa26c.js +0 -321
  105. package/static/app/assets/BrainHome-Be2VPJEc.js +0 -2
  106. package/static/app/assets/BrainSignals-C0__xgpG.js +0 -1
  107. package/static/app/assets/Capture-DdNi5Peb.js +0 -1
  108. package/static/app/assets/Chronicle-B3hNveeI.js +0 -1
  109. package/static/app/assets/CommandPalette-CIsnSsFL.js +0 -1
  110. package/static/app/assets/Library-Bl5XClFV.js +0 -1
  111. package/static/app/assets/LivingBrain-DjkTB_gh.js +0 -1
  112. package/static/app/assets/ProductFlow-DCUNRNHs.js +0 -1
  113. package/static/app/assets/ReviewCard-CD3yWvUB.js +0 -3
  114. package/static/app/assets/System-BJ6jQ_SL.js +0 -1
  115. package/static/app/assets/arrow-left-6_28Z0qH.js +0 -1
  116. package/static/app/assets/brain-B9BDrMTe.js +0 -1
  117. package/static/app/assets/button-D6JcpYcf.js +0 -1
  118. package/static/app/assets/circle-check-BFu9lD-3.js +0 -1
  119. package/static/app/assets/index-CWKRRsLW.js +0 -10
  120. package/static/app/assets/input-CEqsxtil.js +0 -1
  121. package/static/app/assets/primitives-DORg7Z_7.js +0 -1
  122. package/static/app/assets/search-0NQ21wXe.js +0 -1
  123. package/static/app/assets/textarea-jtQcRSXo.js +0 -1
  124. package/static/app/assets/useFocusTrap-HRemcWId.js +0 -1
  125. package/static/app/assets/useMutation-DqlFE-Bw.js +0 -1
  126. package/static/app/assets/useQuery-BizqBNGw.js +0 -1
  127. package/static/app/assets/utils-WgW4V69R.js +0 -4
  128. package/static/app/assets/workspace-DSek3jCY.js +0 -1
@@ -3,13 +3,16 @@
3
3
  > **Status: canonical** — current contributor guidance, kept in sync with the
4
4
  > current release.
5
5
 
6
- Current release: **11.9.0 — Working Order**.
6
+ Current release: **12.1.0 — Fast Path**.
7
7
 
8
- This document is for contributors working on the local-first Digital Brain
9
- codebase. Product positioning and quick start stay in `README.md`; supported
10
- release history is 11.0.0 and later in `docs/CHANGELOG.md` and `RELEASE.md`
11
- (11.6.0 rebuilt the product server in Rust, so `SECURITY.md` supports only
12
- 11.x).
8
+ This document is the contributor onboarding path: how to boot the tree, where
9
+ a change belongs, which gates will go red, and the house rules that keep the
10
+ product honest. Product positioning and the five-minute first-run stay in
11
+ [README.md](../README.md) and [ONBOARDING.md](ONBOARDING.md). Supported
12
+ release history is 11.0.0 and later in [CHANGELOG.md](CHANGELOG.md) and
13
+ [RELEASE.md](../RELEASE.md) (11.6.0 rebuilt the product server in Rust, so
14
+ [SECURITY.md](../SECURITY.md) supports only 11.x). Open gaps toward v12.0.0
15
+ live in [ROADMAP.md](ROADMAP.md) (reference, not canonical).
13
16
 
14
17
  ## Product Contract
15
18
 
@@ -18,8 +21,7 @@ across any AI model.
18
21
 
19
22
  Engineering work should preserve these boundaries:
20
23
 
21
- - the Brain is the durable asset;
22
- - models are replaceable voices;
24
+ - the Brain is the durable asset; models are replaceable voices;
23
25
  - SQLite is the live local Brain store;
24
26
  - Docker, cloud models, downloads, update checks and Brain Network are opt-in
25
27
  (the PostgreSQL scale/migration tooling and the Telegram bridge left the tree
@@ -28,34 +30,128 @@ Engineering work should preserve these boundaries:
28
30
  calls;
29
31
  - normal Brain use must stay separate from Admin/operator controls.
30
32
 
31
- ## Local Setup
33
+ ## 10-minute quickstart
34
+
35
+ Need Python 3.11+, Node, and a Rust toolchain (`cargo`). The product is
36
+ `lattice-host`; Python is the 20-route compute worker it supervises.
32
37
 
33
38
  ```bash
34
- npm install
35
- npm start
39
+ git clone <this-repo>
40
+ cd "Lattice AI" # or whatever the directory is named
41
+
42
+ python3 -m venv .venv
43
+ .venv/bin/pip install -U pip
44
+ .venv/bin/pip install -e ".[dev]"
45
+
46
+ npm ci
47
+ npm start # product. If PATH has a stale global LTCAI: node bin/ltcai.js
36
48
  ```
37
49
 
38
- `npm start` (or `bin/ltcai.js` / the `LTCAI` binary) is the product: it
39
- boots `lattice-host` and serves the SPA at:
50
+ Then open:
40
51
 
41
52
  ```text
42
53
  http://127.0.0.1:4825/app
43
54
  ```
44
55
 
45
- `npm run dev` is **not** the product. It starts the 19-route Python AI
46
- worker with `--reload` (`latticeai.cli.entrypoint`). Use it when iterating
47
- on worker compute seams, not to serve the app.
56
+ The first screen is the wake step: language switcher (한국어 / English), the
57
+ Living Brain, and the Korean headline **「내 지식이 살아나는 Brain을 시작하세요.」**
58
+ with the primary **「Brain 지금 깨우기」**. An existing local Brain can skip
59
+ into the home canvas.
48
60
 
49
- Apple Silicon local model extras:
61
+ Apple Silicon local-model extras (MLX), only if you will load a model:
50
62
 
51
63
  ```bash
52
- pip install "ltcai[local]"
64
+ .venv/bin/pip install -e ".[local]"
53
65
  ```
54
66
 
55
- ## Validation
67
+ ### `npm start` vs `npm run dev`
68
+
69
+ | command | what it actually is | what you get |
70
+ | --- | --- | --- |
71
+ | `npm start` | the `LTCAI` bin → [`bin/ltcai.js`](../bin/ltcai.js) | **the product**: `lattice-host` on `127.0.0.1:4825`, supervising the worker, serving the SPA at `/app` |
72
+ | `node bin/ltcai.js` | the same bin, called directly | same product; use this if a stale global `LTCAI` is on `PATH` |
73
+ | `npm run dev` | `node scripts/run_python.mjs -m latticeai.cli.entrypoint --reload` | **not the product**. The 20-route worker with uvicorn `--reload`. Prints `Lattice AI worker on http://127.0.0.1:4825`. There is no SPA. |
74
+
75
+ `scripts/run_python.mjs` prefers `.venv/bin/python`. `bin/ltcai.js` prefers
76
+ `LTCAI_PYTHON`, then a managed `~/.ltcai/npm-python`, then `.venv`. Pin the
77
+ worker interpreter with `LTCAI_PYTHON` when both exist.
78
+
79
+ `bin/ltcai.js` looks for the host at `LATTICEAI_HOST_BIN` / `LTCAI_HOST`, then
80
+ `rust/target/release/lattice-host`, then `rust/target/debug/lattice-host`. If
81
+ none exist it runs `cargo build -p lattice-host` (debug). A stale
82
+ `rust/target/release/lattice-host` wins over a current debug binary — pin or
83
+ rebuild (`cargo build -p lattice-host --release`) if `-V` is not 12.1.0.
84
+
85
+ Do not run `npm start` and `npm run dev` on the same port. The worker
86
+ entrypoint defaults to `LATTICEAI_PORT` or **4825**, the same port as the
87
+ product. Iterate on worker compute with
88
+ `LATTICEAI_PORT=4835 npm run dev` (and `--no-spawn` / a pinned worker port on
89
+ the host if you are pairing them).
90
+
91
+ ## The map — 어디에 무엇을
92
+
93
+ Two crate-local maps are the contract. This table is the index; they are the
94
+ detail. Read the matching `mod.rs` before adding a file.
95
+
96
+ - [`rust/lattice-agent/ARCHITECTURE.md`](../rust/lattice-agent/ARCHITECTURE.md)
97
+ — `kernel/` decides; `parse/` `content/` `tools/` never import the kernel;
98
+ `surface/` carries HTTP; `prompts/` fills a blank. Arrows point down only.
99
+ - [`rust/lattice-platform/ARCHITECTURE.md`](../rust/lattice-platform/ARCHITECTURE.md)
100
+ — this crate **offers**. It does not decide whether a run is allowed
101
+ (`lattice-agent` kernel) and it does not own what is true (`lattice-core`).
102
+
103
+ ```text
104
+ lattice-host one door — mounts every family
105
+ lattice-platform what the product offers
106
+ workspaceos/ place, people, dials, workspace_os.json
107
+ toolsurface/ MCP, tools, plugins, agents, computer-use
108
+ governance/ review, proposals, automation, workflows, hooks
109
+ knowledge/ export/import/backup, network, boundary, voice
110
+ modelops/ catalog, recommendations, setup
111
+ adminops/ audit, security dashboard, funnel
112
+ shell/ SPA + 308 redirects
113
+ lattice-agent may it run, and what does it do
114
+ lattice-core what is true — GraphWriter is the only graph writer
115
+ lattice-retrieval search, memory, chronicle, command, evidence
116
+ lattice-ingest files, folders, browser capture
117
+ lattice-jobs embed drain + index API
118
+ lattice-chat POST /chat
119
+ lattice-auth who is this
120
+ Python worker compute only — 19 allowlisted routes
121
+ frontend/src SPA
122
+ ```
123
+
124
+ Compatibility re-exports at each crate's `src/lib.rs` keep the pre-v12 paths
125
+ working for *consumers*. New code inside a crate uses the real path
126
+ (`crate::kernel::state`, `crate::adminops::admin`).
127
+
128
+ | 할 일 | 어디에 | 하지 말 것 |
129
+ | --- | --- | --- |
130
+ | **새 제품 API 라우트** | The `lattice-platform` domain that names the subject (`workspaceos/`, `toolsurface/`, `governance/`, `knowledge/`, `modelops/`, `adminops/`). One module owns one family: `MOUNTED: &[(&str, &str)]` + `router(state)`, then mount the factory in [`rust/lattice-host/src/gateway/product.rs`](../rust/lattice-host/src/gateway/product.rs). A **page shell** is a 308 in `shell/ui_redirects`, not a second handler — except `GET /plugins/sdk` (it carries `require_user`, so it is `toolsurface::plugins`). | Do not put retrieval/search/chronicle in platform — that is `lattice-retrieval`. Ingest doors are `lattice-ingest`. Chat is `lattice-chat`. Index drain is `lattice-jobs`. Identity is `lattice-auth`. Agent-loop HTTP is `lattice-agent` `surface/`. A handler here that re-derives "is this allowed" has forked `lattice_agent::kernel::permission`. A module that opens the graph has become a second writer. |
131
+ | **에이전트 툴 추가** | A tool that runs *inside an agent turn* goes in `lattice-agent` `tools/`, in the file named for what it *touches*: `files` (workspace), `vault` (Brain/Obsidian), `shell` (subprocesses), `desktop` (OS actuation), `render` (document creators), `scaffold` (templates), `local` (paths outside the workspace). Add the name to `tools::host::MUTATING_TOOLS` or `RENDER_TOOLS` and the arm to the dispatcher in [`tools/host.rs`](../rust/lattice-agent/src/tools/host.rs). `is_native` follows from those tables. Every path resolves through `tools::sandbox::Workspace::resolve`; every write passes `content::sanitize::sanitize_write_content`. HTTP exposure of an existing capability is `lattice-platform` `toolsurface/tools/` plus `tools::governance_for` — not a second implementation. | Do not add a permission check in the handler — the kernel already said yes. Do not `std::fs` a raw path. Do not put a read-only compute handler here; those stay on the worker (`POST /agent/tool`). Pointer tools (`computer_*` click/move/type/…) stay on the worker on purpose. |
132
+ | **파싱 러그 추가** | `lattice-agent` `parse/action.rs` (or `parse/channel.rs` for a new model-family frame). Append it to the ordered chain — cheapest and most literal first — and **name it in the returned `repairs` list**. CPython primitives (`pyjson`, `pyliteral`, `pyshlex`, `pystr`) are pinned by [`rust/fixtures/agent/`](../rust/fixtures/agent/FROZEN.md). New answers that Python never gave live under **new keys** (see [`agent_loop/FROZEN.md`](../rust/fixtures/agent_loop/FROZEN.md) `*_extended`). | A silent repair is a bug. Never supply a tool name, path, or argument the model did not write. Do not put a decision here (that is `kernel/`) or file-body salvage (that is `content/`). Do not rewrite a frozen row in place. |
133
+ | **새 워커 계산** | Compute only — embed, parse, extract, render, ASR, LLM. Add the handler next to its family (`latticeai/api/worker_compute.py` for the `/worker/*` seams, or `WORKER_ROUTES` / `WORKER_SEAM_ROUTES` in [`latticeai/runtime/build_phases/worker_profile.py`](../latticeai/runtime/build_phases/worker_profile.py) if it is a model/engine door). Append `(method, path)` to the matching tuple. Regenerate the committed projection: `.venv/bin/python scripts/gen_worker_allowlist_fixture.py`. `tests/unit/test_worker_allowlist.py` pins the file; `lattice-host` `include_str!`s [`rust/fixtures/worker_allowlist.json`](../rust/fixtures/worker_allowlist.json). There are **19** routes. Anything not native and not on that list is `404`. | Do not add a worker route for a product feature. The worker stores nothing — no graph, no sessions, no files. `latticeai.app_factory.build_context` is the **worker** composition root (`create_worker_app`); it is not the product. Import of `latticeai.app_factory` must stay free of MLX/GPU init, singleton construction, filesystem writes, and network calls. |
134
+ | **프론트 화면 / 패널** | Route screens in `frontend/src/pages/` (`Brain`, `Capture`, `Chronicle`, `Act`, `Library`, `System`). Feature panels in `frontend/src/features/<area>/`. Shared chrome in `frontend/src/components/`. Hash routes and aliases in `frontend/src/routes.ts`. Copy in `frontend/src/i18n/`: one namespace module calls `registerCopy` (`shell` is eager; `brain`, `workspace`, `onboarding`, `chronicle` register from the lazy route). Author Korean first; ship the English pair in the same module. `t()` falls back to `COPY.ko`. Colors from [`frontend/src/styles/tokens.css`](../frontend/src/styles/tokens.css) as `hsl(var(--token))`; surface rules in the narrowest file under `frontend/src/styles/experience/`. Brain behavior belongs in `useBrainChat` / `useBrainHistory` / `useBrainIngestion` / `useBrainProof`. Failed `ApiResult` stays an unavailable/error state. Colocate a Vitest file; the floor is 100% `all: true`. | No user-facing English/Korean literals in TSX (`scripts/check_i18n_literals.mjs`). Do not import a namespace you do not register (`check:i18n-namespaces`). Do not add a competing shell rule to `styles.css`. Do not put runtime metrics or admin tools on the default first screen. Regenerate `frontend/src/api/openapi.ts` with `npm run frontend:openapi` when a contract changes — do not edit it by hand. |
135
+ | **지식 그래프 쓰기** | [`lattice_core::graph_write::GraphWriter`](../rust/lattice-core/src/graph_write/mod.rs) only. Public doors: `open` / `with_parts`; ingest `ingest_file` / `ingest_content` / `ingest_message` / `ingest_event`; vectors `write_vectors` / `write_vectors_with` / `rebuild_vector_index`; curation `curate` / `curate_scan` / `curate_noise` / promotions / `mark_superseded`; documents `delete_document_tree` / `delete_node` / `set_node_sensitivity` / `stamp_node_validity` / `set_local_source_watch` / `remove_local_source` / `upsert_nodes` / `upsert_edges`; provenance `record_ingestion` / `import_graph_data`. Production `open()` stamps `"personal"` when a write omits a workspace. Chunks, concepts, triples and embeddings arrive as *data* on the request types — the writer never calls a model. | Do not open `knowledge_graph.sqlite` for writing from another crate. Do not go through the retired `/worker/graph/mutate`. `with_parts` leaves `default_workspace` unset so frozen goldens keep their legacy-null rows — that constructor is for parity, not the product. |
136
+ | **검증 / 골든** | Each family has a `FROZEN.md` stating what wrote it and that it must not be regenerated. **Agent kernel** — [`rust/fixtures/agent/`](../rust/fixtures/agent/FROZEN.md), `lattice-agent` `tests/parity.rs`. **Agent loop** — [`rust/fixtures/agent_loop/`](../rust/fixtures/agent_loop/FROZEN.md), `tests/agent_loop.rs`. **Retrieval / embeddings** — [`rust/fixtures/golden/`](../rust/fixtures/golden/FROZEN.md), `lattice-retrieval` + `lattice-core` `tests/golden_embeddings.rs`. **Graph writes** — [`rust/fixtures/graph_write/`](../rust/fixtures/graph_write/FROZEN.md). **Chunking** — [`rust/fixtures/chunking/`](../rust/fixtures/chunking/FROZEN.md), `lattice-ingest`. **HTTP replay** — [`rust/fixtures/http/`](../rust/fixtures/http/FROZEN.md). Python worker: `npm run test:unit`. SPA: `npm run test:frontend`. Visual: `npm run test:visual`. Integration starts its own loopback host with disposable HOME/data and refuses non-loopback URLs (`npm run test:integration`). | Frozen expected values are inviolable. If the port disagrees, **the port changed** — fix the port. A deliberate new answer goes under a **new key** (or a new file) and is named as not-parity; do not edit a row in place. Do not resurrect a deleted generator to "refresh" a golden. |
137
+
138
+ A new gate or verdict goes in `lattice-agent` `kernel/` and fails **closed**:
139
+ unknown is a refusal, an unreachable verifier is `NEEDS_REVIEW`, a policy-less
140
+ tool is its most dangerous plausible class. Gate order is pinned by the agent
141
+ goldens; reordering is a behaviour change.
142
+
143
+ A new built-in prompt goes in `prompts/mod.rs` as a constant plus the test that
144
+ feeds it through the real parser. A caller-supplied prompt always wins.
145
+
146
+ `workspace_os.json` has exactly one writer:
147
+ `workspaceos::workspace::store::WorkspaceOsStore`. Governance's
148
+ `GovernanceState` is a facade over the same `Arc`. Audit lines go through
149
+ `adminops::admin::append_audit_event` only.
150
+
151
+ ## The gates
56
152
 
57
- Run the smallest affected gate while iterating. Before committing broad runtime,
58
- API, UI, or release work, run:
153
+ Run the smallest affected gate while iterating. Before committing broad
154
+ runtime, API, UI, or release work:
59
155
 
60
156
  ```bash
61
157
  npm run lint
@@ -63,125 +159,177 @@ npm run typecheck
63
159
  npm run test:frontend
64
160
  npm run test:unit
65
161
  npm run docs:check-links
162
+ npm run rust:lint # fmt --check + clippy -D warnings
163
+ npm run rust:test
66
164
  ```
67
165
 
68
- `npm run lint` is **ten** gates since 11.8.0, in order: `lint:python`
69
- (ruff + mypy), `lint:visual`, `lint:frontend`, `frontend:openapi:check`,
70
- `check_i18n_literals.mjs`, `check:i18n-namespaces`, `check:bundle`,
71
- `check:server-i18n`, `check_release_evidence_bound.mjs`, and
72
- `check:max-file-lines`. Three left in 11.8.0: `check:python` (ruff already
73
- parses every file on every CI test leg), `check:legacy-debt` (the mjs mirror had
74
- drifted from the Python test that states the same rule — the Python test is
75
- authoritative), and the extension tests, which CI now invokes directly on the
76
- 3.11 + ubuntu leg.
77
-
78
- Coverage floors are not symmetrical, and the difference is deliberate rather
79
- than an oversight: **Python is a line-coverage floor of 90**
80
- (`[tool.coverage.report] fail_under = 90`, branch measurement off) and the
81
- frontend still pins **100% on all four vitest metrics**. Run
82
- `npm run test:coverage` / `npm run test:frontend:coverage` to see either.
83
-
84
166
  Use these when the change touches the relevant surface:
85
167
 
86
168
  ```bash
87
169
  npm run test:integration
88
170
  npm run test:visual
171
+ npm run test:coverage
172
+ npm run test:frontend:coverage
89
173
  npm run desktop:tauri:check
90
174
  npm run release:evidence
91
175
  npm run release:artifacts
92
176
  npm run release:validate
93
177
  ```
94
178
 
95
- Run `npm run build:assets` before `npm run release:evidence`. The capture command
96
- reads the current package version, writes only to `output/release/vX.Y.Z/`, and
97
- requires Playwright Chromium plus `ffmpeg` for the checked-in GIF/WebM evidence.
98
-
99
- `npm run test:integration` starts its own loopback server with disposable HOME,
100
- data, Brain, agent, XDG, temp, SQLite, and vault paths. It refuses non-loopback
101
- external base URLs so validation cannot mutate a developer's real local Brain.
102
-
103
- Regenerate committed API artifacts with `npm run frontend:openapi`; CI and
104
- `npm run lint` fail when either `frontend/openapi.json` or
105
- `frontend/src/api/openapi.ts` differs from a fresh isolated export.
106
-
107
- ## Frontend Experience Ownership
108
-
109
- `frontend/src/styles/tokens.css` owns React color tokens and
110
- `frontend/src/styles/experience.css` imports the focused shell, conversation,
111
- graph, capture, and responsive layers under `frontend/src/styles/experience/`.
112
- Keep ownership in the narrowest surface file; do not add another competing
113
- shell or composer rule to `styles.css`.
114
-
115
- Brain behavior belongs in the focused `useBrainChat`, `useBrainHistory`,
116
- `useBrainIngestion`, and `useBrainProof` hooks. Translation namespaces live in
117
- `frontend/src/i18n/`. Failed `ApiResult` values must remain unavailable/error
118
- states rather than being normalized to healthy empty data, and affected paths
119
- must have Vitest coverage.
120
-
121
- Default mode should expose user tasks and outcomes. Runtime metrics, registry
122
- identifiers, pipeline controls, and administrator tools belong in progressive
123
- disclosure or advanced/admin mode. New navigation and tabs must preserve
124
- keyboard access, visible focus, mobile safe areas, and reduced-motion behavior.
125
-
126
- ## Runtime Assembly
127
-
128
- `latticeai.app_factory` is the composition root. Keep it import-safe:
129
-
130
- - no MLX/GPU init at module import time;
131
- - no singleton construction at module import time;
132
- - no filesystem writes at module import time;
133
- - no external network calls at module import time.
134
-
135
- Runtime assembly seams live under `latticeai.runtime` as typed stages:
136
-
137
- - `config_runtime.py` derives immutable app config values from `Config`;
138
- - `security_runtime.py` applies trusted proxy/security-derived settings;
139
- - `brain_runtime.py` constructs Brain Core and conversation primitives;
140
- - model, platform, and router stages receive explicit dependencies and return
141
- typed bundles rather than ambient `locals()` maps.
142
-
143
- The module-level `server_app` compatibility surface is an explicit allowlist.
144
- Model services use injected state, and HTTP exceptions belong at the API
145
- boundary. Readiness tests should reject forbidden patterns instead of treating
146
- symbol presence alone as architectural completion.
147
-
148
- Future extraction should continue with AgentRuntime, ToolRegistry, config,
149
- server decomposition, and Knowledge Graph stabilization in that order when
150
- architectural debt is present.
151
-
152
- ## Runtime Hook Coverage
153
-
154
- Knowledge Graph ingestion paths must continue to pass through the shared
155
- pre-tool/post-tool lifecycle:
156
-
157
- - browser `read-url` ingestion dispatches `tool.kg_ingest.*` events;
158
- - browser `ingest-current-tab` ingestion dispatches the same lifecycle;
159
- - the native ingest door in `lattice-ingest` (writing through `graph_write`)
160
- remains the single ingestion boundary behind those routes.
161
-
162
- ## Documentation Sync
179
+ ### `npm run lint` — ten members, in this order
180
+
181
+ 1. `lint:python` — `ruff check .` then `mypy`. Fix ruff locally (`ruff check --fix`) before arguing with the rule; mypy errors name the module under `latticeai/` or `lattice_brain/`.
182
+ 2. `lint:visual` — `node --check` on every `tests/visual/*.js`, `*.cjs`, and `tests/visual/mock_server/*.cjs`. A syntax error in a screenshot spec fails here, not in Playwright.
183
+ 3. `lint:frontend` — `scripts/lint_frontend.mjs`: `tsc --noEmit` plus static checks over `frontend/` and `static/app`.
184
+ 4. `frontend:openapi:check` — `frontend/openapi.json` and `frontend/src/api/openapi.ts` must match a fresh isolated export. Fix: `npm run frontend:openapi` and commit both artifacts.
185
+ 5. `scripts/check_i18n_literals.mjs` — no user-facing string literals in `frontend/src` TS/TSX. Move the words into the matching `frontend/src/i18n/` namespace (both `ko` and `en`).
186
+ 6. `check:i18n-namespaces` — a lazy chunk may only read keys whose namespace it imports. Import the namespace module from the route, do not reach into a sibling dictionary.
187
+ 7. `check:bundle` — initial JS gzip closure stays under 150 KiB. Heavy UI belongs behind `React.lazy`. Do not raise the budget to hide a sync import.
188
+ 8. `check:server-i18n` — routers listed in `scripts/check_server_i18n.mjs` must raise through `http_error` / `translate`, never a quoted `detail=`. Adding a name to `LOCALIZED` is how a migration is declared finished. Today it locks 4 worker routers and reports 2 unclaimed (`health`, `search`).
189
+ 9. `scripts/check_release_evidence_bound.mjs` — see the binding paragraph below.
190
+ 10. `check:max-file-lines` — see the line-limit paragraph below.
191
+
192
+ Three things left the local chain in 11.8.0 and must not be "restored" as
193
+ habits: `check:python` (ruff already parses every file on every CI test leg),
194
+ `check:legacy-debt` (the Python test is authoritative), and the extension
195
+ tests (CI invokes them directly on the 3.11 + ubuntu leg). See
196
+ [CI_AND_RELEASE_GATES.md](CI_AND_RELEASE_GATES.md).
197
+
198
+ ### Coverage floors
199
+
200
+ The floors are not symmetrical, and that is deliberate. Python
201
+ (`[tool.coverage.report] fail_under = 90` in `pyproject.toml`) is a
202
+ **line-coverage** floor; branch measurement is off. Run
203
+ `npm run test:coverage`. A red report is uncovered product lines in
204
+ `latticeai/` or `lattice_brain/` — add a unit test, do not lower the floor
205
+ and do not hide the line with `pragma: no cover` unless the exclusion is
206
+ reasoned. The frontend (`vitest.config.ts`) pins **100% on statements,
207
+ branches, functions, and lines** with `all: true`, so an untested new module
208
+ is in the denominator. Run `npm run test:frontend:coverage`. A file that
209
+ vanishes from the text reporter is fully covered; confirm with
210
+ `--coverage.reporter=json-summary`. `frontend/src/i18n/**`,
211
+ `frontend/src/api/openapi.ts`, and `frontend/src/main.tsx` are excluded on
212
+ purpose.
213
+
214
+ ### Max-file-lines (1,000)
215
+
216
+ `scripts/check_max_file_lines.mjs` fails any git-tracked
217
+ `*.py *.ts *.tsx *.js *.mjs *.cjs *.css *.rs` over 1,000 lines, tests
218
+ included. It exists because a 10k-line CSS file and twenty-seven other
219
+ oversize modules landed before anyone noticed — long files burn review and
220
+ conflict on every feature. Split by cohesion, not at line 1,000. Exempt only
221
+ generated paths (`frontend/src/api/openapi.ts`, `static/app/`,
222
+ `static/vendor/`, `src-tauri/gen/`). A new exemption for a hand-written file
223
+ is not a fix.
224
+
225
+ ### Clippy `-D warnings`
226
+
227
+ `npm run rust:lint` is `cargo fmt --all -- --check` then
228
+ `cargo clippy --workspace --all-targets -- -D warnings` inside `rust/`. CI
229
+ runs the same. The workspace no longer carries blanket `#![allow]`. Fix the
230
+ diagnostic at the source; do not re-allow it. Format drift is `cargo fmt`
231
+ in `rust/`.
232
+
233
+ ### Docs gates
234
+
235
+ `npm run docs:check-links` is two scripts:
236
+ `scripts/check_markdown_links.mjs` (README plus one hop of README-linked
237
+ Markdown) and `scripts/check_doc_status.mjs` (every relative link under the
238
+ root canonical docs and `docs/**`). A red line is a broken relative path —
239
+ fix the href or put the file where the href says. Canonical docs (this one
240
+ included) must carry `Current release: **12.1.0 — …**` matching
241
+ `package.json`; do not bump that marker here ahead of the release lane.
242
+ `npm run docs:check-current` is the release-set version pin
243
+ ([`scripts/check_current_release_docs.mjs`](../scripts/check_current_release_docs.mjs)).
244
+ Reference docs ([ROADMAP.md](ROADMAP.md), this file's siblings with
245
+ `Status: reference`) are not held to the version marker. Historical
246
+ changelog entries that mention old versions stay.
247
+
248
+ ### Release-evidence binding
249
+
250
+ `scripts/check_release_evidence_bound.mjs` (lint member 9) checks that
251
+ `output/release/v12.1.0/SCREENSHOT_INDEX.md` still names the live
252
+ `static/app/asset-manifest.json` digest and the visual mock-server
253
+ fingerprint. It exists so a later `npm run build:assets` cannot ship
254
+ screenshots of an older UI, and so a mock-server edit cannot leave the
255
+ evidence describing a panel the tree no longer produces. Fix: run
256
+ `npm run build:assets` then `npm run release:evidence` (needs Playwright
257
+ Chromium and `ffmpeg`); the capture writes only to
258
+ `output/release/vX.Y.Z/`. `LTCAI_SKIP_RELEASE_EVIDENCE_BOUND=1` is the
259
+ capture script's own hatch, not a commit-time escape.
260
+
261
+ ## House rules
262
+
263
+ **Honesty.** A stub is a stub. A capability that cannot complete surfaces
264
+ `unavailable` / `skipped` / `simulation` / `awaiting_approval` with a reason
265
+ — never a fabricated success, score, or record. A remaining `—` in
266
+ [FEATURE_STATUS.md](../FEATURE_STATUS.md) or
267
+ [SURFACE_PARITY.md](SURFACE_PARITY.md) states why it is a design boundary.
268
+ Do not write "Current" over a surface that 404s. See
269
+ [PROJECT_PRINCIPLES.md](../PROJECT_PRINCIPLES.md).
270
+
271
+ **Fail-closed verification is law.** An unverifiable agent outcome is
272
+ `NEEDS_REVIEW`, not success. An unknown permission case is a refusal. A
273
+ policy-less tool takes its most dangerous plausible class. The worker
274
+ allowlist answers `404` for everything it does not name. Graph scope errors
275
+ do not silently widen. Change-proposal approval whose base SHA no longer
276
+ matches is a **409**, never a merge and never a silent overwrite. Additive
277
+ creates may run with minimal friction; mutations and deletions of existing
278
+ user files are staged as review proposals (`change_proposal`) and applied
279
+ exactly as reviewed.
280
+
281
+ **Korean UX is the default.** Author product copy in Korean first; ship the
282
+ English pair in the same `registerCopy` table. `t()` falls back to
283
+ `COPY.ko`. The first-run `LanguageChooser` is on every onboarding step.
284
+ Do not leave an English literal on a path a Korean reader will see — the
285
+ i18n gates exist to catch that, and the remaining English API `detail`
286
+ strings are a named gap in [ROADMAP.md](ROADMAP.md), not a style.
287
+
288
+ **Tokens-only CSS.** React color lives in `frontend/src/styles/tokens.css`
289
+ and is consumed as `hsl(var(--token))`. No hex on a themed surface. Put a
290
+ new rule in the narrowest `frontend/src/styles/experience/` file. Do not
291
+ add another competing shell or composer rule to `styles.css`. No glass —
292
+ the 11.7.0 elevation ladder is the vocabulary.
293
+
294
+ **Workspace `null` = personal.** `DEFAULT_WORKSPACE_ID` is `"personal"`.
295
+ Reads treat a NULL or blank `workspace_id` as personal visibility.
296
+ Production `GraphWriter::open` stamps `"personal"` when a write omits a
297
+ workspace. A request that names two different workspaces (header vs.
298
+ query/body) is 403. Do not invent a second default string, and do not
299
+ treat NULL as "visible to every tenant".
300
+
301
+ ## Documentation sync
163
302
 
164
303
  For user-facing, API, runtime, release, or packaging changes, check:
165
304
 
166
- - `README.md`
167
- - `ARCHITECTURE.md`
168
- - `FEATURE_STATUS.md`
169
- - `RELEASE.md`
170
- - `docs/CHANGELOG.md`
171
- - `SECURITY.md` when trust/security changes
172
- - `vscode-extension/README.md` when editor integration changes
173
- - `docs/LEGACY_COMPATIBILITY.md` when root compatibility files change
305
+ - [README.md](../README.md)
306
+ - [ARCHITECTURE.md](../ARCHITECTURE.md)
307
+ - [FEATURE_STATUS.md](../FEATURE_STATUS.md)
308
+ - [RELEASE.md](../RELEASE.md)
309
+ - [CHANGELOG.md](CHANGELOG.md)
310
+ - [SECURITY.md](../SECURITY.md) when trust/security changes
311
+ - [vscode-extension/README.md](../vscode-extension/README.md) when editor
312
+ integration changes
313
+ - [LEGACY_COMPATIBILITY.md](LEGACY_COMPATIBILITY.md) when root compatibility
314
+ files change
315
+ - this file, when the contributor path or the map changes
316
+ - [ROADMAP.md](ROADMAP.md), when a named gap closes or a new honest leftover
317
+ appears
318
+
319
+ The release-docs lane owns README / ARCHITECTURE / FEATURE_STATUS version
320
+ bumps. Do not move this file's `Current release` marker off 12.1.0 ahead of
321
+ that lane.
174
322
 
175
323
  Release/publish examples must use exact target-version filenames. Do not
176
324
  document wildcard artifact upload commands.
177
325
 
178
- For 11.9.0 release work, exact artifacts are:
326
+ For 12.1.0 release work, exact artifacts are:
179
327
 
180
- - `dist/ltcai-11.9.0-py3-none-any.whl`
181
- - `dist/ltcai-11.9.0.tar.gz`
182
- - `ltcai-11.9.0.tgz`
183
- - `dist/ltcai-11.9.0.vsix`
184
- - `src-tauri/target/release/bundle/dmg/Lattice AI_11.9.0_aarch64.dmg`
328
+ - `dist/ltcai-12.1.0-py3-none-any.whl`
329
+ - `dist/ltcai-12.1.0.tar.gz`
330
+ - `ltcai-12.1.0.tgz`
331
+ - `dist/ltcai-12.1.0.vsix`
332
+ - `src-tauri/target/release/bundle/dmg/Lattice AI_12.1.0_aarch64.dmg`
185
333
 
186
334
  The dmg is ad-hoc signed (effectively unsigned); `npm run release:validate`
187
335
  checks the names and presence, not a Developer ID signature.
@@ -22,7 +22,7 @@ boundary honest (Community is never crippled to upsell Enterprise).
22
22
  ## The extension seam
23
23
 
24
24
  The seam moved to Rust with the product server in 11.6.0 and now lives in
25
- [`rust/lattice-platform/src/admin/enterprise.rs`](../rust/lattice-platform/src/admin/enterprise.rs):
25
+ [`rust/lattice-platform/src/adminops/admin/enterprise.rs`](../rust/lattice-platform/src/adminops/admin/enterprise.rs):
26
26
 
27
27
  - `Edition` — `community` (default) or `enterprise`.
28
28
  - `EnterpriseCapability` — the enum of reserved capabilities (below).
@@ -3,14 +3,14 @@
3
3
  The Multi-Agent Runtime is the **orchestration layer** introduced in v2.0.0 and
4
4
  operationalized in v2.2.0. It sits
5
5
  *above* the single-agent state machine, which since 11.6.0 is the Rust loop
6
- orchestrator ([`lattice_agent::agentloop`](../rust/lattice-agent/src/agentloop/mod.rs))
6
+ orchestrator ([`lattice_agent::agentloop`](../rust/lattice-agent/src/kernel/agentloop/mod.rs))
7
7
  and coordinates a pipeline of named **roles** that hand off work to one another,
8
8
  retry on a failing review, and emit a fully observable, replayable timeline.
9
9
 
10
- - **Source of truth:** `rust/lattice-platform/src/agents.rs` (the orchestration
11
- routes) over `rust/lattice-agent/src/agentloop/` (the state machine)
10
+ - **Source of truth:** `rust/lattice-platform/src/toolsurface/agents/mod.rs` (the orchestration
11
+ routes) over `rust/lattice-agent/src/kernel/agentloop/` (the state machine)
12
12
  - **HTTP surface:** the `agents` family, mounted by `lattice-platform`
13
- - **Persistence:** `rust/lattice-platform/src/review_queue.rs` — the same
13
+ - **Persistence:** `rust/lattice-platform/src/governance/review_queue.rs` — the same
14
14
  `workspace_os.json` document the Review Center reads
15
15
 
16
16
  > **Moved in 11.6.0.** The Python modules this page used to name
@@ -1,6 +1,6 @@
1
1
  # Lattice AI Onboarding
2
2
 
3
- Current release: **11.9.0 — Working Order**.
3
+ Current release: **12.1.0 — Fast Path**.
4
4
 
5
5
  The first-run goal is a five-minute path from "I opened the app" to "my Brain
6
6
  has a source, a question, and proof." This page is the product contract behind
@@ -11,7 +11,9 @@ read the docs first.
11
11
 
12
12
  1. Wake the Brain and confirm the local owner profile.
13
13
  2. Let Lattice inspect local model/runtime readiness (`/setup/scan` is a
14
- real probe; brew/pip items stay manual).
14
+ real probe). Since 12.0.0 a listed brew/pip/uv item can be installed
15
+ from here, one item at a time and only when the user says so — the
16
+ default is still to show the command rather than run it.
15
17
  3. Pick the recommended Brain voice (`/models/recommendations` probes RAM
16
18
  and the worker catalog) or skip model loading.
17
19
  4. Add one source: upload a file, choose and scan a folder, save a note, or
@@ -32,7 +34,15 @@ read the docs first.
32
34
  - Empty states suggest one concrete next action without claiming proof that does
33
35
  not exist yet.
34
36
  - Core-service failures show an unavailable/error state and recovery guidance;
35
- they are never presented as an empty or healthy Brain.
37
+ they are never presented as an empty or healthy Brain. Since 12.0.0 every
38
+ route and every heavy panel sits inside an error boundary with a 다시 시도
39
+ action, so one failing panel costs its own card rather than the screen.
40
+ - The first source can be added on any model the machine can hold: the agent
41
+ profile is measured on load, and a model that cannot emit a tool call is
42
+ guided through numbered choices instead of failing the run.
43
+ - Re-scanning a folder that has not changed is nearly free, so re-indexing is
44
+ a safe habit rather than a cost. A file deleted from disk keeps its memory
45
+ until the user confirms the 「삭제된 파일 정리」 action.
36
46
  - Upload, note, browser, and message ingestion all converge through the unified
37
47
  ingestion pipeline when it is available.
38
48
  - Workspace-scoped content must not leak or overwrite another workspace's graph
@@ -1,4 +1,4 @@
1
- # Lattice AI — Operations Guide (v11.9.0)
1
+ # Lattice AI — Operations Guide (v12.1.0)
2
2
 
3
3
  > **Status: canonical** — kept in sync with the current release. Storage layout
4
4
  > below reflects the SQLite live Brain store and workspace scoping, not the
@@ -29,9 +29,18 @@ tar -czf ltcai-backup-$(date +%Y%m%d).tar.gz ~/.ltcai ~/ltcai-agent
29
29
  tar -xzf ltcai-backup-YYYYMMDD.tar.gz -C ~
30
30
  ```
31
31
 
32
- 인앱 복원(`.latticebrain` / `VACUUM INTO` 스냅샷)은 원자적입니다. **오래
33
- 떠 있는 프로세스에서 복원하면 커넥션이 재활용되기 전까지 복원 전
34
- 바이트를 줄 수 있습니다.** 복원 뒤에는 호스트를 재시작하십시오.
32
+ 인앱 복원(`.latticebrain` / `VACUUM INTO` 스냅샷)은 원자적이고, **v12.0.0
33
+ 부터 즉시 반영됩니다 — 복원 뒤 재시작이 필요 없습니다.** 스토어가
34
+ 세대(generation) 에폭을 들고 있어서, 복원이 에폭을 올리면 이전 세대에서
35
+ 열린 풀 커넥션은 다음 체크아웃에서 낡은 것으로 판정되어 닫히고, 그다음
36
+ 읽기는 복원된 바이트를 봅니다. 11.9.0까지의 완화책이던 "복원 후 재시작"
37
+ 절차는 더 이상 필요하지 않습니다.
38
+
39
+ 삭제된 파일 정리는 별개의 확인된 동작입니다: `POST
40
+ /api/ingestion/folder/prune`가 `confirm` 없이는 dry-run(무엇이 지워질지와
41
+ 노드/엣지/청크/벡터 수)이고, `confirm`이 있을 때만 문서 서브트리를
42
+ `delete_document_tree`로 제거합니다. 어느 경로도 디스크의 파일을 지우지
43
+ 않습니다.
35
44
 
36
45
  ### 2.3 히스토리만 초기화
37
46
  ```bash
@@ -5,7 +5,7 @@ Realtime Collaboration is the subsystem that gives a Lattice AI workspace a live
5
5
  workspace-scoped execution observability for agents, handoffs, reviews,
6
6
  workflows, plugins, retries, and failures. It is delivered over Server-Sent
7
7
  Events (SSE) by an in-process pub/sub bus, the
8
- [`RealtimeBus`](../rust/lattice-platform/src/realtime.rs), native since 11.6.0.
8
+ [`RealtimeBus`](../rust/lattice-platform/src/workspaceos/realtime.rs), native since 11.6.0.
9
9
 
10
10
  The design goal is to surface "what is happening in the workspace right now"
11
11
  (workspaces created, graphs indexed, agents and workflows run, plugins enabled,