ltcai 10.6.0 → 10.6.2

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 (93) hide show
  1. package/README.md +61 -52
  2. package/docs/CHANGELOG.md +126 -0
  3. package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
  4. package/docs/DEVELOPMENT.md +1 -1
  5. package/docs/ONBOARDING.md +1 -1
  6. package/docs/OPERATIONS.md +1 -1
  7. package/docs/TRUST_MODEL.md +1 -1
  8. package/docs/WHY_LATTICE.md +1 -1
  9. package/docs/kg-schema.md +1 -1
  10. package/lattice_brain/__init__.py +1 -1
  11. package/lattice_brain/graph/_kg_contract.py +15 -0
  12. package/lattice_brain/graph/provenance.py +61 -5
  13. package/lattice_brain/graph/retrieval.py +16 -4
  14. package/lattice_brain/graph/retrieval_reads.py +136 -23
  15. package/lattice_brain/graph/retrieval_vector.py +156 -8
  16. package/lattice_brain/ingestion.py +14 -1
  17. package/lattice_brain/ingestion_jobs.py +255 -5
  18. package/lattice_brain/portability.py +5 -2
  19. package/lattice_brain/runtime/multi_agent.py +1 -1
  20. package/latticeai/__init__.py +1 -1
  21. package/latticeai/api/browser.py +8 -14
  22. package/latticeai/api/knowledge_graph.py +46 -16
  23. package/latticeai/api/workspace.py +4 -11
  24. package/latticeai/api/workspace_scope.py +125 -0
  25. package/latticeai/core/agent.py +68 -3
  26. package/latticeai/core/config.py +6 -0
  27. package/latticeai/core/csrf.py +293 -0
  28. package/latticeai/core/legacy_compatibility.py +1 -1
  29. package/latticeai/core/marketplace.py +1 -1
  30. package/latticeai/core/workspace_os_constants.py +1 -1
  31. package/latticeai/models/router.py +61 -16
  32. package/latticeai/runtime/build_phases.py +2 -0
  33. package/latticeai/runtime/config_runtime.py +2 -0
  34. package/latticeai/runtime/runtime_context.py +1 -0
  35. package/latticeai/runtime/web_runtime.py +31 -4
  36. package/latticeai/services/architecture_readiness.py +1 -1
  37. package/latticeai/services/product_readiness.py +1 -1
  38. package/latticeai/services/search_service.py +9 -1
  39. package/latticeai/services/tool_dispatch.py +13 -7
  40. package/latticeai/tools/__init__.py +1 -0
  41. package/latticeai/tools/documents.py +44 -13
  42. package/package.json +1 -1
  43. package/scripts/build_frontend_assets.mjs +21 -4
  44. package/scripts/check_current_release_docs.mjs +1 -1
  45. package/scripts/check_frontend_build_freshness.mjs +170 -0
  46. package/src-tauri/Cargo.lock +1 -1
  47. package/src-tauri/Cargo.toml +1 -1
  48. package/src-tauri/tauri.conf.json +1 -1
  49. package/static/app/asset-manifest.json +39 -39
  50. package/static/app/assets/{Act-B3MSgNsJ.js → Act-aNud-lKL.js} +1 -1
  51. package/static/app/assets/{AdminConsole-Ds8u36lf.js → AdminConsole-QiCTH68K.js} +1 -1
  52. package/static/app/assets/{Brain-XdHCIB6a.js → Brain-CMFh5q6k.js} +1 -1
  53. package/static/app/assets/{BrainHome-Dp8gzQoF.js → BrainHome-B4Ar3atu.js} +2 -2
  54. package/static/app/assets/{BrainSignals-D4yZflVt.js → BrainSignals-RVMlmTGC.js} +1 -1
  55. package/static/app/assets/{Capture-BwSZmiZ8.js → Capture-DEBo-0vZ.js} +1 -1
  56. package/static/app/assets/{CommandPalette-Ds0DnRSC.js → CommandPalette-bW5WxWWZ.js} +1 -1
  57. package/static/app/assets/{Library-SqzHjyfx.js → Library-5gFexm83.js} +1 -1
  58. package/static/app/assets/{LivingBrain-DpKt-NKE.js → LivingBrain-FlHFfu9i.js} +1 -1
  59. package/static/app/assets/ProductFlow-CFUNDOHu.js +1 -0
  60. package/static/app/assets/{ReviewCard-DVPi1LPZ.js → ReviewCard-BHj86h2Z.js} +2 -2
  61. package/static/app/assets/{System-w-9miIG8.js → System-CeNHoZRu.js} +1 -1
  62. package/static/app/assets/{activity-C0QavXjd.js → activity-7_ZmZqN0.js} +1 -1
  63. package/static/app/assets/arrow-left-Dv2Tiwhe.js +1 -0
  64. package/static/app/assets/{bot-C1-MbzCF.js → bot-D1gX4xks.js} +1 -1
  65. package/static/app/assets/{brain-CeRqzJVc.js → brain-Dn_bDfl4.js} +1 -1
  66. package/static/app/assets/{button-Z-2N8PUb.js → button-DPFZ9lGw.js} +1 -1
  67. package/static/app/assets/{circle-pause-CgjCLdmO.js → circle-pause-B2P72YOO.js} +1 -1
  68. package/static/app/assets/{circle-play-Bj45uClm.js → circle-play-BiC8z2vg.js} +1 -1
  69. package/static/app/assets/{cpu-B-2Chwfy.js → cpu-Cx-wRR_V.js} +1 -1
  70. package/static/app/assets/{download-CKmiOG3C.js → download-CKxlzxsK.js} +1 -1
  71. package/static/app/assets/{folder-open-fE7lLvhZ.js → folder-open-Bbjju0tt.js} +1 -1
  72. package/static/app/assets/{hard-drive-rDl1xsJ1.js → hard-drive-Bon1VBvq.js} +1 -1
  73. package/static/app/assets/index-DYUs0cWy.css +2 -0
  74. package/static/app/assets/{index-AEIqmjwZ.js → index-mLP0-YNO.js} +3 -3
  75. package/static/app/assets/{input-BflJYT5-.js → input-DrMc0Xns.js} +1 -1
  76. package/static/app/assets/{permissionCopy-BNNvkXSX.js → permissionCopy-BGUsI7vw.js} +1 -1
  77. package/static/app/assets/{primitives-CP68OWk2.js → primitives-CEMTjBz1.js} +1 -1
  78. package/static/app/assets/search-HsIji1wY.js +1 -0
  79. package/static/app/assets/{share-2-CyjG2_yY.js → share-2-D7THHq5K.js} +1 -1
  80. package/static/app/assets/{shield-alert-C_JBPWYd.js → shield-alert-BoU4_8r9.js} +1 -1
  81. package/static/app/assets/{textarea-Ds1Exelb.js → textarea-rU1Lb7ia.js} +1 -1
  82. package/static/app/assets/{useFocusTrap-BgIZQ4if.js → useFocusTrap-BgvK4Nkx.js} +1 -1
  83. package/static/app/assets/{useQuery-6Bu27NQe.js → useQuery-CT2ChyuU.js} +1 -1
  84. package/static/app/assets/{users-31lxlciQ.js → users-DlbfHBQV.js} +1 -1
  85. package/static/app/assets/{utils-C0-C5mZc.js → utils-fEGWreKB.js} +1 -1
  86. package/static/app/assets/workspace-ClDBz_0f.js +1 -0
  87. package/static/app/index.html +4 -4
  88. package/static/sw.js +1 -1
  89. package/static/app/assets/ProductFlow-BimeVtWH.js +0 -1
  90. package/static/app/assets/arrow-left-TsAKz-s_.js +0 -1
  91. package/static/app/assets/index-DcMODGjM.css +0 -2
  92. package/static/app/assets/search-B4O4iIgg.js +0 -1
  93. package/static/app/assets/workspace-DAB-urHL.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.6.0 Living Brain walkthrough](output/release/v10.6.0/gifs/v10.6.0-living-brain-walkthrough.gif)
14
+ ![v10.6.2 Living Brain walkthrough](output/release/v10.6.2/gifs/v10.6.2-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,10 +24,10 @@ 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.6.0/screenshots/04-brain-chat-home.png) | **See how knowledge connects** — a real relationship graph, not a file list ![Memory Graph](output/release/v10.6.0/screenshots/05-memory-graph.png) |
28
- | **Capture anything** — files, whole folders, notes, screenshots, web pages ![Capture](output/release/v10.6.0/screenshots/06-capture.png) | **Automate with review** — agent changes become proposals you approve first ![Review Center](output/release/v10.6.0/screenshots/12-review-center.png) |
29
- | **Pick a model in one click** — recommended local models for your hardware ![Recommended Models](output/release/v10.6.0/screenshots/02-recommended-models.png) | **Stay in control** — audit, roles, retention in a separate admin surface ![Admin Console](output/release/v10.6.0/screenshots/10-admin-console.png) |
30
- | **Watch a file become memory** — three named steps, not a pipeline diagram ![Material to memory](output/release/v10.6.0/screenshots/11-knowledge-journey.png) | **Say how much it may do alone** — one dial in plain words; dangerous actions stay blocked either way ![Settings](output/release/v10.6.0/screenshots/08-system.png) |
27
+ | **Chat with a Brain that remembers** — every conversation grows durable, source-linked memory ![Brain Chat](output/release/v10.6.2/screenshots/04-brain-chat-home.png) | **See how knowledge connects** — a real relationship graph, not a file list ![Memory Graph](output/release/v10.6.2/screenshots/05-memory-graph.png) |
28
+ | **Capture anything** — files, whole folders, notes, screenshots, web pages ![Capture](output/release/v10.6.2/screenshots/06-capture.png) | **Automate with review** — agent changes become proposals you approve first ![Review Center](output/release/v10.6.2/screenshots/12-review-center.png) |
29
+ | **Pick a model in one click** — recommended local models for your hardware ![Recommended Models](output/release/v10.6.2/screenshots/02-recommended-models.png) | **Stay in control** — audit, roles, retention in a separate admin surface ![Admin Console](output/release/v10.6.2/screenshots/10-admin-console.png) |
30
+ | **Watch a file become memory** — three named steps, not a pipeline diagram ![Material to memory](output/release/v10.6.2/screenshots/11-knowledge-journey.png) | **Say how much it may do alone** — one dial in plain words; dangerous actions stay blocked either way ![Settings](output/release/v10.6.2/screenshots/08-system.png) |
31
31
 
32
32
  ## Why Lattice AI
33
33
 
@@ -58,62 +58,69 @@ First-run flow — wake the Brain, pick the owner, load a recommended model:
58
58
 
59
59
  | | | |
60
60
  | --- | --- | --- |
61
- | ![Login](output/release/v10.6.0/screenshots/01-login.png) | ![Model install](output/release/v10.6.0/screenshots/03-install-load-progress.png) | ![Model library](output/release/v10.6.0/screenshots/07-model-library.png) |
61
+ | ![Login](output/release/v10.6.2/screenshots/01-login.png) | ![Model install](output/release/v10.6.2/screenshots/03-install-load-progress.png) | ![Model library](output/release/v10.6.2/screenshots/07-model-library.png) |
62
62
 
63
63
  Screenshot index and capture notes:
64
- [output/release/v10.6.0/SCREENSHOT_INDEX.md](output/release/v10.6.0/SCREENSHOT_INDEX.md)
64
+ [output/release/v10.6.2/SCREENSHOT_INDEX.md](output/release/v10.6.2/SCREENSHOT_INDEX.md)
65
65
 
66
66
  ## Current Release
67
67
 
68
- The current release is **10.6.0 — Promoted Panels**:
69
-
70
- 10.5.0 changed the words on each screen. 10.6.0 changes where things sit. Every
71
- main screen used to open as a row of equal tabs, which asks a first-time user to
72
- choose before they know what the choices are. Each screen now opens on the panel
73
- that answers the question that brought you there, and everything else moves
74
- below it. No feature was removed — every panel is still on the page, it just no
75
- longer competes for the top of it.
76
-
77
- - **Each screen leads with one panel instead of a row of equals.** Capture opens
78
- on a single 자료 추가하기 card holding all three ways to add material —
79
- 파일 올리기 / 폴더 연결하기 / 웹페이지 저장하기 — as a choice inside one card
80
- rather than three tabs, with progress and connected folders moved to a quieter
81
- second row. Work opens on 검토함, what is waiting on you, instead of on the
82
- empty goal composer. The model library answers "which model is running, and
83
- can I switch it" in a card above the tabs.
84
- - **The Brain home is one card, not five stacked blocks.** Greeting, composer,
85
- the add-material row and the autonomy dial now sit inside a single bordered
86
- station that lifts when you type anywhere in it. The Brain artwork shrank so
87
- that it introduces the composer rather than headlining the screen.
88
- - **Everyday and management destinations stopped being one list.** 대화 · 자료 ·
89
- 기억 stay in the primary nav; 작업 · AI 모델 · 설정 became topbar links at
90
- desktop widths and fold into the menu below that — built from one array so the
91
- two copies cannot drift, and shown by one breakpoint so they can never both
92
- appear or both vanish.
93
- - **The knowledge graph became a subview rather than a third tab.** Memory opens
94
- on search, with 연결 지도 열기 one button away and a labelled way back.
95
- - **Seven settings tabs became three named groups** — 나와 작업공간 · 내 데이터
96
- 보관 · 동작 방식과 연결 — so the row reads as a short list of decisions.
97
- - **Links that named a screen now open that screen.** `#/act/review` and its
98
- siblings landed on the Brain home for every caller that emitted them — the
99
- command palette, the daily briefing — because nothing parsed the
100
- `<screen>/<tab>` form they had always used. The palette also read a private
101
- second copy of the destination list, so 작업 opened a different screen
102
- depending on whether you clicked it or searched for it. One list, one parser.
103
- - **Guarded, not asserted.** The visual sweep walks ten viewport widths and
104
- fails if a management link is visible in both places at once or in neither, if
105
- a navigation landmark is unnamed or shares its name with another, or if the
106
- topbar overflows.
68
+ The current release is **10.6.2 — Ask First**:
69
+
70
+ 10.6.1 rebuilt five screens, and the Brain home is the one it only half
71
+ rebuilt: the order changed, the shape did not. A large Brain and a centred
72
+ headline still ran down the middle of the screen before the box you type into,
73
+ and the three suggestions still sat between that box and its own toolbar — so
74
+ the alternative to typing wore the same border as typing, and the composer was
75
+ separated from its own controls by an unrelated block. This release splits the
76
+ screen into two surfaces. Nothing was removed; the second choice stopped
77
+ sharing a card with the first.
78
+
79
+ - **The greeting introduces the composer instead of headlining the screen.**
80
+ The Brain and the greeting were a centred column above the input; they are a
81
+ compact banner across the top of the card now — a small Brain beside the title
82
+ and the memory count, on a tinted strip closed by a hairline. The Brain is
83
+ still the button that opens the memory map, and the banner stacks back into a
84
+ centred column on a phone.
85
+ - **One card is the first move, and only the first move.** The station reads
86
+ greeting → the box you type into → one row for 자료 추가 and 혼자 해도 되는 일,
87
+ with nothing between the box and its own controls.
88
+ - **What to try is a card of its own.** The three suggestions moved out of the
89
+ station onto a second surface below it, where they read as an offer rather
90
+ than a step. That card is a named `<section>`, so a screen reader announces it
91
+ as a region — the label used to sit on a plain `<div>`, where the browser
92
+ discards it.
93
+ - **The empty state is designed rather than inherited.** A Brain with nothing to
94
+ suggest yet — every Brain on its first day — shows starter pills instead of
95
+ suggestion cards. That row had never been styled for this screen: it borrowed
96
+ a pill height drawn for live conversations and sat noticeably taller than the
97
+ cards it stands in for. Both branches read as peers now, and a pill fills the
98
+ composer rather than sending a question you did not choose to ask.
99
+ - **Three bugs that only moving things could find.** The Brain's halo is written
100
+ as an inline `box-shadow`, so no stylesheet could shrink it — a 60px glow
101
+ stayed wrapped around a 58px Brain until the blur became a CSS variable the
102
+ host can set (every other screen keeps its old value through the fallback).
103
+ Clipping the card would have hidden the 노트 / 웹 popover and turned the card
104
+ into a scroll container that scrolls the greeting away on focus, so the banner
105
+ clips its own corners instead. And the reduced-motion rule that cancels the
106
+ card lift had to follow the cards to their new selector, or a reader who asked
107
+ the system to hold still would have got the animation back.
108
+ - **Guarded, not asserted.** Unit tests hold the split itself — station, deck
109
+ and footer as three siblings, with the suggestions provably *not* inside the
110
+ station. Browser tests hold what only a browser can see: the popover opens
111
+ unclipped, the cards fill their card, they collapse to chips under 900px and
112
+ stay visible under 760px, reduced motion is honoured, the empty-state pill row
113
+ renders, and the halo is measured against the Brain it surrounds.
107
114
 
108
115
  Release notes: [RELEASE.md](RELEASE.md) · Full history: [docs/CHANGELOG.md](docs/CHANGELOG.md)
109
116
 
110
- Expected artifacts for 10.6.0 release must use exact filenames:
117
+ Expected artifacts for 10.6.2 release must use exact filenames:
111
118
 
112
- - `dist/ltcai-10.6.0-py3-none-any.whl`
113
- - `dist/ltcai-10.6.0.tar.gz`
114
- - `ltcai-10.6.0.tgz`
115
- - `dist/ltcai-10.6.0.vsix`
116
- - `src-tauri/target/release/bundle/dmg/Lattice AI_10.6.0_aarch64.dmg`
119
+ - `dist/ltcai-10.6.2-py3-none-any.whl`
120
+ - `dist/ltcai-10.6.2.tar.gz`
121
+ - `ltcai-10.6.2.tgz`
122
+ - `dist/ltcai-10.6.2.vsix`
123
+ - `src-tauri/target/release/bundle/dmg/Lattice AI_10.6.2_aarch64.dmg`
117
124
 
118
125
  Do not use wildcard artifact uploads. Package registry publishing remains owner-run.
119
126
 
@@ -148,6 +155,8 @@ See [ARCHITECTURE.md](ARCHITECTURE.md) for details and
148
155
 
149
156
  | Version | Theme |
150
157
  | --- | --- |
158
+ | 10.6.2 | Ask First |
159
+ | 10.6.1 | First Things |
151
160
  | 10.6.0 | Promoted Panels |
152
161
  | 10.5.0 | Everyday Words |
153
162
  | 10.4.0 | Named Ground |
package/docs/CHANGELOG.md CHANGED
@@ -4,6 +4,132 @@ 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.6.2] - 2026-08-03
8
+
9
+ The Brain home, re-shaped rather than re-ordered. 10.6.1 gave this screen a new
10
+ order and left its shape alone: a 5.4rem organism and a centred headline ran
11
+ down the middle before the composer, and the suggestion strip sat between the
12
+ composer and its own toolbar. It is two surfaces now — a station holding the
13
+ first move, and a deck below it holding the alternatives. No feature was
14
+ removed. Backend behaviour is unchanged; `frontend/openapi.json` differs from
15
+ 10.6.1 only in `info.version`.
16
+
17
+ ### Changed
18
+ - **The greeting is a banner, not a headline.** `.brain-home-station > .brain-hero`
19
+ is a `row` instead of a `column`: organism 5.4rem → 3.2rem beside the title and
20
+ the memory counts (`.brain-hero-header-text`), on a tinted strip with a
21
+ hairline foot. It stacks back to a centred column under 640px, and the
22
+ short-window rule now shrinks the organism to 2.6rem rather than growing it.
23
+ - **The station holds the first move and nothing else.** Greeting → composer →
24
+ one toolbar (`자료 추가` left, `혼자 해도 되는 일` right) with nothing between
25
+ the composer and its own controls.
26
+ - **The suggestions are their own card.** `.brain-secondary-deck` is a sibling
27
+ of the station under `.brain-centered-home`, which now carries three direct
28
+ children: station, deck, quiet row. The stage widened 44rem → 50rem.
29
+ - **The empty branch is styled for this screen.** With no `suggested_questions`,
30
+ the deck renders `.brain-prompt-pills-row`, scoped and sized here (notably
31
+ `min-height: 0`, against `conversation.css`'s 2.65rem pill floor) so both
32
+ branches read as peers. A pill fills the composer instead of sending.
33
+
34
+ ### Fixed
35
+ - **The Brain's halo could not be scaled by any stylesheet.** `LivingBrain`
36
+ writes the aura's `box-shadow` inline, so a `.brain-hero-organism .brain-aura`
37
+ rule had no effect and a 60px blur stayed around a 58px organism, clipped into
38
+ a flat smudge. The blur reads `var(--aura-blur, 60px)`; the banner sets 14px.
39
+ Every other host is pixel-identical through the fallback.
40
+ - **`aria-label` on a plain `<div>` was being discarded.** The suggestion block
41
+ reached assistive tech unnamed. The label moved to the deck's `<section>`
42
+ (named ⇒ `region`); the inner strip carries none.
43
+ - **Reduced motion stopped covering the suggestion cards.** `affordance.css`'s
44
+ hover-lift and `prefers-reduced-motion` selectors still named
45
+ `.brain-home-prompt-strip > button`, which matches nothing after the grid; they
46
+ are re-pointed at `.brain-prompt-grid button`. The `white-space` opt-in entry
47
+ is deliberately not re-added — `home-simple.css` flips those cards to `nowrap`
48
+ chips under 900px and a matching selector in the last-loaded sheet would win
49
+ that media rule.
50
+ - **The station must not clip.** `overflow: hidden` there would hide the capture
51
+ popover (anchored to the toolbar, opening below it) and make the card a scroll
52
+ container that scrolls the greeting away on focus. The station stays visible;
53
+ the banner uses `overflow: clip` and rounds its own top corners.
54
+
55
+ ### Tests
56
+ - `BrainHome.test.tsx`: station/deck/quiet are direct siblings of the stage in
57
+ order; the station contains neither the strip nor the deck; the deck is a named
58
+ `region` and the inner strip is unlabelled; the starter-pill branch renders.
59
+ - `tests/visual/v3.spec.js`: the popover opens unclipped without scrolling the
60
+ station; the grid fills ≥98% of the deck's inner width; the cards stay visible
61
+ and become chips at 900/760/640/420px; reduced motion drops both `transform`
62
+ and `transition` while hover still answers in colour; the aura's blur is
63
+ measured against the organism and its box against the banner.
64
+
65
+ ## [10.6.1] - 2026-08-03
66
+
67
+ The second half of the layout rebuild. 10.6.0 promoted one panel per main screen
68
+ and left five screens untouched: sign-in, the recommended model, the Brain home,
69
+ the automation runs list, and the review center. Each of those now opens on the
70
+ one thing it exists for, with everything else grouped beneath it. No feature was
71
+ removed. Backend behaviour is unchanged; `frontend/openapi.json` differs from
72
+ 10.6.0 only in `info.version`.
73
+
74
+ ### Changed
75
+ - **Login is one card.** The three-card promise bar moved from between the
76
+ greeting and the form to a quiet hairline strip below it
77
+ (`.ritual-promise.is-quiet`), leaving the form as the only raised surface. The
78
+ password-stays-local note and the existing-Brain note now read after the
79
+ submit button instead of between the last field and it.
80
+ - **The recommendation screen names its top pick once.** The duplicate CTA above
81
+ the list is gone; rank, size, name, reason, time estimate and the action live
82
+ in one `.ritual-primary-hero-card`, with the other two models under a labelled
83
+ `다른 선택지` grid as `.ritual-model-card.is-compact`. The footer splits 뒤로 to
84
+ one end and the hint + 모델 없이 Brain 열기 to the other.
85
+ - **The Brain home leads with the composer.** It gained its own bordered wrapper
86
+ and focus ring; the suggestion strip moved directly under it and became a card
87
+ grid that shows each suggestion's detail line (previously `title`-only); the
88
+ add-material dock and the autonomy dial dropped to the station floor as one
89
+ toolbar; the past-conversation count reads as a badge.
90
+ - **The runs tab stacks by urgency.** `승인함` moved from the bottom to the top
91
+ with an attention treatment (`.data-panel.is-attention`), installed automations
92
+ became visible from this tab with their last run — mode, result, timestamp,
93
+ summary — inside the card, and the agent and workflow run tables moved to the
94
+ end as history.
95
+ - **A review item is evidence beside a decision.** `ReviewCard` splits into a
96
+ 7/5 grid: proposal diff, snooze state, risk/class/tool provenance and the
97
+ technical `<details>` on the left; an always-visible decision panel with
98
+ approve / reject / snooze / run-now and the reject reason on the right. Items
99
+ with no evidence render as a single column instead of an empty left half.
100
+
101
+ ### Accessibility
102
+ - Login: `<label for>` on every field, form named by the page heading,
103
+ `aria-invalid` + `aria-describedby` wired to the error only once it exists.
104
+ - Recommendation: hero card labelled by the model name it recommends.
105
+ - Brain home: greeting is the page `<h1>`; station header and footer scoped so
106
+ they do not register as page landmarks; suggestion strip and station toolbar
107
+ carry accessible names.
108
+ - Review: each item is an `<article>` named by its `<h3>`, the decision block is
109
+ an `<h4>`, the approve/reject cluster is a named `role="group"`, and the status
110
+ and source filters have visible captions referenced by `aria-labelledby`.
111
+
112
+ ### Fixed
113
+ - `p-6` on the Brain home stage added padding on top of children that already
114
+ pad themselves and pushed the quiet shelves under the fixed mobile nav, where
115
+ the tap hit the nav. The project's stylesheets are unlayered and therefore beat
116
+ Tailwind's `@layer utilities` for properties they set — but not for properties
117
+ they leave unset, which is why this one applied.
118
+ `frontend/src/styles/cssLayering.test.ts` asserts the project ships no
119
+ `@layer`, which is what makes that rule hold.
120
+ - The installed-automations empty state pointed at "the suggestions above" from
121
+ a tab that has none above it; `act.installed.empty` now names the 레시피 tab in
122
+ both languages.
123
+
124
+ ### Tests
125
+ - New: `LoginScreen.test.tsx`, `RecommendationScreen.test.tsx`,
126
+ `BrainHome.test.tsx`, `ReviewCard.test.tsx`, `ReviewInbox.test.tsx`,
127
+ `cssLayering.test.ts`; `Act.test.tsx` gained runs-tab ordering coverage.
128
+ - `tests/visual/mock_server.cjs` serves `/api/proposals/counts`, two installed
129
+ automations with last-run detail, and a `change_proposal` review item with a
130
+ real diff — without them the promoted approval block and the new evidence
131
+ column are captured empty.
132
+
7
133
  ## [10.6.0] - 2026-08-03
8
134
 
9
135
  A layout rebuild, not a copy pass. Every main screen used to open as a row of
@@ -1,6 +1,6 @@
1
1
  # Community And Plugins
2
2
 
3
- Current release: **10.6.0 — Promoted Panels**.
3
+ Current release: **10.6.2 — Ask First**.
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.6.0 — Promoted Panels**.
6
+ Current release: **10.6.2 — Ask First**.
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
@@ -1,6 +1,6 @@
1
1
  # Lattice AI Onboarding
2
2
 
3
- Current release: **10.6.0 — Promoted Panels**.
3
+ Current release: **10.6.2 — Ask First**.
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.6.0)
1
+ # Lattice AI — Operations Guide (v10.6.2)
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.6.0 — Promoted Panels**.
3
+ Current release: **10.6.2 — Ask First**.
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.6.0 — Promoted Panels**.
3
+ Current release: **10.6.2 — Ask First**.
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.6.0 — Promoted Panels**.
3
+ Current release: **10.6.2 — Ask First**.
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.6.0"
29
+ __version__ = "10.6.2"
30
30
 
31
31
  __all__ = [
32
32
  "AgentRuntime",
@@ -158,6 +158,21 @@ class KnowledgeGraphCore:
158
158
  ) -> List[str]:
159
159
  raise NotImplementedError
160
160
 
161
+ # ── retrieval_vector.py: derived vector index ────────────────────────────
162
+ def rebuild_vector_index(
163
+ self,
164
+ *,
165
+ full: bool = False,
166
+ include_nodes: bool = True,
167
+ include_chunks: bool = True,
168
+ ) -> Dict[str, Any]:
169
+ """Re-derive the vector index; also records the embedder fingerprint."""
170
+ raise NotImplementedError
171
+
172
+ def vector_freshness(self) -> Dict[str, Any]:
173
+ """``{status, pending_items, total_items, detail}`` — never raises."""
174
+ raise NotImplementedError
175
+
161
176
  # ── retrieval_reads.py: workspace scoping ────────────────────────────────
162
177
  def filter_scoped_nodes(
163
178
  self,
@@ -217,8 +217,10 @@ class KnowledgeGraphProvenanceMixin(_Core):
217
217
  include_legacy_global: bool = False,
218
218
  ) -> Dict[str, Any]:
219
219
  """Raw, lossless logical export of the graph (nodes/edges/chunks/sources/
220
- provenance). Vector embeddings are intentionally omitted — they are
221
- re-derived on import — so the artifact stays portable and small. Use
220
+ provenance). Vector embeddings are intentionally omitted — the importer
221
+ re-derives them with *its own* embedder (:meth:`import_graph_data`
222
+ embeds on write and then reindexes, reporting the result under
223
+ ``index``) — so the artifact stays portable and small. Use
222
224
  :meth:`backup_database` for a faithful binary copy incl. embeddings.
223
225
 
224
226
  ``workspace_id`` REALLY filters (v4): the artifact contains only nodes
@@ -274,15 +276,68 @@ class KnowledgeGraphProvenanceMixin(_Core):
274
276
  data["counts"] = {k: len(v) for k, v in data.items()}
275
277
  return data
276
278
 
279
+ def _reindex_after_import(self) -> Dict[str, Any]:
280
+ """Bring the vector index in line with what was just imported.
281
+
282
+ A logical artifact carries no embeddings (see :meth:`export_graph_data`)
283
+ so they have to be re-derived on this machine. The write door
284
+ (``_upsert_node`` / ``_upsert_chunk``) already embeds inline, which
285
+ makes this pass a *verification* in the common case — but "the index is
286
+ consistent after an import" has to be a guarantee the import makes, not
287
+ an accident of where the embedding call happens to live. It also
288
+ records the embedder fingerprint, which the inline write path never
289
+ does; without it ``index_status()`` reports ``recorded: None`` and the
290
+ ``stale_embedder`` honesty signal is dead after every import.
291
+
292
+ Never raises. The graph rows are already committed at this point, so an
293
+ embedding provider that dies here means "content imported, recall
294
+ degraded" — reported with the repo's existing ``vector_freshness``
295
+ vocabulary (``ready`` / ``pending`` / ``stale_embedder`` /
296
+ ``unavailable``) plus an explicit ``degraded`` flag — never a rollback
297
+ of work that already landed, and never a silent success.
298
+ """
299
+ reindexed = 0
300
+ try:
301
+ outcome = self.rebuild_vector_index(full=False) or {}
302
+ reindexed = int(outcome.get("items_indexed") or 0)
303
+ except Exception as exc: # noqa: BLE001 — the import already committed
304
+ return {
305
+ "status": "unavailable",
306
+ "degraded": True,
307
+ "detail": (
308
+ "imported content is stored, but the vector index could not "
309
+ f"be rebuilt ({exc}); search stays lexical-only for the new "
310
+ "items until a rebuild succeeds"
311
+ ),
312
+ "reindexed_items": 0,
313
+ }
314
+ report = self.vector_freshness() or {}
315
+ status = str(report.get("status") or "unavailable")
316
+ return {
317
+ "status": status,
318
+ "degraded": status != "ready",
319
+ "detail": report.get("detail"),
320
+ "pending_items": int(report.get("pending_items") or 0),
321
+ "total_items": int(report.get("total_items") or 0),
322
+ "reindexed_items": reindexed,
323
+ }
324
+
277
325
  def import_graph_data(
278
326
  self, data: Dict[str, Any], *, mode: str = "merge", dry_run: bool = False
279
327
  ) -> Dict[str, Any]:
280
328
  """Import a logical export back into the store.
281
329
 
282
330
  ``mode='merge'`` upserts on top of existing data (id collisions update);
283
- ``mode='replace'`` clears the graph first. ``dry_run=True`` reports the
284
- plan without writing. Refuses artifacts from a NEWER graph schema than
285
- this build.
331
+ ``mode='replace'`` clears the graph first (including the derived vector
332
+ rows — the re-import re-embeds them inside the same transaction, so a
333
+ failed artifact rolls back to the previous index rather than an empty
334
+ one). ``dry_run=True`` reports the plan without writing. Refuses
335
+ artifacts from a NEWER graph schema than this build.
336
+
337
+ A completed import carries an ``index`` block reporting the state of
338
+ the re-derived vector index (see :meth:`_reindex_after_import`); when
339
+ ``index["degraded"]`` is true, retrieval for the imported scope is
340
+ lexical-only until the index is rebuilt.
286
341
  """
287
342
  nodes = data.get("nodes") or []
288
343
  edges = data.get("edges") or []
@@ -418,6 +473,7 @@ class KnowledgeGraphProvenanceMixin(_Core):
418
473
  ),
419
474
  )
420
475
  plan["imported"] = True
476
+ plan["index"] = self._reindex_after_import()
421
477
  return plan
422
478
 
423
479
  def backup_database(self, dest_path) -> Path:
@@ -454,17 +454,25 @@ class KnowledgeGraphRetrievalMixin(_Core):
454
454
  mode = "hybrid"
455
455
  detail: Optional[str] = None
456
456
  vector_matches: List[Dict[str, Any]] = []
457
+ vector_recall: Optional[Dict[str, Any]] = None
457
458
  vector_fn = getattr(self, "vector_search", None)
458
459
  if not callable(vector_fn):
459
460
  mode = "lexical_only"
460
461
  detail = "vector search is not available on this store"
461
462
  else:
462
463
  try:
463
- vector_matches = list(
464
- (vector_fn(search_query, limit=vec_fetch, min_score=min_vector_score) or {}).get(
465
- "matches", []
466
- )
464
+ vector_payload = (
465
+ vector_fn(search_query, limit=vec_fetch, min_score=min_vector_score)
466
+ or {}
467
467
  )
468
+ vector_matches = list(vector_payload.get("matches", []))
469
+ # Partial recall must reach the caller: the vector channel can
470
+ # only score a capped slice of a large index (see
471
+ # retrieval_vector.vector_search), and a fused answer built on
472
+ # a truncated scan is not the same claim as a complete one.
473
+ recall = vector_payload.get("recall")
474
+ if isinstance(recall, dict) and recall.get("truncated"):
475
+ vector_recall = dict(recall)
468
476
  except Exception as exc: # noqa: BLE001 — degrade, never fail the search
469
477
  mode = "lexical_only"
470
478
  detail = f"vector index unavailable: {exc}"
@@ -619,6 +627,10 @@ class KnowledgeGraphRetrievalMixin(_Core):
619
627
  }
620
628
  if vector_degraded is not None:
621
629
  result["vector_degraded"] = vector_degraded
630
+ if vector_recall is not None:
631
+ result["vector_recall"] = vector_recall
632
+ if vector_degraded is None:
633
+ result["vector_degraded"] = "partial_recall"
622
634
  return result
623
635
 
624
636
  def context_for_query(