ltcai 10.2.0 → 10.4.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 (149) hide show
  1. package/README.md +40 -44
  2. package/docs/CHANGELOG.md +125 -0
  3. package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
  4. package/docs/DEVELOPMENT.md +1 -1
  5. package/docs/MYPY_BACKLOG.md +65 -0
  6. package/docs/ONBOARDING.md +1 -1
  7. package/docs/OPERATIONS.md +1 -1
  8. package/docs/SURFACE_PARITY.md +33 -6
  9. package/docs/TRUST_MODEL.md +1 -1
  10. package/docs/WHY_LATTICE.md +1 -1
  11. package/docs/kg-schema.md +1 -1
  12. package/lattice_brain/__init__.py +1 -1
  13. package/lattice_brain/context.py +23 -3
  14. package/lattice_brain/graph/_kg_common.py +164 -9
  15. package/lattice_brain/graph/_kg_contract.py +223 -0
  16. package/lattice_brain/graph/_kg_fsutil.py +2 -2
  17. package/lattice_brain/graph/curator.py +4 -4
  18. package/lattice_brain/graph/discovery.py +12 -1
  19. package/lattice_brain/graph/discovery_index.py +12 -1
  20. package/lattice_brain/graph/documents.py +17 -2
  21. package/lattice_brain/graph/ingest.py +12 -1
  22. package/lattice_brain/graph/proactive.py +9 -5
  23. package/lattice_brain/graph/projection.py +12 -1
  24. package/lattice_brain/graph/provenance.py +12 -1
  25. package/lattice_brain/graph/retrieval.py +12 -1
  26. package/lattice_brain/graph/retrieval_docgen.py +12 -1
  27. package/lattice_brain/graph/retrieval_reads.py +12 -1
  28. package/lattice_brain/graph/retrieval_vector.py +12 -1
  29. package/lattice_brain/graph/schema.py +6 -2
  30. package/lattice_brain/graph/write_master.py +12 -1
  31. package/lattice_brain/ingestion.py +1 -1
  32. package/lattice_brain/quality.py +5 -5
  33. package/lattice_brain/runtime/agent_runtime.py +7 -2
  34. package/lattice_brain/runtime/contracts.py +2 -1
  35. package/lattice_brain/runtime/hooks.py +1 -1
  36. package/lattice_brain/runtime/multi_agent.py +1 -1
  37. package/lattice_brain/workflow.py +10 -7
  38. package/latticeai/__init__.py +1 -1
  39. package/latticeai/api/admin.py +2 -2
  40. package/latticeai/api/chat.py +19 -15
  41. package/latticeai/api/chat_agent_http.py +19 -7
  42. package/latticeai/api/chat_helpers.py +9 -8
  43. package/latticeai/api/chat_stream.py +2 -2
  44. package/latticeai/api/computer_use.py +2 -2
  45. package/latticeai/api/hooks.py +4 -2
  46. package/latticeai/api/local_files.py +8 -4
  47. package/latticeai/api/mcp.py +2 -2
  48. package/latticeai/api/models.py +5 -5
  49. package/latticeai/api/permissions.py +2 -2
  50. package/latticeai/api/search.py +5 -3
  51. package/latticeai/api/security_dashboard.py +3 -5
  52. package/latticeai/api/setup.py +3 -3
  53. package/latticeai/api/static_routes.py +7 -2
  54. package/latticeai/api/tools.py +9 -4
  55. package/latticeai/api/workspace.py +25 -22
  56. package/latticeai/app_factory.py +132 -1346
  57. package/latticeai/cli/entrypoint.py +1 -1
  58. package/latticeai/core/agent.py +28 -14
  59. package/latticeai/core/audit.py +5 -3
  60. package/latticeai/core/embedding_providers.py +19 -6
  61. package/latticeai/core/legacy_compatibility.py +1 -1
  62. package/latticeai/core/marketplace.py +2 -2
  63. package/latticeai/core/mcp_catalog.py +3 -1
  64. package/latticeai/core/model_compat.py +1 -1
  65. package/latticeai/core/plugins.py +1 -1
  66. package/latticeai/core/project_sessions.py +1 -1
  67. package/latticeai/core/run_explain.py +6 -4
  68. package/latticeai/core/workspace_os.py +0 -4
  69. package/latticeai/core/workspace_os_constants.py +1 -1
  70. package/latticeai/core/workspace_os_state.py +1 -1
  71. package/latticeai/core/workspace_snapshots.py +3 -3
  72. package/latticeai/integrations/telegram_bot.py +52 -3
  73. package/latticeai/models/router.py +61 -34
  74. package/latticeai/runtime/access_runtime.py +3 -1
  75. package/latticeai/runtime/build_phases.py +1417 -0
  76. package/latticeai/runtime/history_writer.py +138 -0
  77. package/latticeai/runtime/review_wiring.py +6 -1
  78. package/latticeai/runtime/runtime_context.py +276 -0
  79. package/latticeai/runtime/sso_config_runtime.py +1 -1
  80. package/latticeai/runtime/stages.py +8 -2
  81. package/latticeai/services/app_context.py +23 -0
  82. package/latticeai/services/architecture_readiness.py +1 -1
  83. package/latticeai/services/brain_intelligence.py +4 -2
  84. package/latticeai/services/cloud_streaming.py +5 -3
  85. package/latticeai/services/command_center.py +1 -1
  86. package/latticeai/services/evidence_actions.py +2 -1
  87. package/latticeai/services/hybrid_chat.py +1 -1
  88. package/latticeai/services/local_knowledge.py +25 -9
  89. package/latticeai/services/memory_service.py +8 -4
  90. package/latticeai/services/model_catalog.py +12 -12
  91. package/latticeai/services/model_engines.py +15 -17
  92. package/latticeai/services/model_loading.py +1 -1
  93. package/latticeai/services/model_runtime.py +31 -33
  94. package/latticeai/services/multimodal_streaming.py +10 -4
  95. package/latticeai/services/openai_compatible_adapter.py +4 -3
  96. package/latticeai/services/product_readiness.py +1 -1
  97. package/latticeai/services/run_executor.py +12 -3
  98. package/latticeai/services/search_service.py +1 -1
  99. package/latticeai/services/triggers.py +1 -1
  100. package/latticeai/setup/auto_setup.py +1 -1
  101. package/latticeai/setup/wizard.py +12 -3
  102. package/latticeai/tools/computer.py +3 -1
  103. package/latticeai/tools/documents.py +2 -2
  104. package/package.json +4 -2
  105. package/scripts/check_current_release_docs.mjs +1 -1
  106. package/src-tauri/Cargo.lock +1 -1
  107. package/src-tauri/Cargo.toml +1 -1
  108. package/src-tauri/tauri.conf.json +1 -1
  109. package/static/app/asset-manifest.json +37 -37
  110. package/static/app/assets/{Act-CbdGD-2i.js → Act-BK__Y5VE.js} +1 -1
  111. package/static/app/assets/{AdminConsole-LgCkXpnf.js → AdminConsole-ZrTyN8_F.js} +1 -1
  112. package/static/app/assets/{Brain-CoPGJI1L.js → Brain-CYHDXpPB.js} +1 -1
  113. package/static/app/assets/{BrainHome-gVnaxSwp.js → BrainHome-bbJulb5g.js} +1 -1
  114. package/static/app/assets/{BrainSignals-ChxAYHtj.js → BrainSignals-CesSPtUP.js} +1 -1
  115. package/static/app/assets/{Capture-DBtgkHZg.js → Capture-p5upXmz2.js} +1 -1
  116. package/static/app/assets/{CommandPalette-Ovtv5I0Y.js → CommandPalette-0zkyC0RY.js} +1 -1
  117. package/static/app/assets/Library-Dt-vABI2.js +1 -0
  118. package/static/app/assets/{LivingBrain-CNz-6NSd.js → LivingBrain-Bz-h6LBH.js} +1 -1
  119. package/static/app/assets/{ProductFlow-ePX-Y73J.js → ProductFlow-DAZ7DJCE.js} +1 -1
  120. package/static/app/assets/{ReviewCard-C4mpvkwH.js → ReviewCard-Bv0WGbY6.js} +1 -1
  121. package/static/app/assets/{System-BvWNK1zc.js → System-BhrJQSD_.js} +1 -1
  122. package/static/app/assets/{activity-CiauPV_3.js → activity-AolpZJCH.js} +1 -1
  123. package/static/app/assets/{bot-Dwct-Q1x.js → bot-CsVsT2nd.js} +1 -1
  124. package/static/app/assets/{brain-BKDW1F0T.js → brain-vuppco5y.js} +1 -1
  125. package/static/app/assets/{button-CCB9Kuw7.js → button-1BP1PJZ3.js} +1 -1
  126. package/static/app/assets/{circle-pause-DNa8WHC5.js → circle-pause-5paeCOtq.js} +1 -1
  127. package/static/app/assets/{circle-play-8jmxn5mf.js → circle-play-By8mFYRV.js} +1 -1
  128. package/static/app/assets/{cpu-mxvF3V1I.js → cpu-DvUQxdKe.js} +1 -1
  129. package/static/app/assets/{download--SmCcx0p.js → download-CFqYG90a.js} +1 -1
  130. package/static/app/assets/{folder-open-D4Wy5roo.js → folder-open-CVSnp-Dp.js} +1 -1
  131. package/static/app/assets/{hard-drive-BwQcSPlS.js → hard-drive-Ckr4Vd6-.js} +1 -1
  132. package/static/app/assets/{index-DDV2YZwM.js → index--0zKF5Ow.js} +3 -3
  133. package/static/app/assets/index-CEkL4_M-.css +2 -0
  134. package/static/app/assets/{input-DFhSjmLS.js → input-D-JKquXU.js} +1 -1
  135. package/static/app/assets/{network-Bts7VO94.js → network-DB88FZ2T.js} +1 -1
  136. package/static/app/assets/{primitives-BuSMEJY8.js → primitives-D8U9OaIo.js} +1 -1
  137. package/static/app/assets/search-B8jsVTw_.js +1 -0
  138. package/static/app/assets/{shield-alert-BfTO6X_G.js → shield-alert-DnsvO79P.js} +1 -1
  139. package/static/app/assets/{textarea-BjctW1oh.js → textarea-4RchwbTa.js} +1 -1
  140. package/static/app/assets/{useFocusTrap-CE43-LrA.js → useFocusTrap-CFJjy7cS.js} +1 -1
  141. package/static/app/assets/{useQuery-Bv3ejLL5.js → useQuery-3Reu-A4a.js} +1 -1
  142. package/static/app/assets/{users-CsNqLZAj.js → users-BTxYksxa.js} +1 -1
  143. package/static/app/assets/{utils-KFFdVG_t.js → utils-D02Vf-fw.js} +1 -1
  144. package/static/app/assets/{workspace-wdCvdyPF.js → workspace-CSJNgX-C.js} +1 -1
  145. package/static/app/index.html +4 -4
  146. package/static/sw.js +1 -1
  147. package/static/app/assets/Library-CBia2Fvm.js +0 -1
  148. package/static/app/assets/index-CQWdDU3z.css +0 -2
  149. package/static/app/assets/search-DZzxhWaQ.js +0 -1
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
  [![CI Status](https://github.com/TaeSooPark-PTS/LatticeAI/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/TaeSooPark-PTS/LatticeAI/actions/workflows/ci.yml)
12
12
  [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
13
13
 
14
- ![v10.2.0 Living Brain walkthrough](output/release/v10.2.0/gifs/v10.2.0-living-brain-walkthrough.gif)
14
+ ![v10.4.0 Living Brain walkthrough](output/release/v10.4.0/gifs/v10.4.0-living-brain-walkthrough.gif)
15
15
 
16
16
  Chat, files, folders, notes, and web pages all flow into one durable knowledge
17
17
  graph on your computer. Any model — local MLX or cloud — can speak with that
@@ -24,9 +24,9 @@ memory. Nothing leaves your machine without explicit consent.
24
24
 
25
25
  | | |
26
26
  | --- | --- |
27
- | **Chat with a Brain that remembers** — every conversation grows durable, source-linked memory ![Brain Chat](output/release/v10.2.0/screenshots/04-brain-chat-home.png) | **See how knowledge connects** — a real relationship graph, not a file list ![Memory Graph](output/release/v10.2.0/screenshots/05-memory-graph.png) |
28
- | **Capture anything** — files, whole folders, notes, screenshots, web pages ![Capture](output/release/v10.2.0/screenshots/06-capture.png) | **Automate with review** — agent changes become proposals you approve first ![Review Center](output/release/v10.2.0/screenshots/12-review-center.png) |
29
- | **Pick a model in one click** — recommended local models for your hardware ![Recommended Models](output/release/v10.2.0/screenshots/02-recommended-models.png) | **Stay in control** — audit, roles, retention in a separate admin surface ![Admin Console](output/release/v10.2.0/screenshots/10-admin-console.png) |
27
+ | **Chat with a Brain that remembers** — every conversation grows durable, source-linked memory ![Brain Chat](output/release/v10.4.0/screenshots/04-brain-chat-home.png) | **See how knowledge connects** — a real relationship graph, not a file list ![Memory Graph](output/release/v10.4.0/screenshots/05-memory-graph.png) |
28
+ | **Capture anything** — files, whole folders, notes, screenshots, web pages ![Capture](output/release/v10.4.0/screenshots/06-capture.png) | **Automate with review** — agent changes become proposals you approve first ![Review Center](output/release/v10.4.0/screenshots/12-review-center.png) |
29
+ | **Pick a model in one click** — recommended local models for your hardware ![Recommended Models](output/release/v10.4.0/screenshots/02-recommended-models.png) | **Stay in control** — audit, roles, retention in a separate admin surface ![Admin Console](output/release/v10.4.0/screenshots/10-admin-console.png) |
30
30
 
31
31
  ## Why Lattice AI
32
32
 
@@ -57,53 +57,47 @@ First-run flow — wake the Brain, pick the owner, load a recommended model:
57
57
 
58
58
  | | | |
59
59
  | --- | --- | --- |
60
- | ![Login](output/release/v10.2.0/screenshots/01-login.png) | ![Model install](output/release/v10.2.0/screenshots/03-install-load-progress.png) | ![Model library](output/release/v10.2.0/screenshots/07-model-library.png) |
60
+ | ![Login](output/release/v10.4.0/screenshots/01-login.png) | ![Model install](output/release/v10.4.0/screenshots/03-install-load-progress.png) | ![Model library](output/release/v10.4.0/screenshots/07-model-library.png) |
61
61
 
62
62
  Screenshot index and capture notes:
63
- [output/release/v10.2.0/SCREENSHOT_INDEX.md](output/release/v10.2.0/SCREENSHOT_INDEX.md)
63
+ [output/release/v10.4.0/SCREENSHOT_INDEX.md](output/release/v10.4.0/SCREENSHOT_INDEX.md)
64
64
 
65
65
  ## Current Release
66
66
 
67
- The current release is **10.2.0 — Load-Bearing Fixes**:
68
-
69
- A full code review of 10.1.1 scored the codebase 71/100 and found two things
70
- that were true but invisible. This release is that review's response.
71
-
72
- - **A database connection leak that hid behind garbage collection.**
73
- `with sqlite3.connect(...)` commits but never closes, and 70+ call sites
74
- relied on it. Nothing showed until something held a stack frame alive — a
75
- profiler, a logged traceback, a coverage tracer — and then descriptors ran
76
- out. Every one of those sites now closes.
77
- - **Coverage is measurable for the first time: 71%.** It could not be run
78
- before, because running it *was* the thing that exhausted descriptors. There
79
- is now a floor in CI so it cannot silently fall.
80
- - **"Sensitive memories are never sent" is finally true.** The filter was real,
81
- correct, and wired — but nothing in the product could mark a memory, and the
82
- blocked-type list was empty, so it could never fire. You can now mark any
83
- memory from the boundary panel, files under `.ssh`/`.aws`/`.env` and friends
84
- are flagged automatically at ingestion, and credential-shaped node types are
85
- blocked outright.
86
- - **Knowledge that leaves is now redacted and recorded.** Secret-shaped text is
87
- stripped from the outbound payload, and every send — and every refusal —
88
- writes an audit entry naming what went, how much, and where to.
89
- - **112 silent `except: pass` handlers now say something.** Behaviour is
90
- unchanged; each records the failure at debug level, so a genuine bug in an
91
- optional path is no longer indistinguishable from the optional thing being
92
- absent.
93
- - **The lint net was widened to the bug classes that actually bit** — loop
94
- variable capture, truncating `zip`, silent handlers — and mypy now runs on
95
- the trust-critical modules. CI gained macOS and Python 3.14, so the machine
96
- this is developed on and the platform it ships a `.dmg` for are both tested.
67
+ The current release is **10.4.0 — Named Ground**:
68
+
69
+ 10.3.0 wrote down what was not measured. 10.4.0 emptied that list — and the
70
+ things that made the list long turned out not to be typing problems.
71
+
72
+ - **The type backlog went from 1,407 errors across 77 modules to zero across
73
+ 274.** Two root causes accounted for 954 of them, and both were readability
74
+ problems: a computed `__all__` that made every star-imported name invisible
75
+ to a checker, and an eleven-mixin contract that existed but was written down
76
+ nowhere. Both are now explicit, and both are guarded by tests.
77
+ - **The composition root is no longer one function.** `app_factory._build` went
78
+ from 1,318 lines to 26, split into ten ordered phases sharing a typed
79
+ `RuntimeContext`. The phase order is a contract with a test, not a comment —
80
+ and reading a value before its phase has run fails by name.
81
+ - **Four real defects surfaced on the way**, including one that meant
82
+ `python -m latticeai.server_app` had never worked: `main` was a local inside
83
+ the old closure and was never on the export list.
84
+ - **The surface-parity matrix has no ◐ left.** VS Code gained folder capture,
85
+ artifact cards with their repair/validation flags, and a hardware-derived
86
+ model recommendation; Telegram gained the same artifact card. All four were
87
+ rendering gaps — the sidecar was already reporting the data.
88
+ - **Coverage moved honestly, not to target.** Frontend 28.5% → 32.3% (208 → 337
89
+ tests), Python 71.6% → 71.8% (1,896 → 1,956). Frontend 70% is still roughly
90
+ 2,200 statements away, and this release does not claim otherwise.
97
91
 
98
92
  Release notes: [RELEASE.md](RELEASE.md) · Full history: [docs/CHANGELOG.md](docs/CHANGELOG.md)
99
93
 
100
- Expected artifacts for 10.2.0 release must use exact filenames:
94
+ Expected artifacts for 10.4.0 release must use exact filenames:
101
95
 
102
- - `dist/ltcai-10.2.0-py3-none-any.whl`
103
- - `dist/ltcai-10.2.0.tar.gz`
104
- - `ltcai-10.2.0.tgz`
105
- - `dist/ltcai-10.2.0.vsix`
106
- - `src-tauri/target/release/bundle/dmg/Lattice AI_10.2.0_aarch64.dmg`
96
+ - `dist/ltcai-10.4.0-py3-none-any.whl`
97
+ - `dist/ltcai-10.4.0.tar.gz`
98
+ - `ltcai-10.4.0.tgz`
99
+ - `dist/ltcai-10.4.0.vsix`
100
+ - `src-tauri/target/release/bundle/dmg/Lattice AI_10.4.0_aarch64.dmg`
107
101
 
108
102
  Do not use wildcard artifact uploads. Package registry publishing remains owner-run.
109
103
 
@@ -123,7 +117,7 @@ See [ARCHITECTURE.md](ARCHITECTURE.md) for details and
123
117
 
124
118
  - External package registries are owner-published and can lag behind GitHub.
125
119
  - PostgreSQL/pgvector is optional scale/migration tooling. SQLite remains the
126
- live local Brain store in 10.2.0.
120
+ live local Brain store in 10.3.0.
127
121
  - Docker, model downloads, cloud model calls, Telegram, Brain Network, and
128
122
  update checks require explicit user action.
129
123
  - Conversation does not fabricate answers when no model is loaded. Agent and
@@ -132,12 +126,14 @@ See [ARCHITECTURE.md](ARCHITECTURE.md) for details and
132
126
  model success.
133
127
  - Some backend-generated messages (for example the Postgres DSN notice) are
134
128
  produced server-side in English and are shown as-is; server-side i18n is not
135
- part of 10.2.0.
129
+ part of 10.3.0.
136
130
 
137
131
  ## Release History
138
132
 
139
133
  | Version | Theme |
140
134
  | --- | --- |
135
+ | 10.4.0 | Named Ground |
136
+ | 10.3.0 | Measured Ground |
141
137
  | 10.2.0 | Load-Bearing Fixes |
142
138
  | 10.1.1 | Reachable Boundary |
143
139
  | 10.1.0 | Hybrid Brain |
package/docs/CHANGELOG.md CHANGED
@@ -4,6 +4,131 @@ The top entry is either the current unreleased main-branch work or the current
4
4
  release line. Older entries are historical and may describe behavior as it
5
5
  existed at that release.
6
6
 
7
+ ## [10.4.0] - 2026-08-02
8
+
9
+ ### Changed
10
+ - **`app_factory._build` is decomposed.** 1,318 lines → 26. The assembly now
11
+ lives in `latticeai/runtime/build_phases.py` as ten ordered phases sharing a
12
+ typed `latticeai/runtime/runtime_context.py::RuntimeContext`:
13
+ `platform → config → identity → brain → domain → web → services →
14
+ foundation_routes → platform_features → interaction`.
15
+ The single closure worked because closures resolve free variables at call
16
+ time; several sections depend on names bound far below them. The
17
+ RuntimeContext preserves that late binding while naming the shared state.
18
+ `tests/unit/test_runtime_context.py` fixes the phase order, which phase
19
+ produces which attribute, and that reading one early fails by name.
20
+ - **mypy covers the whole tree: 274 of 274 modules, 0 errors** (was 193 of 270
21
+ with 1,407 outstanding). `pyproject.toml`'s `[tool.mypy] files` is no longer
22
+ a curated subset.
23
+ - `lattice_brain/graph/_kg_common.py`'s `__all__` is a literal instead of a
24
+ comprehension over `globals()`. The computed form was invisible to a type
25
+ checker, so twelve modules resolved no names behind
26
+ `from ._kg_common import *` (~750 false `name-defined` errors).
27
+ `tests/unit/test_kg_common_exports.py` keeps the literal honest.
28
+ - The star import of `._kg_fsutil` inside `_kg_common` is now an explicit list
29
+ for the same reason.
30
+ - JSON-shaped payloads in `services/model_runtime.py`, `model_engines.py`,
31
+ `model_catalog.py`, `api/setup.py` and `api/models.py` are typed
32
+ `Dict[str, Any]` rather than `Dict[str, object]`, which forced a cast at
33
+ every nested access without catching anything.
34
+ - Optional dependencies (`AsyncOpenAI`, `mx`, `vlm_load`, `lm_load`,
35
+ `pyautogui`) are aliased on import and re-exported as `Any`, so "installed"
36
+ and "absent" have one declared type.
37
+
38
+ ### Added
39
+ - `lattice_brain/graph/_kg_contract.py` — the 23-member surface the eleven
40
+ graph mixins share, written down for the first time. Typing-only: `_Core` is
41
+ `object` at runtime, so `KnowledgeGraphStore`'s MRO is unchanged.
42
+ `tests/unit/test_kg_contract.py` asserts both the contract and that it never
43
+ becomes a real base class.
44
+ - `AppContext.require(field)` — binds a dependency a router cannot run without,
45
+ so an absent one names itself at router construction instead of surfacing as
46
+ `'NoneType' object is not callable` inside a request handler.
47
+ - VS Code `ltcai.captureFolder` — folder ingestion through the same
48
+ `/api/ingestion/folder` endpoint and the same local-read approval dance the
49
+ web Capture view uses.
50
+ - VS Code `ltcai.showArtifacts` — `artifacts[]` rendered as cards with the
51
+ server's repair and validation flags, so a deterministically repaired
52
+ scaffold no longer looks identical to clean model output.
53
+ - VS Code model picker now shows the hardware-derived recommendation from
54
+ `GET /setup/scan`, with the server's own reasoning. No scan, no banner.
55
+ - Telegram sends the same `artifacts[]` card summary before the files.
56
+ - Frontend tests for four previously untested modules with real logic:
57
+ `routes.ts`, `store/appStore.ts`, `api/base.ts` error translation, and
58
+ `pages/brain/graphExplorer.ts`.
59
+
60
+ ### Fixed
61
+ - **`python -m latticeai.server_app` raised `AttributeError`.** `main` was a
62
+ local inside the old `_build` closure and was never on the export allowlist,
63
+ so `get_shared_runtime().main()` could never resolve it. `main()` now serves
64
+ the shared runtime directly and needs no export.
65
+ - `core/workspace_os.py`: four lines duplicated after a `return` in
66
+ `remove_member` — dead since the merge that introduced them.
67
+ - `models/router.py`: a `str` shadowed a `dict` in the custom cloud-model loop,
68
+ so those entries built their ids from the wrong variable.
69
+ - `lattice_brain/context.py`: an unconfigured retrieval seam was called
70
+ unguarded and died as a `TypeError` inside failure isolation instead of
71
+ naming itself.
72
+ - `runtime/review_wiring.py` declared its injected `HTTPException` as
73
+ `type[Exception]`, which does not accept `(status_code=, detail=)`.
74
+ - `api/security_dashboard.py`: a defensive `isinstance(rows, list)` re-check
75
+ that every branch above already guaranteed.
76
+
77
+ ### Documentation
78
+ - `docs/MYPY_BACKLOG.md` is now the record of how the boundary closed, not a
79
+ list of what is left — including the conventions the sweep settled on.
80
+ - `docs/SURFACE_PARITY.md`: no ◐ remains in the matrix. A stale v9.9.6 note
81
+ claiming the live step timeline was web-only is corrected (v9.9.7's
82
+ `runAgentLive` provides it).
83
+
84
+ ## [10.3.0] - 2026-07-29
85
+
86
+ ### Fixed
87
+ - **Frontend coverage was not being measured.** Vitest reports only files a
88
+ test imports unless `all: true`; untested modules left the denominator
89
+ instead of counting against it. Reported 54%, actual **28.5%**.
90
+ - **Python coverage was over-reported.** `omit = ["*/tests/*"]` does not match
91
+ the repo-relative `tests/...` paths coverage records, so the suite counted
92
+ itself. Reported 80%, actual **71.6%**.
93
+ - `lattice_brain/runtime/hooks.py` referenced `self._path` (does not exist)
94
+ inside the handler for an unreadable registry, turning a recoverable failure
95
+ into `AttributeError`.
96
+ - `lattice_brain/graph/_kg_fsutil.py` used `Iterable` in an annotation without
97
+ importing it.
98
+ - `lattice_brain/runtime/agent_runtime.py` could call `.get` on `None`.
99
+ - `pages/Library.tsx` printed the raw registry coordinate
100
+ (`mlx-community/gemma-4-…`) when a catalog entry had no display name; it now
101
+ falls through `humanizeModelId` like every other model surface.
102
+
103
+ ### Added
104
+ - `latticeai/runtime/history_writer.py` — `save_to_history` extracted from the
105
+ `app_factory._build` closure with its ordering contract documented, plus 14
106
+ tests (`tests/unit/test_history_writer.py`).
107
+ - `frontend/src/test/renderPage.tsx` — a page-level harness that stubs the
108
+ whole `latticeApi` surface, making error states, empty states and both
109
+ languages reachable in unit tests.
110
+ - First unit tests for every page: `System` (11), `Library` (7), `Capture` (6),
111
+ `Act` (8), `Brain` (8). Frontend suite 154 → 208 tests.
112
+ - Python tests for boundaries that had none: Telegram allowlist (26),
113
+ `run_command` containment (34), audit log and sensitivity report (22),
114
+ model-load consent gates (14), connection lifecycle guards. 1,786 → 1,896.
115
+ - `docs/MYPY_BACKLOG.md` — the 77 modules still outside type checking with
116
+ per-module error counts, smallest first.
117
+ - ARCHITECTURE.md "Verification Surface" diagram: which figures gate CI and
118
+ which are only reported.
119
+
120
+ ### Changed
121
+ - mypy adopted set: 13 → **193 of 270** modules.
122
+ - `vitest.config.ts` gains a coverage block with `all: true`.
123
+ - `npm run test:frontend:coverage` added.
124
+
125
+ ### Known
126
+ - `app_factory._build` remains ~1,300 lines; only the history writer was
127
+ extracted this release.
128
+ - Frontend coverage (28.5%) is reported but not gated — flooring it at today's
129
+ figure would freeze it there.
130
+ - 77 modules remain outside mypy; see the backlog.
131
+
7
132
  ## [10.2.0] - 2026-07-29
8
133
 
9
134
  Response to a full code review of 10.1.1 (71/100). All twelve findings.
@@ -1,6 +1,6 @@
1
1
  # Community And Plugins
2
2
 
3
- Current release: **10.2.0 — Load-Bearing Fixes**.
3
+ Current release: **10.4.0 — Named Ground**.
4
4
 
5
5
  LatticeAI defines the path from a strong local-first framework (8.4.0
6
6
  action-aware baseline, 8.5.0 registry+DI hardening, 8.6.0 capture/navigation
@@ -3,7 +3,7 @@
3
3
  > **Status: canonical** — current contributor guidance, kept in sync with the
4
4
  > current release.
5
5
 
6
- Current release: **10.2.0 — Load-Bearing Fixes**.
6
+ Current release: **10.4.0 — Named Ground**.
7
7
 
8
8
  This document is for contributors working on the local-first Digital Brain
9
9
  codebase. Product positioning and quick start stay in `README.md`; release
@@ -0,0 +1,65 @@
1
+ # mypy backlog
2
+
3
+ > **Status: reference**
4
+ > 10.2.0: 13 modules checked. 10.3.0: 193 of 270, with the remaining 77 listed
5
+ > here by error count. **10.4.0: 274 of 274 — the backlog is empty.**
6
+
7
+ `[tool.mypy] files` in `pyproject.toml` now lists **every** module in
8
+ `lattice_brain/` and `latticeai/`. There is no "adopted set" any more; adding a
9
+ module to the tree adds it to the checked set, and CI fails if it does not
10
+ type-check.
11
+
12
+ This file stays as the record of how the boundary closed, because two of the
13
+ 1,407 errors 10.3.0 measured turned out to be worth more than the annotations.
14
+
15
+ ## Two root causes were 68% of the backlog
16
+
17
+ **`_kg_common.__all__` was computed** (`[name for name in globals() if not
18
+ name.startswith("__")]`). Correct at runtime, opaque to a checker: mypy could
19
+ not resolve a single name behind `from ._kg_common import *`, so twelve graph
20
+ modules reported **~750 false `name-defined` errors** and stayed unchecked.
21
+ Freezing the list to a literal fixed all of them.
22
+ `tests/unit/test_kg_common_exports.py` asserts the literal still equals what
23
+ the expression would produce, so it cannot drift.
24
+
25
+ **The eleven graph mixins had an unwritten contract.** `ingest` calls
26
+ `_upsert_node` from `write_master`, which calls `_v2_project_node` from
27
+ `projection`, and everything calls `_connect` — 229 `attr-defined` errors,
28
+ because from a checker's position each mixin is a bare class calling methods it
29
+ does not have. `lattice_brain/graph/_kg_contract.py` writes that contract down:
30
+ 23 declared members, typing-only (`_Core` is `object` at runtime, so the MRO is
31
+ unchanged), verified by `tests/unit/test_kg_contract.py`.
32
+
33
+ Neither was a typing problem. Both were *readability* problems that a reader
34
+ had to solve by grepping eleven files.
35
+
36
+ ## Real defects the remaining work surfaced
37
+
38
+ | defect | module | why it mattered |
39
+ | --- | --- | --- |
40
+ | four lines duplicated after a `return` | `core/workspace_os.py` | dead since the merge that introduced it; `remove_member` looked like it did its work twice |
41
+ | `main` was never exported | `app_factory.py` | `python -m latticeai.server_app` raised `AttributeError` — the module entrypoint did not run |
42
+ | `str` shadowed a `dict` in a loop | `models/router.py` | the custom-cloud-model branch built ids from the wrong variable |
43
+ | `Optional` seam called unguarded | `lattice_brain/context.py` | an unconfigured retrieval port raised `TypeError` inside failure isolation instead of naming itself |
44
+
45
+ (10.3.0 found three more the same way: `self._path` inside an error handler,
46
+ `Iterable` used without importing it, and `.get` on a possible `None`.)
47
+
48
+ ## Conventions the sweep settled on
49
+
50
+ * **JSON-shaped payloads are `Dict[str, Any]`, not `Dict[str, object]`.**
51
+ `object` forced a cast at every nested access without catching anything;
52
+ the value really is arbitrary JSON.
53
+ * **Optional dependencies are aliased, then re-exported as `Any`**
54
+ (`AsyncOpenAI`, `mx`, `vlm_load`, `pyautogui`), so "installed" and "absent"
55
+ have the same declared type and call sites keep their historical name.
56
+ * **Async generators in Protocols are declared with `def`, not `async def`.**
57
+ `async def … -> AsyncIterator[T]` says "a coroutine resolving to an
58
+ iterator", which no implementation does.
59
+ * **A router dependency it cannot run without is bound through
60
+ `AppContext.require("name")`** — an absent dependency names itself at router
61
+ construction instead of surfacing as `'NoneType' object is not callable`
62
+ inside a request handler.
63
+ * **A defensive `isinstance` check on a parameter means the annotation is
64
+ wrong.** Widen the parameter (`Any`, `Optional[str]`) rather than deleting
65
+ the guard: the guard is what the callers actually need.
@@ -1,6 +1,6 @@
1
1
  # Lattice AI Onboarding
2
2
 
3
- Current release: **10.2.0 — Load-Bearing Fixes**.
3
+ Current release: **10.4.0 — Named Ground**.
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
@@ -1,4 +1,4 @@
1
- # Lattice AI — Operations Guide (v10.2.0)
1
+ # Lattice AI — Operations Guide (v10.4.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
@@ -2,7 +2,8 @@
2
2
 
3
3
  > **Status: reference**
4
4
  > 도입: v9.9.4 (2026-07-25, 리뷰 Wave 1.4 "표면 패리티 체크리스트").
5
- > 갱신: v9.9.7 — 표에 남아 있던 ✖ 갭을 **전부** 해소. Browser 확장도 캡처 전용 경계를 넘어 회상/승인 가시성을 갖췄다.
5
+ > 갱신: v9.9.7 — 표에 남아 있던 ✖ 갭을 **전부** 해소.
6
+ > 갱신: **v10.4.0 — 남아 있던 ◐(부분 제공)를 전부 해소.** 표에 ◐ 는 더 이상 없다.
6
7
  > 목적: 각 표면(서피스)이 Brain 계약의 어디까지를 제공하는지 **정직하게** 기록한다.
7
8
  > "앱마다 다른 Lattice"를 방지하는 기준표이며, 기능을 과장하지 않는다.
8
9
 
@@ -22,9 +23,9 @@
22
23
 
23
24
  | 루프 순간 | Web `/app` | Desktop | VS Code | Browser | Telegram |
24
25
  | --- | --- | --- | --- | --- | --- |
25
- | 첫 저장 (Capture) | ✅ DnD·폴더·웹 수집, 품질 경고 | ✅ (웹과 동일) | ◐ `sendToLattice` 선택 영역 전송 | ✅ 페이지 캡처 (단일 목적) | ◐ 메시지 수집 |
26
+ | 첫 저장 (Capture) | ✅ DnD·폴더·웹 수집, 품질 경고 | ✅ (웹과 동일) | ✅ `sendToLattice` 파일/선택 + `captureFolder` 폴더 수집 (v10.4.0) | ✅ 페이지 캡처 (단일 목적) | ✅ 메시지 + 문서/사진 업로드 수집 |
26
27
  | 첫 회상 (Recall) | ✅ 하이브리드 검색 + grounding 배지 + 출처→청크 | ✅ (동일) | ✅ `askCurrentFile`/`askBrain` — 동일 `/chat` grounding 배지 | ✅ 팝업 질문 + 동일 grounding 배지 (v9.9.7) | ✅ 답변 + grounding 배지 (v9.9.7) |
27
- | 첫 산출물 (Artifact) | ✅ artifacts[] 카드·미리보기·Brain 기억 칩 | ✅ (동일) | ◐ `createFile` + `runAgent`/`runAgentLive` 스텝·파일 요약, 카드 UI 없음 | — (의도: 브라우저에서 파일 쓰기 없음) | ◐ 파일 전송만 |
28
+ | 첫 산출물 (Artifact) | ✅ artifacts[] 카드·미리보기·Brain 기억 칩 | ✅ (동일) | ✅ `createFile` + `showArtifacts` 카드(보정/검증 플래그 포함, v10.4.0) | — (의도: 브라우저에서 파일 쓰기 없음) | ✅ 파일 + artifacts[] 카드 요약 (v10.4.0) |
28
29
  | 첫 보호 (Approval) | ✅ 승인 카드 + TTL 카운트다운 + 재시작 생존 | ✅ (동일) | ✅ List/Approve/Reject 커맨드 + 토큰 캐시 | ✅ 대기 건수 표시 (승인은 웹에서, v9.9.7) | ✅ 인라인 Done/Cancel + run_id/token 재개 |
29
30
 
30
31
  ## 기능 상세 × 표면
@@ -42,14 +43,14 @@
42
43
  | Watch 상태 신호 (v9.9.4) | ✅ 홈 카드 | ✅ | — | — | — |
43
44
  | Review Center (제안 승인/거절) | ✅ | ✅ | ✅ `ltcai.reviewCenter` (409 충돌 정직 보고) | — (의도: 승인은 웹/에디터/봇에서) | ✅ `/review` + 인라인 승인/거절 (v9.9.7) |
44
45
  | 데모 코퍼스 First Value Loop | ✅ | ✅ | — | — | — |
45
- | 모델 로드/추천 | ✅ | ✅ | ◐ `loadModel` | — | — |
46
+ | 모델 로드/추천 | ✅ | ✅ | ✅ `loadModel` + 하드웨어 기반 추천·근거 (v10.4.0) | — (의도) | — (의도) |
46
47
  | 코드 편집 보조 (edit/explain/refactor/tests) | — | — | ✅ | — | — |
47
48
 
48
49
  ## 규칙
49
50
 
50
51
  1. **API 계약이 기준이다.** 모든 표면은 동일한 sidecar API를 소비한다. 표면별 전용 API를 만들지 않는다.
51
52
  2. **◐/✖ 는 문서화된 상태로만 존재한다.** 새 기능을 Web에 추가할 때 이 표를 갱신하고, 다른 표면에 제공하지 않을 경우 "—(의도)" 인지 "✖(갭)" 인지 명시한다.
52
- 3. **✖ 갭은 릴리스 노트의 Honest Limitations 후보다.** v9.9.7 기준 표에 남은 ✖는 **없다**.
53
+ 3. **✖ 갭은 릴리스 노트의 Honest Limitations 후보다.** v9.9.7 이후 표에 남은 ✖는 **없고**, v10.4.0 이후 ◐ 도 **없다**.
53
54
  - 남은 "—"는 전부 **설계 경계**이며, 각 항목에 이유를 함께 적는다. 예: 브라우저 확장은 파일을 쓰지 않으므로 산출물 생성이 없고, 승인 *결정*은 서명된 토큰이 필요한 흐름이라 대기 건수만 보여 주고 승인은 웹/에디터/봇에서 한다.
54
55
  - 경계를 새로 만들 때는 "왜 제공하지 않는가"를 이 문서에 남긴다. 이유 없는 "—"는 갭이지 경계가 아니다.
55
56
  4. 릴리스마다 이 표를 검토한다 (release checklist의 docs 단계).
@@ -63,7 +64,7 @@
63
64
 
64
65
  - **회상 배지**: `askCurrentFile` / `askBrain`은 웹과 동일한 `POST /chat` 응답의 `grounding` 판정을 그대로 읽는다. 판정이 없으면 "근거 있음"으로 승격하지 않고 `unknown`으로 보고한다 (`vscode-extension/surface.ts::groundingBadge`).
65
66
  - **Review Center**: `ltcai.reviewCenter`는 웹과 같은 `/api/proposals` 표면을 쓴다. 승인은 검토한 내용 그대로 적용하고, 409(스테이징 이후 파일 변경)는 "아무것도 쓰지 않았다"고 정직하게 알린다.
66
- - **스텝 요약**: `ltcai.runAgent`는 실행 후 `steps`/`created_files`/`explanation`을 출력 채널에 요약한다. 라이브 SSE 타임라인은 아직 웹 전용이다 (◐).
67
+ - **스텝 요약**: `ltcai.runAgent`는 실행 후 `steps`/`created_files`/`explanation`을 출력 채널에 요약한다. (라이브 SSE 타임라인은 v9.9.7의 `ltcai.runAgentLive`가 같은 `agent_step` 프레임으로 제공한다 — 이 메모는 v9.9.6 시점 기준이었다.)
67
68
  - 파싱 계약은 `tests/vscode-extension.test.cjs`가 sidecar 페이로드 모양 그대로 검증한다.
68
69
 
69
70
  ## v9.9.7 — 남은 갭을 닫은 방법
@@ -81,3 +82,29 @@
81
82
  - **Browser 확장**: 캡처 전용 경계를 넘어 `/chat` 질문과 `agent/approvals` 대기
82
83
  건수를 보여 준다. **판정을 로컬에서 계산하지 않는다** — 판정이 없으면
83
84
  "근거 확인 불가"이며 절대 "근거 있음"으로 승격하지 않는다.
85
+
86
+ ## v10.4.0 — 마지막 ◐ 셋을 닫은 방법
87
+
88
+ 세 갭 모두 **계약의 문제가 아니라 렌더링의 문제**였다. 사이드카는 이미 필요한
89
+ 데이터를 내고 있었고, 에디터/봇이 그것을 버리고 있었다.
90
+
91
+ - **VS Code 폴더 수집** (`ltcai.captureFolder`): 웹 Capture 와 **같은**
92
+ `POST /api/ingestion/folder` 를 쓴다. 로컬 디스크를 읽으므로 승인 절차도
93
+ 동일하다 — 토큰 없는 첫 호출은 `permission_required` 이고, 확장은 그것을
94
+ 그대로 보여 준다(조용히 재시도하지 않는다). 백그라운드 job 으로 큐잉되며
95
+ 진행 상황은 웹에서 본다.
96
+ - **VS Code 산출물 카드** (`ltcai.showArtifacts`): `/agent` 응답의
97
+ `artifacts[]` 를 그대로 읽는다. 결정적으로 **보정된(repaired) 스캐폴드와
98
+ 깨끗한 모델 출력이 구분된다** — 이전에는 평평한 경로 목록이라 둘이 같아
99
+ 보였다. 서버의 `valid=false` 판정을 확장이 성공으로 승격하지 않는다.
100
+ 구형 사이드카(`created_files` 만 있는 응답)는 "세부 정보 없음"이라고
101
+ 정직하게 말한다.
102
+ - **VS Code 모델 추천**: `GET /setup/scan` 의 `zero_config.recommend` 를
103
+ 읽어 **이 기기에 왜 이 모델인지**를 근거와 함께 보여 준다. 스캔이 없으면
104
+ 배너도 없다 — 이유를 지어내지 않는다.
105
+ - **Telegram 산출물 카드**: 같은 `artifacts[]` 플래그를 파일 전송 직전에
106
+ 요약한다. 8개까지 나열하고 나머지는 `… +N` 으로 정직하게 줄인다.
107
+
108
+ 계약 테스트: `tests/vscode-extension.test.cjs` (19), `tests/unit/test_telegram_security.py`.
109
+ 전부 사이드카 페이로드 모양 그대로 검증하며, 확장/봇이 서버 판정을 재계산하지
110
+ 않는지도 함께 고정한다.
@@ -1,6 +1,6 @@
1
1
  # Lattice AI Trust Model
2
2
 
3
- Current release: **10.2.0 — Load-Bearing Fixes**.
3
+ Current release: **10.4.0 — Named Ground**.
4
4
 
5
5
  Lattice AI is local-first, explicit about external communication, and honest
6
6
  when a capability is unavailable.
@@ -1,6 +1,6 @@
1
1
  # Why Lattice AI Exists
2
2
 
3
- Current release: **10.2.0 — Load-Bearing Fixes**.
3
+ Current release: **10.4.0 — Named Ground**.
4
4
 
5
5
  **Lattice AI is a local-first Digital Brain that keeps your knowledge durable
6
6
  across any AI model.**
package/docs/kg-schema.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Knowledge Graph Schema
2
2
 
3
- Current release: **10.2.0 — Load-Bearing Fixes**.
3
+ Current release: **10.4.0 — Named Ground**.
4
4
 
5
5
  명세 출처: `lattice_ai_full_spec.pptx` 슬라이드 20·21·22
6
6
  구현: `lattice_brain/graph/schema.py`
@@ -26,7 +26,7 @@ from .storage import (
26
26
  storage_from_env,
27
27
  )
28
28
 
29
- __version__ = "10.2.0"
29
+ __version__ = "10.4.0"
30
30
 
31
31
  __all__ = [
32
32
  "AgentRuntime",
@@ -24,14 +24,27 @@ def approx_tokens(text: str) -> int:
24
24
  return max(0, (len(text or "") + 3) // 4)
25
25
 
26
26
 
27
- def _call_context_seam(callback: Callable[..., Any], query: str, **context: Any) -> Any:
27
+ class _SeamUnavailable(RuntimeError):
28
+ """Raised when a context seam is called but was never configured."""
29
+
30
+
31
+ def _call_context_seam(
32
+ callback: Optional[Callable[..., Any]], query: str, **context: Any
33
+ ) -> Any:
28
34
  """Pass identity/scope only when a legacy seam declares support for it.
29
35
 
30
36
  Signature inspection preserves old one-argument adapters without catching
31
37
  a callback's own ``TypeError`` and retrying it with less restrictive scope.
32
38
  If a callable cannot be inspected, the secure behavior is to pass every
33
39
  context field and let the caller's failure isolation omit that section.
40
+
41
+ ``callback`` is Optional because the seams themselves are: an assembler
42
+ built without a hybrid-search port simply has no knowledge section. The
43
+ section builders already run inside failure isolation, so an unconfigured
44
+ seam degrades to "section omitted" rather than crashing the turn.
34
45
  """
46
+ if callback is None:
47
+ raise _SeamUnavailable("seam is not configured")
35
48
  try:
36
49
  parameters = inspect.signature(callback).parameters
37
50
  except (TypeError, ValueError):
@@ -47,8 +60,12 @@ def _call_context_seam(callback: Callable[..., Any], query: str, **context: Any)
47
60
  return callback(query, **supported)
48
61
 
49
62
 
50
- def _call_keyword_seam(callback: Callable[..., Any], **context: Any) -> Any:
63
+ def _call_keyword_seam(
64
+ callback: Optional[Callable[..., Any]], **context: Any
65
+ ) -> Any:
51
66
  """Invoke a keyword-only seam while preserving legacy narrow signatures."""
67
+ if callback is None:
68
+ raise _SeamUnavailable("seam is not configured")
52
69
  try:
53
70
  parameters = inspect.signature(callback).parameters
54
71
  except (TypeError, ValueError):
@@ -177,7 +194,10 @@ class ContextAssembler:
177
194
  # ── section builders (each failure-isolated and honest) ───────────────
178
195
  def _memories_section(self, query, user_email, workspace_id, limit) -> ContextSection:
179
196
  try:
180
- recall = self._memory_recall(query, user_email=user_email, workspace_id=workspace_id, limit=limit)
197
+ recall_seam = self._memory_recall
198
+ if recall_seam is None:
199
+ raise _SeamUnavailable("memory_recall is not configured")
200
+ recall = recall_seam(query, user_email=user_email, workspace_id=workspace_id, limit=limit)
181
201
  results = [r for r in recall.get("results", []) if r.get("source") == "workspace"][:limit]
182
202
  except Exception as exc:
183
203
  logging.debug("context: memory recall failed: %s", exc)