ltcai 10.2.0 → 10.3.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 (69) hide show
  1. package/README.md +39 -44
  2. package/docs/CHANGELOG.md +48 -0
  3. package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
  4. package/docs/DEVELOPMENT.md +1 -1
  5. package/docs/MYPY_BACKLOG.md +98 -0
  6. package/docs/ONBOARDING.md +1 -1
  7. package/docs/OPERATIONS.md +1 -1
  8. package/docs/TRUST_MODEL.md +1 -1
  9. package/docs/WHY_LATTICE.md +1 -1
  10. package/docs/kg-schema.md +1 -1
  11. package/lattice_brain/__init__.py +1 -1
  12. package/lattice_brain/graph/_kg_fsutil.py +1 -1
  13. package/lattice_brain/runtime/agent_runtime.py +7 -2
  14. package/lattice_brain/runtime/hooks.py +1 -1
  15. package/lattice_brain/runtime/multi_agent.py +1 -1
  16. package/latticeai/__init__.py +1 -1
  17. package/latticeai/app_factory.py +43 -67
  18. package/latticeai/core/legacy_compatibility.py +1 -1
  19. package/latticeai/core/marketplace.py +1 -1
  20. package/latticeai/core/workspace_os_constants.py +1 -1
  21. package/latticeai/runtime/history_writer.py +138 -0
  22. package/latticeai/services/architecture_readiness.py +1 -1
  23. package/latticeai/services/product_readiness.py +1 -1
  24. package/package.json +4 -2
  25. package/scripts/check_current_release_docs.mjs +1 -1
  26. package/src-tauri/Cargo.lock +1 -1
  27. package/src-tauri/Cargo.toml +1 -1
  28. package/src-tauri/tauri.conf.json +1 -1
  29. package/static/app/asset-manifest.json +37 -37
  30. package/static/app/assets/{Act-CbdGD-2i.js → Act-DrKIGZeJ.js} +1 -1
  31. package/static/app/assets/{AdminConsole-LgCkXpnf.js → AdminConsole-CEV1VefA.js} +1 -1
  32. package/static/app/assets/{Brain-CoPGJI1L.js → Brain-BZSXnwWU.js} +1 -1
  33. package/static/app/assets/{BrainHome-gVnaxSwp.js → BrainHome-CEBd86k6.js} +1 -1
  34. package/static/app/assets/{BrainSignals-ChxAYHtj.js → BrainSignals-O4s6R7av.js} +1 -1
  35. package/static/app/assets/{Capture-DBtgkHZg.js → Capture-AGbOxsg-.js} +1 -1
  36. package/static/app/assets/{CommandPalette-Ovtv5I0Y.js → CommandPalette-CeqBjCn2.js} +1 -1
  37. package/static/app/assets/Library-CXnbNiQW.js +1 -0
  38. package/static/app/assets/{LivingBrain-CNz-6NSd.js → LivingBrain-D9Iei2ZJ.js} +1 -1
  39. package/static/app/assets/{ProductFlow-ePX-Y73J.js → ProductFlow-JlXiDfMM.js} +1 -1
  40. package/static/app/assets/{ReviewCard-C4mpvkwH.js → ReviewCard-QQly2bTV.js} +1 -1
  41. package/static/app/assets/{System-BvWNK1zc.js → System-Bp9ZY4ER.js} +1 -1
  42. package/static/app/assets/{activity-CiauPV_3.js → activity-CxRPOQ2F.js} +1 -1
  43. package/static/app/assets/{bot-Dwct-Q1x.js → bot-1bcsWB2s.js} +1 -1
  44. package/static/app/assets/{brain-BKDW1F0T.js → brain-CxaeKCRM.js} +1 -1
  45. package/static/app/assets/{button-CCB9Kuw7.js → button-ZYFqGan7.js} +1 -1
  46. package/static/app/assets/{circle-pause-DNa8WHC5.js → circle-pause-B5NOIqvk.js} +1 -1
  47. package/static/app/assets/{circle-play-8jmxn5mf.js → circle-play-BiJVFXf7.js} +1 -1
  48. package/static/app/assets/{cpu-mxvF3V1I.js → cpu-Vvq0OKTr.js} +1 -1
  49. package/static/app/assets/{download--SmCcx0p.js → download-SEcXzkx1.js} +1 -1
  50. package/static/app/assets/{folder-open-D4Wy5roo.js → folder-open-Dw1ff_Qa.js} +1 -1
  51. package/static/app/assets/{hard-drive-BwQcSPlS.js → hard-drive-B00fR7_r.js} +1 -1
  52. package/static/app/assets/index-CEkL4_M-.css +2 -0
  53. package/static/app/assets/{index-DDV2YZwM.js → index-Dm9VXl1o.js} +3 -3
  54. package/static/app/assets/{input-DFhSjmLS.js → input-DtdWYQEa.js} +1 -1
  55. package/static/app/assets/{network-Bts7VO94.js → network-CpjBu0kI.js} +1 -1
  56. package/static/app/assets/{primitives-BuSMEJY8.js → primitives-CwsudBwc.js} +1 -1
  57. package/static/app/assets/search-CwEJWBEA.js +1 -0
  58. package/static/app/assets/{shield-alert-BfTO6X_G.js → shield-alert-CpjBc-gQ.js} +1 -1
  59. package/static/app/assets/{textarea-BjctW1oh.js → textarea-DUPyjHQs.js} +1 -1
  60. package/static/app/assets/{useFocusTrap-CE43-LrA.js → useFocusTrap-BWcMtCWH.js} +1 -1
  61. package/static/app/assets/{useQuery-Bv3ejLL5.js → useQuery-Dg5fPqTS.js} +1 -1
  62. package/static/app/assets/{users-CsNqLZAj.js → users-BgcdTQGM.js} +1 -1
  63. package/static/app/assets/{utils-KFFdVG_t.js → utils-a3YXFtEO.js} +1 -1
  64. package/static/app/assets/{workspace-wdCvdyPF.js → workspace-BMUEiXUm.js} +1 -1
  65. package/static/app/index.html +4 -4
  66. package/static/sw.js +1 -1
  67. package/static/app/assets/Library-CBia2Fvm.js +0 -1
  68. package/static/app/assets/index-CQWdDU3z.css +0 -2
  69. 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.3.0 Living Brain walkthrough](output/release/v10.3.0/gifs/v10.3.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.3.0/screenshots/04-brain-chat-home.png) | **See how knowledge connects** — a real relationship graph, not a file list ![Memory Graph](output/release/v10.3.0/screenshots/05-memory-graph.png) |
28
+ | **Capture anything** — files, whole folders, notes, screenshots, web pages ![Capture](output/release/v10.3.0/screenshots/06-capture.png) | **Automate with review** — agent changes become proposals you approve first ![Review Center](output/release/v10.3.0/screenshots/12-review-center.png) |
29
+ | **Pick a model in one click** — recommended local models for your hardware ![Recommended Models](output/release/v10.3.0/screenshots/02-recommended-models.png) | **Stay in control** — audit, roles, retention in a separate admin surface ![Admin Console](output/release/v10.3.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.3.0/screenshots/01-login.png) | ![Model install](output/release/v10.3.0/screenshots/03-install-load-progress.png) | ![Model library](output/release/v10.3.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.3.0/SCREENSHOT_INDEX.md](output/release/v10.3.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.3.0 — Measured Ground**:
68
+
69
+ 10.2.0 answered a code review. 10.3.0 is about knowing where things actually
70
+ stand: several numbers this project reported about itself turned out to be
71
+ wrong, and those corrections matter more than the features around them.
72
+
73
+ - **Frontend coverage was never really measured.** Vitest only reports files a
74
+ test already imports, so every untested module quietly left the denominator
75
+ and the tool read 54%. With `all: true` the honest figure is **28.5%** — and
76
+ 208 tests now cover pages that had none, including every settings and memory
77
+ screen.
78
+ - **Python coverage was reported as 80%. It is 71.6%.** The `omit` pattern did
79
+ not match the paths coverage records, so the suite was counting itself — and
80
+ test files run start to finish by construction.
81
+ - **mypy went from 13 modules to 193 of 270**, finding three real defects on
82
+ the way: a log line inside an error handler referenced an attribute that does
83
+ not exist (turning a recoverable failure into a crash), an annotation used a
84
+ name that was never imported, and one call could dereference `None`.
85
+ - **The chat-turn writer left the composition root.** 66 lines that decide what
86
+ a message looks like after redaction, and what the audit log records about
87
+ it, are now a module with 14 tests instead of a closure nothing could reach.
88
+ - **What is not measured is written down.** `docs/MYPY_BACKLOG.md` lists the 77
89
+ modules still outside type checking with their error counts, so the boundary
90
+ is a fact rather than an intention.
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.3.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.3.0-py3-none-any.whl`
97
+ - `dist/ltcai-10.3.0.tar.gz`
98
+ - `ltcai-10.3.0.tgz`
99
+ - `dist/ltcai-10.3.0.vsix`
100
+ - `src-tauri/target/release/bundle/dmg/Lattice AI_10.3.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,13 @@ 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.3.0 | Measured Ground |
141
136
  | 10.2.0 | Load-Bearing Fixes |
142
137
  | 10.1.1 | Reachable Boundary |
143
138
  | 10.1.0 | Hybrid Brain |
package/docs/CHANGELOG.md CHANGED
@@ -4,6 +4,54 @@ 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.3.0] - 2026-07-29
8
+
9
+ ### Fixed
10
+ - **Frontend coverage was not being measured.** Vitest reports only files a
11
+ test imports unless `all: true`; untested modules left the denominator
12
+ instead of counting against it. Reported 54%, actual **28.5%**.
13
+ - **Python coverage was over-reported.** `omit = ["*/tests/*"]` does not match
14
+ the repo-relative `tests/...` paths coverage records, so the suite counted
15
+ itself. Reported 80%, actual **71.6%**.
16
+ - `lattice_brain/runtime/hooks.py` referenced `self._path` (does not exist)
17
+ inside the handler for an unreadable registry, turning a recoverable failure
18
+ into `AttributeError`.
19
+ - `lattice_brain/graph/_kg_fsutil.py` used `Iterable` in an annotation without
20
+ importing it.
21
+ - `lattice_brain/runtime/agent_runtime.py` could call `.get` on `None`.
22
+ - `pages/Library.tsx` printed the raw registry coordinate
23
+ (`mlx-community/gemma-4-…`) when a catalog entry had no display name; it now
24
+ falls through `humanizeModelId` like every other model surface.
25
+
26
+ ### Added
27
+ - `latticeai/runtime/history_writer.py` — `save_to_history` extracted from the
28
+ `app_factory._build` closure with its ordering contract documented, plus 14
29
+ tests (`tests/unit/test_history_writer.py`).
30
+ - `frontend/src/test/renderPage.tsx` — a page-level harness that stubs the
31
+ whole `latticeApi` surface, making error states, empty states and both
32
+ languages reachable in unit tests.
33
+ - First unit tests for every page: `System` (11), `Library` (7), `Capture` (6),
34
+ `Act` (8), `Brain` (8). Frontend suite 154 → 208 tests.
35
+ - Python tests for boundaries that had none: Telegram allowlist (26),
36
+ `run_command` containment (34), audit log and sensitivity report (22),
37
+ model-load consent gates (14), connection lifecycle guards. 1,786 → 1,896.
38
+ - `docs/MYPY_BACKLOG.md` — the 77 modules still outside type checking with
39
+ per-module error counts, smallest first.
40
+ - ARCHITECTURE.md "Verification Surface" diagram: which figures gate CI and
41
+ which are only reported.
42
+
43
+ ### Changed
44
+ - mypy adopted set: 13 → **193 of 270** modules.
45
+ - `vitest.config.ts` gains a coverage block with `all: true`.
46
+ - `npm run test:frontend:coverage` added.
47
+
48
+ ### Known
49
+ - `app_factory._build` remains ~1,300 lines; only the history writer was
50
+ extracted this release.
51
+ - Frontend coverage (28.5%) is reported but not gated — flooring it at today's
52
+ figure would freeze it there.
53
+ - 77 modules remain outside mypy; see the backlog.
54
+
7
55
  ## [10.2.0] - 2026-07-29
8
56
 
9
57
  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.3.0 — Measured 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.3.0 — Measured 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,98 @@
1
+ # mypy backlog
2
+
3
+ `[tool.mypy] files` in `pyproject.toml` lists every module that type-checks
4
+ today — **193 of 270** as of 10.3.0, up from 13 in 10.2.0.
5
+ This file is the remainder, smallest first, so the boundary is a measured
6
+ fact rather than an open-ended intention.
7
+
8
+ Adding a module to the checked set is a decision to fix that module, not its
9
+ dependencies (`follow_imports = silent`). Work from the top: the short ones
10
+ are usually one missing annotation.
11
+
12
+ Three of these turned out to be real defects when 10.3.0 measured them:
13
+ `hooks.py` referenced a non-existent `self._path` inside the handler for an
14
+ unreadable registry, `_kg_fsutil.py` used `Iterable` without importing it,
15
+ and `agent_runtime.py` called `.get` on a value that could be `None`. Those
16
+ are fixed; the list below is what is left.
17
+
18
+ | errors | module | dominant codes |
19
+ | ---: | --- | --- |
20
+ | 1 | `lattice_brain/graph/_kg_fsutil.py` | `union-attr` ×1 |
21
+ | 1 | `latticeai/api/admin.py` | `call-overload` ×1 |
22
+ | 1 | `latticeai/api/computer_use.py` | `var-annotated` ×1 |
23
+ | 1 | `latticeai/api/hooks.py` | `unreachable` ×1 |
24
+ | 1 | `latticeai/api/local_files.py` | `arg-type` ×1 |
25
+ | 1 | `latticeai/api/static_routes.py` | `assignment` ×1 |
26
+ | 1 | `latticeai/core/marketplace.py` | `arg-type` ×1 |
27
+ | 1 | `latticeai/core/model_compat.py` | `unreachable` ×1 |
28
+ | 1 | `latticeai/core/plugins.py` | `unreachable` ×1 |
29
+ | 1 | `latticeai/core/project_sessions.py` | `var-annotated` ×1 |
30
+ | 1 | `latticeai/core/workspace_os.py` | `unreachable` ×1 |
31
+ | 1 | `latticeai/core/workspace_os_state.py` | `arg-type` ×1 |
32
+ | 1 | `latticeai/runtime/sso_config_runtime.py` | `arg-type` ×1 |
33
+ | 1 | `latticeai/services/command_center.py` | `assignment` ×1 |
34
+ | 1 | `latticeai/services/search_service.py` | `assignment` ×1 |
35
+ | 1 | `latticeai/services/triggers.py` | `truthy-function` ×1 |
36
+ | 1 | `latticeai/setup/auto_setup.py` | `attr-defined` ×1 |
37
+ | 2 | `lattice_brain/ingestion.py` | `assignment` ×2 |
38
+ | 2 | `latticeai/api/chat_stream.py` | `var-annotated` ×1, `arg-type` ×1 |
39
+ | 2 | `latticeai/api/mcp.py` | `var-annotated` ×2 |
40
+ | 2 | `latticeai/core/mcp_registry.py` | `index` ×1, `return-value` ×1 |
41
+ | 2 | `latticeai/core/run_explain.py` | `union-attr` ×1, `call-overload` ×1 |
42
+ | 2 | `latticeai/core/workspace_snapshots.py` | `var-annotated` ×2 |
43
+ | 2 | `latticeai/runtime/review_wiring.py` | `call-arg` ×2 |
44
+ | 2 | `latticeai/services/brain_intelligence.py` | `operator` ×1, `index` ×1 |
45
+ | 2 | `latticeai/services/memory_service.py` | `misc` ×1, `index` ×1 |
46
+ | 2 | `latticeai/services/model_loading.py` | `arg-type` ×1, `call-overload` ×1 |
47
+ | 2 | `latticeai/services/multimodal_streaming.py` | `attr-defined` ×1, `arg-type` ×1 |
48
+ | 2 | `latticeai/setup/wizard.py` | `attr-defined` ×1, `dict-item` ×1 |
49
+ | 2 | `latticeai/tools/documents.py` | `arg-type` ×2 |
50
+ | 3 | `lattice_brain/quality.py` | `var-annotated` ×2, `return-value` ×1 |
51
+ | 3 | `latticeai/api/models.py` | `call-overload` ×2, `attr-defined` ×1 |
52
+ | 3 | `latticeai/cli/entrypoint.py` | `operator` ×1, `union-attr` ×1, `arg-type` ×1 |
53
+ | 3 | `latticeai/core/audit.py` | `assignment` ×1, `index` ×1, `operator` ×1 |
54
+ | 3 | `latticeai/integrations/telegram_bot.py` | `union-attr` ×2, `arg-type` ×1 |
55
+ | 3 | `latticeai/runtime/stages.py` | `arg-type` ×3 |
56
+ | 3 | `latticeai/services/cloud_streaming.py` | `arg-type` ×2, `attr-defined` ×1 |
57
+ | 3 | `latticeai/services/hybrid_chat.py` | `arg-type` ×3 |
58
+ | 3 | `latticeai/services/tool_dispatch.py` | `arg-type` ×3 |
59
+ | 4 | `lattice_brain/graph/_kg_common.py` | `misc` ×3, `return-value` ×1 |
60
+ | 4 | `latticeai/api/chat_agent_http.py` | `arg-type` ×2, `misc` ×1, `assignment` ×1 |
61
+ | 4 | `latticeai/api/chat_helpers.py` | `call-overload` ×2, `union-attr` ×1, `arg-type` ×1 |
62
+ | 4 | `latticeai/api/security_dashboard.py` | `assignment` ×3, `unreachable` ×1 |
63
+ | 4 | `latticeai/services/evidence_actions.py` | `union-attr` ×4 |
64
+ | 5 | `lattice_brain/context.py` | `arg-type` ×4, `misc` ×1 |
65
+ | 5 | `latticeai/api/permissions.py` | `arg-type` ×5 |
66
+ | 5 | `latticeai/services/model_engines.py` | `attr-defined` ×2, `call-overload` ×2, `unreachable` ×1 |
67
+ | 5 | `latticeai/services/run_executor.py` | `unreachable` ×3, `arg-type` ×1, `return-value` ×1 |
68
+ | 6 | `lattice_brain/runtime/contracts.py` | `union-attr` ×6 |
69
+ | 6 | `latticeai/core/agent.py` | `arg-type` ×4, `return-value` ×2 |
70
+ | 7 | `lattice_brain/graph/proactive.py` | `assignment` ×2, `arg-type` ×2, `index` ×1 |
71
+ | 7 | `latticeai/app_factory.py` | `arg-type` ×3, `assignment` ×2, `attr-defined` ×2 |
72
+ | 7 | `latticeai/core/embedding_providers.py` | `unreachable` ×2, `assignment` ×2, `union-attr` ×2 |
73
+ | 7 | `latticeai/services/openai_compatible_adapter.py` | `arg-type` ×7 |
74
+ | 8 | `lattice_brain/graph/curator.py` | `operator` ×8 |
75
+ | 8 | `lattice_brain/workflow.py` | `arg-type` ×5, `index` ×2, `assignment` ×1 |
76
+ | 8 | `latticeai/api/setup.py` | `index` ×5, `attr-defined` ×3 |
77
+ | 8 | `latticeai/services/local_knowledge.py` | `union-attr` ×3, `misc` ×2, `operator` ×2 |
78
+ | 10 | `latticeai/api/tools.py` | `arg-type` ×10 |
79
+ | 13 | `latticeai/api/search.py` | `return` ×12, `return-value` ×1 |
80
+ | 15 | `lattice_brain/graph/store.py` | `name-defined` ×15 |
81
+ | 16 | `latticeai/api/chat.py` | `arg-type` ×12, `misc` ×3, `call-overload` ×1 |
82
+ | 16 | `latticeai/tools/computer.py` | `union-attr` ×16 |
83
+ | 19 | `lattice_brain/graph/retrieval_docgen.py` | `name-defined` ×12, `attr-defined` ×7 |
84
+ | 35 | `latticeai/services/model_runtime.py` | `index` ×15, `arg-type` ×8, `attr-defined` ×6 |
85
+ | 45 | `lattice_brain/graph/retrieval_reads.py` | `name-defined` ×30, `attr-defined` ×15 |
86
+ | 46 | `latticeai/models/router.py` | `arg-type` ×26, `assignment` ×7, `attr-defined` ×4 |
87
+ | 57 | `lattice_brain/graph/write_master.py` | `name-defined` ×48, `attr-defined` ×9 |
88
+ | 64 | `lattice_brain/graph/documents.py` | `name-defined` ×45, `attr-defined` ×19 |
89
+ | 71 | `lattice_brain/graph/provenance.py` | `name-defined` ×59, `attr-defined` ×12 |
90
+ | 77 | `lattice_brain/graph/retrieval.py` | `name-defined` ×61, `attr-defined` ×16 |
91
+ | 79 | `lattice_brain/graph/discovery.py` | `name-defined` ×72, `attr-defined` ×7 |
92
+ | 107 | `lattice_brain/graph/retrieval_vector.py` | `name-defined` ×68, `attr-defined` ×39 |
93
+ | 108 | `latticeai/api/workspace.py` | `misc` ×105, `arg-type` ×2, `call-overload` ×1 |
94
+ | 127 | `lattice_brain/graph/discovery_index.py` | `name-defined` ×98, `attr-defined` ×29 |
95
+ | 143 | `lattice_brain/graph/projection.py` | `name-defined` ×122, `attr-defined` ×21 |
96
+ | 177 | `lattice_brain/graph/ingest.py` | `name-defined` ×122, `attr-defined` ×55 |
97
+
98
+ Total outstanding: **1407** errors across **77** modules.
@@ -1,6 +1,6 @@
1
1
  # Lattice AI Onboarding
2
2
 
3
- Current release: **10.2.0 — Load-Bearing Fixes**.
3
+ Current release: **10.3.0 — Measured 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.3.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
@@ -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.3.0 — Measured 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.3.0 — Measured 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.3.0 — Measured 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.3.0"
30
30
 
31
31
  __all__ = [
32
32
  "AgentRuntime",
@@ -18,7 +18,7 @@ import platform
18
18
  import re
19
19
  from datetime import datetime
20
20
  from pathlib import Path
21
- from typing import Any, Dict, List, Optional, Tuple
21
+ from typing import Any, Dict, Iterable, List, Optional, Tuple
22
22
 
23
23
  from ..quiet import quiet
24
24
  from ..utils import now_iso as _now
@@ -518,8 +518,13 @@ class AgentRuntime:
518
518
  if sentence.strip(" -•\t")
519
519
  ]
520
520
  facts = [_compact_text(sentence, limit=180) for sentence in sentences[:4]]
521
- review = getattr(result, "review", None) if isinstance(getattr(result, "review", None), dict) else {}
522
- plan_review = getattr(result, "plan_review", None) if isinstance(getattr(result, "plan_review", None), dict) else {}
521
+ # `or {}` rather than the isinstance ternary: the ternary could still
522
+ # yield None when the attribute was a dict-typed None, and the next
523
+ # line calls .get on both.
524
+ review = getattr(result, "review", None)
525
+ review = review if isinstance(review, dict) else {}
526
+ plan_review = getattr(result, "plan_review", None)
527
+ plan_review = plan_review if isinstance(plan_review, dict) else {}
523
528
  decision_seed = review.get("decision") or review.get("status") or plan_review.get("decision") or getattr(result, "status", "")
524
529
  decisions = [
525
530
  _compact_text(f"Run finished with {decision_seed}", limit=160),
@@ -344,7 +344,7 @@ class HooksRegistry:
344
344
  data.setdefault("overrides", {})
345
345
  return data
346
346
  except (OSError, json.JSONDecodeError, TypeError, ValueError) as exc:
347
- LOGGER.warning("hook registry state is unreadable at %s: %s", self._path, exc)
347
+ LOGGER.warning("hook registry state is unreadable at %s: %s", self.path, exc)
348
348
  return {"custom": [], "overrides": {}}
349
349
 
350
350
  def _save(self) -> None:
@@ -47,7 +47,7 @@ from typing import Any, Callable, Dict, List, Optional
47
47
  from ..utils import now_iso as _now
48
48
  from .contracts import multi_agent_contract
49
49
 
50
- MULTI_AGENT_VERSION = "10.2.0"
50
+ MULTI_AGENT_VERSION = "10.3.0"
51
51
 
52
52
  AGENT_ROLES = ("researcher", "planner", "executor", "reviewer", "release")
53
53
  CORE_PIPELINE = ("planner", "executor", "reviewer")
@@ -1,3 +1,3 @@
1
1
  """Lattice AI - modular server package."""
2
2
 
3
- __version__ = "10.2.0"
3
+ __version__ = "10.3.0"
@@ -31,6 +31,7 @@ from latticeai.runtime.chat_wiring import (
31
31
  from latticeai.runtime.config_runtime import build_config_runtime
32
32
  from latticeai.runtime.context_runtime import build_context_runtime
33
33
  from latticeai.runtime.history_runtime import build_history_query_runtime
34
+ from latticeai.runtime.history_writer import HistoryWriterDeps, write_chat_turn
34
35
  from latticeai.runtime.hooks_runtime import (
35
36
  bind_builtin_hook_runners,
36
37
  bind_trigger_hook_runner,
@@ -258,7 +259,6 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
258
259
  except Exception:
259
260
  keyring = None
260
261
 
261
- from datetime import datetime
262
262
 
263
263
  # ── App-level config — parsed once, in one place (latticeai.core.config) ──────
264
264
  # The module-level names below are kept as a compatibility surface for the rest
@@ -409,6 +409,46 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
409
409
  # free). Legacy chat_history.json is imported once, idempotently, and the
410
410
  # file is left untouched on disk as the import source.
411
411
  CONVERSATIONS = _brain_runtime["CONVERSATIONS"]
412
+
413
+ def save_to_history(
414
+ role: str,
415
+ message: str,
416
+ user_email: Optional[str] = None,
417
+ user_nickname: Optional[str] = None,
418
+ source: Optional[str] = None,
419
+ conversation_id: Optional[str] = None,
420
+ workspace_id: Optional[str] = None,
421
+ ) -> None:
422
+ """Persist one chat turn. Logic lives in runtime/history_writer.py.
423
+
424
+ This stays a closure on purpose: `append_audit_event`,
425
+ `classify_sensitive_message` and `redact_secret_text` are bound further
426
+ down this composition root, and a closure resolves them at call time.
427
+ Hoisting the construction instead would reorder the whole function.
428
+ """
429
+ write_chat_turn(
430
+ role,
431
+ message,
432
+ user_email=user_email,
433
+ user_nickname=user_nickname,
434
+ source=source,
435
+ conversation_id=conversation_id,
436
+ workspace_id=workspace_id,
437
+ deps=HistoryWriterDeps(
438
+ conversations=CONVERSATIONS,
439
+ append_audit_event=append_audit_event,
440
+ classify_sensitive_message=classify_sensitive_message,
441
+ redact_secret_text=redact_secret_text,
442
+ normalize_branding=normalize_branding,
443
+ ingestion_pipeline=INGESTION_PIPELINE,
444
+ ingestion_item_factory=IngestionItem,
445
+ enable_graph=ENABLE_GRAPH,
446
+ knowledge_graph=KNOWLEDGE_GRAPH,
447
+ ),
448
+ )
449
+
450
+
451
+
412
452
  # Hooks registry is constructed here (ahead of the watcher) so folder-watch
413
453
  # reindexes can fire the pre_index/post_index lifecycle hooks. The registry
414
454
  # + watcher pair is assembled behind the hooks_runtime seam.
@@ -534,72 +574,7 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
534
574
 
535
575
  # audit build moved after redact_secret_text is defined (see below)
536
576
 
537
- def save_to_history(
538
- role: str,
539
- message: str,
540
- user_email: Optional[str] = None,
541
- user_nickname: Optional[str] = None,
542
- source: Optional[str] = None,
543
- conversation_id: Optional[str] = None,
544
- workspace_id: Optional[str] = None,
545
- ):
546
- try:
547
- message = redact_secret_text(message)
548
- if role == "assistant":
549
- message = normalize_branding(message)
550
- item = {"role": role, "content": message, "timestamp": datetime.now().isoformat()}
551
- if user_email:
552
- item["user_email"] = user_email
553
- if user_nickname:
554
- item["user_nickname"] = user_nickname
555
- if source:
556
- item["source"] = source
557
- if conversation_id:
558
- item["conversation_id"] = conversation_id
559
- if workspace_id:
560
- item["workspace_id"] = workspace_id
561
- sensitive = classify_sensitive_message(item, -1)
562
- append_audit_event(
563
- "chat_message",
564
- role=role,
565
- user_email=user_email,
566
- user_nickname=user_nickname,
567
- source=source,
568
- conversation_id=conversation_id,
569
- workspace_id=workspace_id,
570
- content_preview=sensitive.get("preview"),
571
- content_chars=len(message or ""),
572
- sensitivity=sensitive.get("sensitivity"),
573
- sensitive_labels=sensitive.get("labels") or [],
574
- )
575
- # v4: conversations are durable episodic memory — unbounded SQLite
576
- # store (the 50-message chat_history.json cap is dead).
577
- CONVERSATIONS.append(item)
578
- try:
579
- if ENABLE_GRAPH and KNOWLEDGE_GRAPH:
580
- # v4: chat messages enter the brain through the unified
581
- # ingestion pipeline (provenance + hook lifecycle), not by
582
- # bypassing it with a direct store call.
583
- INGESTION_PIPELINE.ingest(
584
- IngestionItem(
585
- source_type="chat_message",
586
- text=message,
587
- owner=user_email,
588
- workspace_id=workspace_id,
589
- conversation_id=conversation_id,
590
- metadata={
591
- "role": role,
592
- "user_nickname": user_nickname,
593
- "source": source,
594
- "raw": item,
595
- },
596
- ),
597
- user_email=user_email,
598
- )
599
- except Exception as graph_error:
600
- logging.warning("knowledge graph message ingest failed: %s", graph_error)
601
- except Exception as e:
602
- logging.warning("save_to_history failed: %s", e)
577
+
603
578
 
604
579
  def redact_secret_text(text: str) -> str:
605
580
  return _redact_secret_text(text)
@@ -692,6 +667,7 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
692
667
  def build_sensitivity_report(history: List[Dict]) -> Dict:
693
668
  return _build_sensitivity_report(history)
694
669
 
670
+
695
671
  # ── Admin audit report — delegated to latticeai.core.audit ───────────────────
696
672
  def build_admin_audit_report(users: Dict, audit_events: Optional[List[Dict]] = None) -> Dict:
697
673
  graph_stats = None
@@ -13,7 +13,7 @@ from dataclasses import dataclass
13
13
  from pathlib import Path
14
14
  from typing import Any, Dict, List
15
15
 
16
- LEGACY_COMPATIBILITY_VERSION = "10.2.0"
16
+ LEGACY_COMPATIBILITY_VERSION = "10.3.0"
17
17
 
18
18
 
19
19
  @dataclass(frozen=True)
@@ -10,7 +10,7 @@ from __future__ import annotations
10
10
  from copy import deepcopy
11
11
  from typing import Any, Dict, List, Optional
12
12
 
13
- MARKETPLACE_VERSION = "10.2.0"
13
+ MARKETPLACE_VERSION = "10.3.0"
14
14
  TEMPLATE_KINDS = ("plugin", "workflow", "agent", "ingestion_bridge")
15
15
 
16
16
 
@@ -10,7 +10,7 @@ from __future__ import annotations
10
10
 
11
11
  from typing import Dict
12
12
 
13
- WORKSPACE_OS_VERSION = "10.2.0"
13
+ WORKSPACE_OS_VERSION = "10.3.0"
14
14
 
15
15
  # Workspace types separate single-user Personal workspaces from shared
16
16
  # Organization workspaces. Both keep the same local-first JSON store; the type