ltcai 8.7.0 → 8.8.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 (73) hide show
  1. package/README.md +29 -24
  2. package/docs/CHANGELOG.md +26 -0
  3. package/docs/COMMUNITY_AND_PLUGINS.md +4 -3
  4. package/docs/DEVELOPMENT.md +8 -8
  5. package/docs/LEGACY_COMPATIBILITY.md +33 -14
  6. package/docs/ONBOARDING.md +2 -2
  7. package/docs/TRUST_MODEL.md +1 -1
  8. package/docs/WHY_LATTICE.md +2 -2
  9. package/docs/kg-schema.md +1 -1
  10. package/kg_schema.py +1 -1
  11. package/knowledge_graph.py +2 -2
  12. package/lattice_brain/__init__.py +1 -1
  13. package/lattice_brain/core.py +1 -1
  14. package/lattice_brain/ingestion.py +35 -20
  15. package/lattice_brain/runtime/agent_runtime.py +37 -3
  16. package/lattice_brain/runtime/multi_agent.py +1 -1
  17. package/latticeai/__init__.py +1 -1
  18. package/latticeai/app_factory.py +1 -1
  19. package/latticeai/core/legacy_compatibility.py +36 -25
  20. package/latticeai/core/marketplace.py +1 -1
  21. package/latticeai/core/workspace_os.py +1 -1
  22. package/latticeai/runtime/persistence_runtime.py +1 -1
  23. package/latticeai/runtime/platform_services_runtime.py +1 -1
  24. package/latticeai/services/architecture_readiness.py +2 -2
  25. package/latticeai/services/memory_service.py +38 -7
  26. package/latticeai/services/product_readiness.py +11 -11
  27. package/package.json +1 -1
  28. package/src-tauri/Cargo.lock +1 -1
  29. package/src-tauri/Cargo.toml +1 -1
  30. package/src-tauri/tauri.conf.json +1 -1
  31. package/static/app/asset-manifest.json +11 -11
  32. package/static/app/assets/{Act-_U7mhXir.js → Act-C7K9wsO9.js} +1 -1
  33. package/static/app/assets/{Brain-BxyTHZ21.js → Brain-I1OSzxJu.js} +2 -2
  34. package/static/app/assets/{Capture-DyDKWNh9.js → Capture-B3V4_5Xp.js} +1 -1
  35. package/static/app/assets/{Library-DJ8KioFM.js → Library-Cgj-EF50.js} +1 -1
  36. package/static/app/assets/{System-C0FIb3OO.js → System-D1Lkei3I.js} +1 -1
  37. package/static/app/assets/index--P0ksosz.js +17 -0
  38. package/static/app/assets/index-DCh5AoXt.css +2 -0
  39. package/static/app/assets/{primitives-BywkNS3f.js → primitives-BLqaKk5g.js} +1 -1
  40. package/static/app/assets/{textarea-CQ61Rycp.js → textarea-CVQkN2Tk.js} +1 -1
  41. package/static/app/index.html +2 -2
  42. package/static/sw.js +1 -1
  43. package/lattice_brain/_kg_common.py +0 -11
  44. package/lattice_brain/discovery.py +0 -11
  45. package/lattice_brain/documents.py +0 -11
  46. package/lattice_brain/identity.py +0 -11
  47. package/lattice_brain/ingest.py +0 -11
  48. package/lattice_brain/network.py +0 -11
  49. package/lattice_brain/projection.py +0 -11
  50. package/lattice_brain/provenance.py +0 -11
  51. package/lattice_brain/retrieval.py +0 -11
  52. package/lattice_brain/schema.py +0 -11
  53. package/lattice_brain/store.py +0 -11
  54. package/lattice_brain/write_master.py +0 -11
  55. package/latticeai/brain/__init__.py +0 -36
  56. package/latticeai/brain/_kg_common.py +0 -17
  57. package/latticeai/brain/context.py +0 -17
  58. package/latticeai/brain/conversations.py +0 -17
  59. package/latticeai/brain/discovery.py +0 -17
  60. package/latticeai/brain/documents.py +0 -17
  61. package/latticeai/brain/identity.py +0 -17
  62. package/latticeai/brain/ingest.py +0 -17
  63. package/latticeai/brain/memory.py +0 -17
  64. package/latticeai/brain/network.py +0 -17
  65. package/latticeai/brain/projection.py +0 -17
  66. package/latticeai/brain/provenance.py +0 -17
  67. package/latticeai/brain/retrieval.py +0 -17
  68. package/latticeai/brain/schema.py +0 -17
  69. package/latticeai/brain/store.py +0 -17
  70. package/latticeai/brain/write_master.py +0 -17
  71. package/latticeai/services/agent_runtime.py +0 -11
  72. package/static/app/assets/index-Bh7IIlyY.js +0 -16
  73. package/static/app/assets/index-_M5aCv21.css +0 -2
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Lattice AI
2
2
 
3
- **Lattice AI 8.7.0 is the local-first Digital Brain platform. This release hardens runtime state ownership, refreshes the checked-in release evidence, and synchronizes package, desktop, extension, and documentation metadata to the 8.7.0 line.**
3
+ **Lattice AI 8.8.0 is the local-first Digital Brain platform. This release removes internal-only Brain compatibility layers, strengthens recall proof quality, and keeps package, desktop, extension, static app, and documentation metadata synchronized to the 8.8.0 line.**
4
4
 
5
5
  **Lattice AI는 모델이 바뀌어도 내 지식과 맥락을 보존하는 로컬 우선 AI 브레인입니다.**
6
6
 
@@ -64,10 +64,10 @@ You need Lattice AI when:
64
64
 
65
65
  ## Living Brain Flow
66
66
 
67
- The screenshots below are the latest checked-in visual evidence captures from
68
- the 8.7.0 release. They keep the first-run Brain flow, memory graph, source
69
- capture, model library, system view, admin console, and review center visible as
70
- release gates.
67
+ The screenshots below are the latest checked-in visual evidence captures. They
68
+ keep the first-run Brain flow, memory graph, source capture, model library,
69
+ system view, admin console, and review center visible as release gates while
70
+ 8.8.0 focuses on Brain Core extraction readiness and recall proof hardening.
71
71
 
72
72
  ### 1. Wake Brain
73
73
 
@@ -206,29 +206,33 @@ See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for developer workflow details.
206
206
 
207
207
  ## Current Release
208
208
 
209
- The current release is **8.7.0 — Runtime State Hygiene & Release Evidence Refresh**:
210
-
211
- - Internal model-runtime logic now reads from the typed `ModelRuntimeState`
212
- object instead of the legacy module-global compatibility surface.
213
- - `sync_to_module_globals()` remains available for external shims, but now emits
214
- a `DeprecationWarning` so new code does not rebuild global-state coupling.
215
- - The 8.7.0 release evidence refresh updates screenshots, walkthrough GIF/WebM,
216
- package/runtime/static/Tauri metadata, and current-release documentation.
217
- - The 8.6 desktop Capture reliability and 8.5 ToolRegistry/Config DI hardening
218
- remain active release guarantees.
219
-
220
- Expected artifacts for 8.7.0 release must use exact filenames:
221
-
222
- - `dist/ltcai-8.7.0-py3-none-any.whl`
223
- - `dist/ltcai-8.7.0.tar.gz`
224
- - `ltcai-8.7.0.tgz`
225
- - `dist/ltcai-8.7.0.vsix`
226
- - `src-tauri/target/release/bundle/dmg/Lattice AI_8.7.0_aarch64.dmg`
209
+ The current release is **8.8.0 — Brain Core Extraction & Recall Proof Hardening**:
210
+
211
+ - Internal-only Brain shim layers are removed: pre-graph flat modules,
212
+ the deprecated `latticeai.brain` namespace, and the service-layer
213
+ AgentRuntime alias are tracked as intentional 8.8.0 removals.
214
+ - Root compatibility shims remain managed for external entrypoints while Brain
215
+ Core now has one physical import surface guarded by isolation tests.
216
+ - AgentRuntime rejects unknown roles at the boundary, exposes contract envelopes
217
+ for legacy run rows, and persists clamped retry budgets.
218
+ - Brain Chat adds resumable conversation history, stop/regenerate/copy actions,
219
+ richer ingestion panels, and answer proof explainability with matched terms
220
+ and confidence labels.
221
+ - Memory recall drops zero-evidence noise when lexical evidence exists, so
222
+ citations explain why each source was selected.
223
+
224
+ Expected artifacts for 8.8.0 release must use exact filenames:
225
+
226
+ - `dist/ltcai-8.8.0-py3-none-any.whl`
227
+ - `dist/ltcai-8.8.0.tar.gz`
228
+ - `ltcai-8.8.0.tgz`
229
+ - `dist/ltcai-8.8.0.vsix`
230
+ - `src-tauri/target/release/bundle/dmg/Lattice AI_8.8.0_aarch64.dmg`
227
231
 
228
232
  Do not use wildcard artifact uploads. Package registry publishing remains owner-run.
229
233
 
230
234
  See [docs/ROADMAP_RECOMMENDATIONS.md](docs/ROADMAP_RECOMMENDATIONS.md) for the
231
- strategic roadmap slices applied through 8.7.0 and the follow-up tracks.
235
+ strategic roadmap slices applied through 8.8.0 and the follow-up tracks.
232
236
 
233
237
  ## Known Limitations
234
238
 
@@ -245,6 +249,7 @@ strategic roadmap slices applied through 8.7.0 and the follow-up tracks.
245
249
 
246
250
  | Version | Theme |
247
251
  | --- | --- |
252
+ | 8.8.0 | Brain Core Extraction & Recall Proof Hardening: internal-only Brain shim layers are removed, AgentRuntime run contracts/retry budgets are tighter, Brain Chat gains conversation controls, and citation recall exposes matched evidence |
248
253
  | 8.7.0 | Runtime State Hygiene & Release Evidence Refresh: model-runtime internals prefer typed state over legacy globals, compatibility sync is deprecated, 8.7.0 visual evidence is refreshed, and all release metadata/docs are synchronized |
249
254
  | 8.6.0 | Desktop Capture & Navigation Reliability: native folder selection works from the Tauri localhost app, picker failures surface in Capture, web saving remains one-action, and the Brain shell sidebar/admin flow is CI-covered |
250
255
  | 8.5.0 | Tool Registry Readiness & Config DI: ToolRegistry drift removed, `tz_name` flows through central Config into automation runtimes, and current-release documentation is synchronized |
package/docs/CHANGELOG.md CHANGED
@@ -6,6 +6,32 @@ existed at that release.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [8.8.0] - 2026-07-06
10
+
11
+ ### Added
12
+ - Added Brain Core isolation coverage that keeps `lattice_brain` independent of
13
+ product-package imports.
14
+ - Added recall proof quality gates for matched terms, confidence labels, and
15
+ lexical evidence filtering.
16
+ - Added Brain Chat conversation controls for new/resume/delete, stop,
17
+ regenerate, copy, and richer ingestion progress.
18
+
19
+ ### Changed
20
+ - Removed internal-only Brain shim layers: flat pre-graph modules,
21
+ `latticeai.brain`, and `latticeai.services.agent_runtime`.
22
+ - Updated legacy compatibility reporting so removed shim layers are tracked
23
+ separately from remaining external root shims.
24
+ - Hardened AgentRuntime boundary handling for unknown roles, legacy run
25
+ contracts, and persisted retry budgets.
26
+ - Version bumped to 8.8.0 across Python, npm, VS Code extension, Tauri, static
27
+ metadata, readiness gates, release notes, and current-release documentation.
28
+
29
+ ### Fixed
30
+ - File ingestion now rejects directory paths before dispatching to document
31
+ ingestion.
32
+ - Memory recall filters low-evidence noise when stronger lexical matches are
33
+ present and surfaces explainable citation confidence.
34
+
9
35
  ## [8.7.0] - 2026-07-05
10
36
 
11
37
  ### Added
@@ -1,10 +1,11 @@
1
1
  # Community And Plugins
2
2
 
3
- Current release: **8.7.0 — Runtime State Hygiene & Release Evidence Refresh**.
3
+ Current release: **8.8.0 — Brain Core Extraction & Recall Proof Hardening**.
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
7
- reliability, and 8.7.0 runtime-state hygiene) to a product ecosystem. The
7
+ reliability, 8.7.0 runtime-state hygiene, and 8.8.0 Brain Core extraction
8
+ readiness) to a product ecosystem. The
8
9
  immediate goal is small and practical: make it clear how
9
10
  contributors can extend the Brain without weakening local-first trust,
10
11
  workspace scoping, or release quality.
@@ -36,7 +37,7 @@ Plugins should add one bounded capability at a time:
36
37
  - Compatibility requirements belong in `lattice_version`, and examples should
37
38
  use current host-compatible values.
38
39
 
39
- ## 8.4.0 Ecosystem Tasks
40
+ ## Current Ecosystem Tasks
40
41
 
41
42
  - Keep the hello-world plugin valid and boring.
42
43
  - Expand examples only when they exercise real extension seams.
@@ -1,10 +1,10 @@
1
1
  # Lattice AI Development
2
2
 
3
- Current release: **8.7.0 — Runtime State Hygiene & Release Evidence Refresh**.
3
+ Current release: **8.8.0 — Brain Core Extraction & Recall Proof Hardening**.
4
4
 
5
5
  This document is for contributors working on the local-first Digital Brain
6
6
  codebase. Product positioning and quick start stay in `README.md`; release
7
- history is intentionally limited to 7.0.0-8.7.0 in `docs/CHANGELOG.md` and
7
+ history is intentionally limited to 7.0.0-8.8.0 in `docs/CHANGELOG.md` and
8
8
  `RELEASE.md`.
9
9
 
10
10
  ## Product Contract
@@ -114,10 +114,10 @@ For user-facing, API, runtime, release, or packaging changes, check:
114
114
  Release/publish examples must use exact target-version filenames. Do not
115
115
  document wildcard artifact upload commands.
116
116
 
117
- For 8.7.0 release work, exact artifacts are:
117
+ For 8.8.0 release work, exact artifacts are:
118
118
 
119
- - `dist/ltcai-8.7.0-py3-none-any.whl`
120
- - `dist/ltcai-8.7.0.tar.gz`
121
- - `ltcai-8.7.0.tgz`
122
- - `dist/ltcai-8.7.0.vsix`
123
- - `src-tauri/target/release/bundle/dmg/Lattice AI_8.7.0_aarch64.dmg`
119
+ - `dist/ltcai-8.8.0-py3-none-any.whl`
120
+ - `dist/ltcai-8.8.0.tar.gz`
121
+ - `ltcai-8.8.0.tgz`
122
+ - `dist/ltcai-8.8.0.vsix`
123
+ - `src-tauri/target/release/bundle/dmg/Lattice AI_8.8.0_aarch64.dmg`
@@ -1,21 +1,40 @@
1
1
  # Legacy Compatibility Map
2
2
 
3
- Current release: **8.7.0 — Runtime State Hygiene & Release Evidence Refresh**.
3
+ Current target: **8.8.0 — Internal shim removal & Brain Core extraction prep**.
4
4
 
5
5
  Lattice AI is moving toward a smaller, modular architecture centered on
6
6
  `lattice_brain`, `latticeai.services`, `latticeai.api`, and `latticeai.runtime`.
7
7
  Some root-level modules remain packaged for compatibility with older imports,
8
8
  CLI entrypoints, or extension workflows. Their presence does not define the
9
- current 8.7.0 architecture.
9
+ current architecture.
10
10
 
11
- 8.7.0 also tracks the inner compatibility layers that sit below the root
12
- modules. The managed inventory lives in `latticeai.core.legacy_compatibility`
13
- and groups shims by layer:
11
+ The managed inventory lives in `latticeai.core.legacy_compatibility` and
12
+ groups shims by layer:
14
13
 
15
- - `root`: historical repo-root imports such as `knowledge_graph.py`.
16
- - `brain-flat`: pre-graph-package imports such as `lattice_brain.store`.
17
- - `deprecated-namespace`: older `latticeai.brain.*` package imports.
18
- - `service-alias`: service paths that now alias Brain runtime modules.
14
+ - `root`: historical repo-root imports such as `knowledge_graph.py` — **kept**
15
+ (external entrypoints: `uvicorn server:app`, installed CLI, old scripts).
16
+ - `brain-flat`: pre-graph-package imports such as `lattice_brain.store`
17
+ **removed in 8.8.0** (internal-only).
18
+ - `deprecated-namespace`: older `latticeai.brain.*` package imports —
19
+ **removed in 8.8.0** (internal-only).
20
+ - `service-alias`: `latticeai.services.agent_runtime` —
21
+ **removed in 8.8.0** (internal-only).
22
+
23
+ Removed layers stay listed in `legacy_compatibility.REMOVED_SHIMS` and in the
24
+ `legacy_shim_report()` payload (`removed`, `lingering`), so tooling can tell
25
+ "removed on purpose" apart from "missing by accident".
26
+
27
+ ## Brain Core extraction readiness
28
+
29
+ Removing the internal layers gives `lattice_brain` exactly one import surface
30
+ (its physical module paths). Two structural guards keep it extractable as a
31
+ standalone package:
32
+
33
+ - `tests/unit/test_brain_core_isolation.py` — AST guard that fails if any
34
+ `lattice_brain` module imports `latticeai` (the product imports the Brain,
35
+ never the reverse).
36
+ - `tests/unit/test_legacy_root_shims.py::test_internal_shim_layers_are_gone`
37
+ — fails if a removed shim path becomes importable again.
19
38
 
20
39
  ## Current Policy
21
40
 
@@ -47,13 +66,13 @@ and groups shims by layer:
47
66
  | `setup_wizard.py` | setup and model recommendation services | Compatibility for first-run recommendation calls |
48
67
  | `server.py` | lazy proxy to `latticeai.server_app` / `latticeai.app_factory` | Preserves historical `server.app` imports without import-time construction |
49
68
 
50
- ## Inner Shim Layers
69
+ ## Inner Shim Layers (removed in 8.8.0)
51
70
 
52
- | Legacy layer | Example module | Current home / direction | Why it remains |
71
+ | Legacy layer | Example module | Replacement import | Status |
53
72
  | --- | --- | --- | --- |
54
- | `brain-flat` | `lattice_brain.store`, `lattice_brain.ingest`, `lattice_brain.retrieval` | `lattice_brain.graph.*` | Preserves imports from before graph modules were split into a package |
55
- | `deprecated-namespace` | `latticeai.brain.store`, `latticeai.brain.ingest` | `lattice_brain.graph.*` | Preserves the old application-package Brain namespace |
56
- | `service-alias` | `latticeai.services.agent_runtime` | `lattice_brain.runtime.agent_runtime` | Preserves service-layer imports while runtime ownership sits in Brain Core |
73
+ | `brain-flat` | `lattice_brain.store`, `lattice_brain.ingest`, `lattice_brain.retrieval` | `lattice_brain.graph.*` | Removed internal-only, no supported entrypoint depended on it |
74
+ | `deprecated-namespace` | `latticeai.brain.*` | `lattice_brain.*` | Removed namespace also dropped from `pyproject.toml` packages |
75
+ | `service-alias` | `latticeai.services.agent_runtime` | `lattice_brain.runtime.agent_runtime` | Removed runtime ownership sits in Brain Core |
57
76
 
58
77
  ## Packaging Notes
59
78
 
@@ -1,6 +1,6 @@
1
1
  # Lattice AI Onboarding
2
2
 
3
- Current release: **8.7.0 — Runtime State Hygiene & Release Evidence Refresh**.
3
+ Current release: **8.8.0 — Brain Core Extraction & Recall Proof Hardening**.
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
@@ -32,7 +32,7 @@ read the docs first.
32
32
 
33
33
  ## Release Gate
34
34
 
35
- 8.7.0 treats onboarding as a release gate, not marketing copy. The current
35
+ 8.8.0 treats onboarding as a release gate, not marketing copy. The current
36
36
  machine-checkable product readiness report requires this five-minute contract,
37
37
  the Brain Home surface, setup helpers, graph ingestion tests, and exact release
38
38
  artifact documentation before the release can be called complete.
@@ -1,6 +1,6 @@
1
1
  # Lattice AI Trust Model
2
2
 
3
- Current release: **8.7.0 — Runtime State Hygiene & Release Evidence Refresh**.
3
+ Current release: **8.8.0 — Brain Core Extraction & Recall Proof Hardening**.
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: **8.7.0 — Runtime State Hygiene & Release Evidence Refresh**.
3
+ Current release: **8.8.0 — Brain Core Extraction & Recall Proof Hardening**.
4
4
 
5
5
  **Lattice AI is a local-first Digital Brain that keeps your knowledge durable
6
6
  across any AI model.**
@@ -32,7 +32,7 @@ The graph matters, but it is not the product identity. The product identity is a
32
32
  local-first Digital Brain: a place where the user can talk, add sources, watch
33
33
  memory grow, and inspect the Knowledge Graph when they need proof.
34
34
 
35
- In 8.7.0 the first screen is intentionally not a dashboard. The living Brain,
35
+ In 8.8.0 the first screen is intentionally not a dashboard. The living Brain,
36
36
  conversation composer, evidence-backed Brain Brief, and five-minute onboarding
37
37
  path appear together so the user immediately knows what matters, what to add,
38
38
  and why the answer is grounded in local memory.
package/docs/kg-schema.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Knowledge Graph Schema
2
2
 
3
- Current release: **8.7.0 — Runtime State Hygiene & Release Evidence Refresh**.
3
+ Current release: **8.8.0 — Brain Core Extraction & Recall Proof Hardening**.
4
4
 
5
5
  명세 출처: `lattice_ai_full_spec.pptx` 슬라이드 20·21·22
6
6
  구현: `kg_schema.py`
package/kg_schema.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Compatibility shim for the v4 Knowledge Graph schema."""
2
2
 
3
- from lattice_brain.schema import * # noqa: F403,F401
3
+ from lattice_brain.graph.schema import * # noqa: F403,F401
@@ -4,7 +4,7 @@ The implementation now lives under :mod:`lattice_brain`. Root imports are
4
4
  kept for older integrations and tests.
5
5
  """
6
6
 
7
- from lattice_brain._kg_common import ( # noqa: F401
7
+ from lattice_brain.graph._kg_common import ( # noqa: F401
8
8
  EDGE_VERB,
9
9
  GRAPH_SCHEMA_VERSION,
10
10
  LOCAL_CODE_EXTENSIONS,
@@ -24,7 +24,7 @@ from lattice_brain._kg_common import ( # noqa: F401
24
24
  _slug,
25
25
  set_llm_router,
26
26
  )
27
- from lattice_brain.store import KnowledgeGraphStore
27
+ from lattice_brain.graph.store import KnowledgeGraphStore
28
28
 
29
29
  __all__ = [
30
30
  "KnowledgeGraphStore",
@@ -26,7 +26,7 @@ from .storage import (
26
26
  storage_from_env,
27
27
  )
28
28
 
29
- __version__ = "8.7.0"
29
+ __version__ = "8.8.0"
30
30
 
31
31
  __all__ = [
32
32
  "AgentRuntime",
@@ -40,7 +40,7 @@ class BrainCore:
40
40
  )
41
41
 
42
42
  from .conversations import ConversationStore
43
- from .store import KnowledgeGraphStore
43
+ from .graph.store import KnowledgeGraphStore
44
44
 
45
45
  self.knowledge = KnowledgeGraphStore(
46
46
  self.db_path,
@@ -132,7 +132,7 @@ class IngestionPipeline:
132
132
  # ── public API ───────────────────────────────────────────────────────────
133
133
  def ingest(self, item: IngestionItem, *, user_email: Optional[str] = None) -> IngestionResult:
134
134
  """Normalize, hash, route through dispatch_tool, and record provenance."""
135
- source_type = str(item.source_type or "text").strip()
135
+ source_type = str(item.source_type or "text").strip().lower()
136
136
  if not self.available():
137
137
  return IngestionResult(
138
138
  status="unavailable", source_type=source_type,
@@ -187,25 +187,33 @@ class IngestionPipeline:
187
187
  embedded = bool(self._kg.node_is_embedded(node_id)) if node_id else False
188
188
  title = raw.get("title") or item.title
189
189
 
190
- prov = self._kg.record_provenance(
191
- node_id=node_id,
192
- source_type=source_type,
193
- pipeline=self._pipeline_name,
194
- source_uri=item.source_uri,
195
- content_hash=content_hash,
196
- title=title,
197
- owner=owner,
198
- workspace_id=item.workspace_id,
199
- captured_at=captured_at,
200
- modified_at=item.modified_at,
201
- embedded=embedded,
202
- linked=bool(raw.get("source_node_id")),
203
- duplicate=bool(raw.get("duplicate")),
204
- agent_used=item.agent_used,
205
- chunk_count=len(chunk_ids),
206
- permissions=item.permissions,
207
- metadata=item.metadata,
208
- )
190
+ # Provenance capture must never turn an already-persisted ingest into a
191
+ # caller-visible failure: the graph write above succeeded, so a broken
192
+ # provenance table degrades the result instead of raising.
193
+ provenance_detail: Optional[str] = None
194
+ try:
195
+ prov = self._kg.record_provenance(
196
+ node_id=node_id,
197
+ source_type=source_type,
198
+ pipeline=self._pipeline_name,
199
+ source_uri=item.source_uri,
200
+ content_hash=content_hash,
201
+ title=title,
202
+ owner=owner,
203
+ workspace_id=item.workspace_id,
204
+ captured_at=captured_at,
205
+ modified_at=item.modified_at,
206
+ embedded=embedded,
207
+ linked=bool(raw.get("source_node_id")),
208
+ duplicate=bool(raw.get("duplicate")),
209
+ agent_used=item.agent_used,
210
+ chunk_count=len(chunk_ids),
211
+ permissions=item.permissions,
212
+ metadata=item.metadata,
213
+ )
214
+ except Exception as exc: # noqa: BLE001 — the ingest itself already landed
215
+ prov = {}
216
+ provenance_detail = f"provenance capture failed: {exc}"
209
217
  if self._audit is not None:
210
218
  try:
211
219
  self._audit(
@@ -232,11 +240,16 @@ class IngestionPipeline:
232
240
  embedded=embedded,
233
241
  indexing_status="indexed",
234
242
  provenance_id=prov.get("id"),
243
+ detail=provenance_detail,
235
244
  )
236
245
 
237
246
  # ── routing helpers ──────────────────────────────────────────────────────
238
247
  def _ingest_text(self, item, *, source_type, owner, captured_at) -> Dict[str, Any]:
239
248
  text = item.text or ""
249
+ if not text.strip():
250
+ raise ValueError(
251
+ f"Empty content: {source_type} ingestion requires non-empty text."
252
+ )
240
253
  if len(text.encode("utf-8", "ignore")) > self._max_text_bytes:
241
254
  raise ValueError(
242
255
  f"Text payload exceeds the {self._max_text_bytes // (1024 * 1024)}MB ingestion limit."
@@ -297,6 +310,8 @@ class IngestionPipeline:
297
310
  path = Path(item.path)
298
311
  if not path.exists():
299
312
  raise FileNotFoundError(f"File not found: {path}")
313
+ if path.is_dir():
314
+ raise ValueError(f"File ingestion requires a file, got a directory: {path}")
300
315
  return self._kg.ingest_document(
301
316
  path,
302
317
  original_filename=item.title or path.name,
@@ -42,6 +42,7 @@ from .contracts import (
42
42
  contract_views,
43
43
  extract_contract,
44
44
  multi_agent_contract,
45
+ run_record_contract,
45
46
  runtime_boundary_contract,
46
47
  )
47
48
 
@@ -308,7 +309,7 @@ class AgentRuntime:
308
309
  def get_run(self, run_id: str, *, scope: Optional[str] = None) -> Dict[str, Any]:
309
310
  run = self._store.get_agent_run(run_id, workspace_id=scope)
310
311
  payload = {"run": run}
311
- contract = extract_contract(run)
312
+ contract = self._ensure_contract(run)
312
313
  if contract is not None:
313
314
  payload["contract"] = contract_view(contract)
314
315
  return payload
@@ -324,7 +325,7 @@ class AgentRuntime:
324
325
  def events(self, run_id: str, *, scope: Optional[str] = None) -> Dict[str, Any]:
325
326
  run = self._store.get_agent_run(run_id, workspace_id=scope)
326
327
  status = str(run.get("status") or "")
327
- contract = extract_contract(run)
328
+ contract = self._ensure_contract(run)
328
329
  return {
329
330
  "run_id": run_id,
330
331
  "status": status,
@@ -364,6 +365,35 @@ class AgentRuntime:
364
365
  def _clamp_retries(self, max_retries: int) -> int:
365
366
  return max(0, min(int(max_retries or 0), self._max_retries_cap))
366
367
 
368
+ def _validate_roles(self, roles: Optional[List[str]]) -> Optional[List[str]]:
369
+ """Reject unknown roles at the boundary instead of deep in orchestration.
370
+
371
+ ``preview`` reports unknown roles as a blocking reason; execution paths
372
+ must enforce the same contract so a run can never be recorded with a
373
+ role the runtime does not own.
374
+ """
375
+ if not roles:
376
+ return None
377
+ unknown = [role for role in roles if role not in AGENT_ROLES]
378
+ if unknown:
379
+ raise ValueError(f"unknown roles: {', '.join(unknown)}")
380
+ return list(roles)
381
+
382
+ @staticmethod
383
+ def _ensure_contract(record: Any) -> Optional[Dict[str, Any]]:
384
+ """Return the record's family contract, synthesizing one for legacy rows.
385
+
386
+ Every read surface (get_run/events/replay) must expose the
387
+ ``agent-run-contract/v1`` envelope even for runs persisted before the
388
+ contract family existed, so consumers never need a legacy branch.
389
+ """
390
+ contract = extract_contract(record)
391
+ if contract is not None:
392
+ return contract
393
+ if isinstance(record, dict) and record.get("id"):
394
+ return run_record_contract(record)
395
+ return None
396
+
367
397
  @staticmethod
368
398
  def _result_patch(result: Any, goal: str) -> Dict[str, Any]:
369
399
  return {
@@ -420,6 +450,7 @@ class AgentRuntime:
420
450
  """Create the durable queued row used by the async executor."""
421
451
  if not str(goal or "").strip():
422
452
  raise ValueError("goal is required")
453
+ roles = self._validate_roles(roles)
423
454
  pre_dispatch = self._fire_pre_run(
424
455
  goal=goal,
425
456
  roles=roles,
@@ -454,7 +485,9 @@ class AgentRuntime:
454
485
  execution_mode="async",
455
486
  requested_roles=roles or None,
456
487
  inputs=inputs or {},
457
- max_retries=max_retries,
488
+ # Persist the clamped budget so the durable row reflects what the
489
+ # executor will actually honor, not the raw client request.
490
+ max_retries=self._clamp_retries(max_retries),
458
491
  )
459
492
  payload: Dict[str, Any] = {"run": run}
460
493
  if pre_dispatch is not None:
@@ -574,6 +607,7 @@ class AgentRuntime:
574
607
  ) -> Dict[str, Any]:
575
608
  if not str(goal or "").strip():
576
609
  raise ValueError("goal is required")
610
+ roles = self._validate_roles(roles)
577
611
 
578
612
  pre_dispatch = self._fire_pre_run(
579
613
  goal=goal,
@@ -21,7 +21,7 @@ from typing import Any, Callable, Dict, List, Optional
21
21
  from .contracts import multi_agent_contract
22
22
 
23
23
 
24
- MULTI_AGENT_VERSION = "8.7.0"
24
+ MULTI_AGENT_VERSION = "8.8.0"
25
25
 
26
26
  AGENT_ROLES = ("researcher", "planner", "executor", "reviewer", "release")
27
27
  CORE_PIPELINE = ("planner", "executor", "reviewer")
@@ -1,3 +1,3 @@
1
1
  """Lattice AI - modular server package."""
2
2
 
3
- __version__ = "8.7.0"
3
+ __version__ = "8.8.0"
@@ -94,7 +94,7 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
94
94
  from pydantic import BaseModel
95
95
 
96
96
  from latticeai.models.router import LLMRouter, normalize_branding
97
- from lattice_brain._kg_common import set_llm_router
97
+ from lattice_brain.graph._kg_common import set_llm_router
98
98
  from lattice_brain.graph.schema import set_embed_dim
99
99
  from latticeai.core.security import (
100
100
  hash_password,