ltcai 10.5.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 (101) hide show
  1. package/README.md +62 -45
  2. package/docs/CHANGELOG.md +212 -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/capture_release_evidence.mjs +13 -5
  45. package/scripts/check_current_release_docs.mjs +1 -1
  46. package/scripts/check_frontend_build_freshness.mjs +170 -0
  47. package/src-tauri/Cargo.lock +1 -1
  48. package/src-tauri/Cargo.toml +1 -1
  49. package/src-tauri/tauri.conf.json +1 -1
  50. package/static/app/asset-manifest.json +40 -38
  51. package/static/app/assets/{Act-CLsUUE4V.js → Act-aNud-lKL.js} +1 -1
  52. package/static/app/assets/AdminConsole-QiCTH68K.js +1 -0
  53. package/static/app/assets/Brain-CMFh5q6k.js +321 -0
  54. package/static/app/assets/{BrainHome-DMMhKb8j.js → BrainHome-B4Ar3atu.js} +2 -2
  55. package/static/app/assets/{BrainSignals-CcvYpBGn.js → BrainSignals-RVMlmTGC.js} +1 -1
  56. package/static/app/assets/Capture-DEBo-0vZ.js +1 -0
  57. package/static/app/assets/CommandPalette-bW5WxWWZ.js +1 -0
  58. package/static/app/assets/Library-5gFexm83.js +1 -0
  59. package/static/app/assets/{LivingBrain-BVFU-llN.js → LivingBrain-FlHFfu9i.js} +1 -1
  60. package/static/app/assets/ProductFlow-CFUNDOHu.js +1 -0
  61. package/static/app/assets/{ReviewCard-DODh3z_D.js → ReviewCard-BHj86h2Z.js} +2 -2
  62. package/static/app/assets/System-CeNHoZRu.js +1 -0
  63. package/static/app/assets/{activity-DYlZpTrt.js → activity-7_ZmZqN0.js} +1 -1
  64. package/static/app/assets/arrow-left-Dv2Tiwhe.js +1 -0
  65. package/static/app/assets/{bot-BBmfPYKv.js → bot-D1gX4xks.js} +1 -1
  66. package/static/app/assets/brain-Dn_bDfl4.js +1 -0
  67. package/static/app/assets/{button-DXSLgXUl.js → button-DPFZ9lGw.js} +1 -1
  68. package/static/app/assets/{circle-pause-B8TPRvnt.js → circle-pause-B2P72YOO.js} +1 -1
  69. package/static/app/assets/{circle-play-C5aQnYUv.js → circle-play-BiC8z2vg.js} +1 -1
  70. package/static/app/assets/{cpu-BvOUS0hg.js → cpu-Cx-wRR_V.js} +1 -1
  71. package/static/app/assets/{download-BM2Y4baa.js → download-CKxlzxsK.js} +1 -1
  72. package/static/app/assets/{folder-open-DATq5giq.js → folder-open-Bbjju0tt.js} +1 -1
  73. package/static/app/assets/{hard-drive-D2iumtyP.js → hard-drive-Bon1VBvq.js} +1 -1
  74. package/static/app/assets/index-DYUs0cWy.css +2 -0
  75. package/static/app/assets/index-mLP0-YNO.js +10 -0
  76. package/static/app/assets/{input-Ptpha52f.js → input-DrMc0Xns.js} +1 -1
  77. package/static/app/assets/{permissionCopy-DLSOKW1h.js → permissionCopy-BGUsI7vw.js} +1 -1
  78. package/static/app/assets/{primitives-BvxTiz4R.js → primitives-CEMTjBz1.js} +1 -1
  79. package/static/app/assets/search-HsIji1wY.js +1 -0
  80. package/static/app/assets/share-2-D7THHq5K.js +1 -0
  81. package/static/app/assets/{shield-alert-B6RHxSXi.js → shield-alert-BoU4_8r9.js} +1 -1
  82. package/static/app/assets/{textarea-CCZqVitr.js → textarea-rU1Lb7ia.js} +1 -1
  83. package/static/app/assets/{useFocusTrap-QR9BiUDX.js → useFocusTrap-BgvK4Nkx.js} +1 -1
  84. package/static/app/assets/{useQuery-B42HOaUm.js → useQuery-CT2ChyuU.js} +1 -1
  85. package/static/app/assets/{users-D9PLGmAE.js → users-DlbfHBQV.js} +1 -1
  86. package/static/app/assets/{utils-CAuwfT8i.js → utils-fEGWreKB.js} +1 -1
  87. package/static/app/assets/workspace-ClDBz_0f.js +1 -0
  88. package/static/app/index.html +4 -4
  89. package/static/sw.js +1 -1
  90. package/static/app/assets/AdminConsole-D1PBR5Mk.js +0 -1
  91. package/static/app/assets/Brain-Cnb_YCYH.js +0 -321
  92. package/static/app/assets/Capture-DU1HZWqB.js +0 -1
  93. package/static/app/assets/CommandPalette-tU229JbI.js +0 -1
  94. package/static/app/assets/Library-BeutJd8j.js +0 -1
  95. package/static/app/assets/ProductFlow-CZp8oHh_.js +0 -1
  96. package/static/app/assets/System-CTYfZXfv.js +0 -1
  97. package/static/app/assets/brain-RRpEg40e.js +0 -1
  98. package/static/app/assets/index-B1n87vO-.css +0 -2
  99. package/static/app/assets/index-D10Yd0gh.js +0 -10
  100. package/static/app/assets/search-DtMikKf0.js +0 -1
  101. package/static/app/assets/workspace-DT2v1d3T.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.5.0 Living Brain walkthrough](output/release/v10.5.0/gifs/v10.5.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.5.0/screenshots/04-brain-chat-home.png) | **See how knowledge connects** — a real relationship graph, not a file list ![Memory Graph](output/release/v10.5.0/screenshots/05-memory-graph.png) |
28
- | **Capture anything** — files, whole folders, notes, screenshots, web pages ![Capture](output/release/v10.5.0/screenshots/06-capture.png) | **Automate with review** — agent changes become proposals you approve first ![Review Center](output/release/v10.5.0/screenshots/12-review-center.png) |
29
- | **Pick a model in one click** — recommended local models for your hardware ![Recommended Models](output/release/v10.5.0/screenshots/02-recommended-models.png) | **Stay in control** — audit, roles, retention in a separate admin surface ![Admin Console](output/release/v10.5.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.5.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.5.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,55 +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.5.0/screenshots/01-login.png) | ![Model install](output/release/v10.5.0/screenshots/03-install-load-progress.png) | ![Model library](output/release/v10.5.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.5.0/SCREENSHOT_INDEX.md](output/release/v10.5.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.5.0 — Everyday Words**:
69
-
70
- 10.4.0 named the ground the code stands on. 10.5.0 renames the ground the
71
- *reader* stands on: every screen a first-time user lands on now says what
72
- Lattice is doing in the words that user would use. No feature was removed —
73
- the engine's own vocabulary moved behind the advanced mode switch, where it
74
- belongs to the people who asked for it.
75
-
76
- - **The autonomy dial is a sentence, not a mode name.** 엄격 / 신뢰 / 바이패스
77
- became 먼저 물어보기 / 웬만하면 알아서 / 거의 다 알아서, each with one line
78
- about what it will and will not do on its own. The home dial and Settings
79
- now draw that copy from one module, so the same setting is never named two
80
- ways — and a mode the server adds later still renders, from the server's own
81
- label.
82
- - **The path from a file to a memory is three named steps.** 진행 상황 was
83
- hidden from plain mode and held two raw API payloads. It now shows
84
- 내용 읽기 → 뜻 파악하기 → 기억에 연결하기 as a labelled ordered list, with
85
- each step saying what it does to your file. 파싱 · 임베딩 · 인덱싱 are gone
86
- from that screen.
87
- - **Automations are named the way you named them.** A run shows its workflow's
88
- name rather than a database id, and its state is spoken — `awaiting_approval`
89
- reads as "내 승인 기다리는 중". Unknown states say so instead of printing the
90
- token. Advanced mode still shows the id.
91
- - **A model your machine cannot run says so in one sentence** instead of a
92
- registry line half-translated word by word.
93
- - **The published screenshots were wrong before this release.** Capture ran in
94
- `advanced`, so every README frame showed payload panels, storage engines and
95
- hook logs that no first-run user is ever shown. Captures now run in the app's
96
- real default, `basic` — this page's images are what you will actually see.
97
- - **Guarded, not asserted.** A visual sweep walks ten plain-mode routes and
98
- fails if any of them renders empty or puts engine vocabulary in front of a
99
- reader who never asked for it.
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.
100
114
 
101
115
  Release notes: [RELEASE.md](RELEASE.md) · Full history: [docs/CHANGELOG.md](docs/CHANGELOG.md)
102
116
 
103
- Expected artifacts for 10.5.0 release must use exact filenames:
117
+ Expected artifacts for 10.6.2 release must use exact filenames:
104
118
 
105
- - `dist/ltcai-10.5.0-py3-none-any.whl`
106
- - `dist/ltcai-10.5.0.tar.gz`
107
- - `ltcai-10.5.0.tgz`
108
- - `dist/ltcai-10.5.0.vsix`
109
- - `src-tauri/target/release/bundle/dmg/Lattice AI_10.5.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`
110
124
 
111
125
  Do not use wildcard artifact uploads. Package registry publishing remains owner-run.
112
126
 
@@ -141,6 +155,9 @@ See [ARCHITECTURE.md](ARCHITECTURE.md) for details and
141
155
 
142
156
  | Version | Theme |
143
157
  | --- | --- |
158
+ | 10.6.2 | Ask First |
159
+ | 10.6.1 | First Things |
160
+ | 10.6.0 | Promoted Panels |
144
161
  | 10.5.0 | Everyday Words |
145
162
  | 10.4.0 | Named Ground |
146
163
  | 10.3.0 | Measured Ground |
package/docs/CHANGELOG.md CHANGED
@@ -4,6 +4,218 @@ 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
+
133
+ ## [10.6.0] - 2026-08-03
134
+
135
+ A layout rebuild, not a copy pass. Every main screen used to open as a row of
136
+ equal tabs; each now opens on the panel that answers the question that brought
137
+ the reader there, with everything else moved below it. No feature was removed —
138
+ every panel is still on its page. Backend behaviour is unchanged;
139
+ `frontend/openapi.json` differs from 10.5.0 only in `info.version`.
140
+
141
+ ### Changed
142
+ - **Capture leads with one card instead of four page-level tabs.** 파일 올리기 /
143
+ 폴더 연결하기 / 웹페이지 저장하기 became a `role="group"` method picker inside
144
+ a single `자료 추가하기` station, with the chosen method's form opening in the
145
+ same card. Progress, folder health, connected sources and recent documents
146
+ moved to a two-column `.capture-secondary` row that is always visible instead
147
+ of hidden behind a tab. `#/my-computer`, `#/capture-browser` and `#/pipeline`
148
+ still resolve.
149
+ - **Work opens on 검토함 instead of the empty goal composer.** The shell's 작업
150
+ entry point moved from `#/agents` to `#/review`, the Runs tab leads the tab
151
+ strip, and 검토함 leads 실행 within it. The page heading and description now
152
+ follow the selected tab instead of repeating one sentence on all five.
153
+ - **The model library answers the active model above the tab strip.** A new
154
+ `지금 작동 중인 모델` card names the loaded model in human terms and offers
155
+ one-click switching, listing only models already downloaded, supported and
156
+ loadable — never one that is still awaiting consent. With none loaded it says
157
+ so ("아직 사용할 모델이 없어요").
158
+ - **The Brain home is one bordered station instead of five stacked blocks.**
159
+ Greeting, composer, the ingestion dock and the autonomy dial now live in one
160
+ card that lifts on `:focus-within`; the dock and the dial were two competing
161
+ strips and became one `.brain-station-toolbar` row. The Brain organism shrank
162
+ from 7rem to 5.4rem.
163
+ - **The knowledge graph is a subview, not a third peer tab.** Memory keeps two
164
+ tabs and reaches the graph through a `연결 지도 열기` button, which swaps the
165
+ tab strip for a labelled `기억 화면으로 돌아가기` bar. `#/knowledge-graph`
166
+ still resolves.
167
+ - **Settings' seven flat tabs became three named groups**: 나와 작업공간
168
+ (계정, 작업공간), 내 데이터 보관 (스냅샷, 활동), 동작 방식과 연결 (설정,
169
+ 네트워크, 관리). All tab routes are unchanged.
170
+ - **Everyday and management destinations split.** 대화 · 자료 · 기억 stay in the
171
+ primary nav; 작업 · AI 모델 · 설정 render as topbar quick links at ≥960px and
172
+ inside the menu below that. Both copies are built from one array and shown by
173
+ one breakpoint in `shell.css`, so they cannot drift apart and cannot both
174
+ appear or both vanish.
175
+
176
+ ### Fixed
177
+ - **`#/act/review` never opened the review inbox.** The command palette and the
178
+ daily briefing had always emitted `<screen>/<tab>` hashes, and `parseHash`
179
+ had no case for that shape, so `#/act/review`, `#/act/workflows` and
180
+ `#/brain/graph` all fell through to the Brain home. The generic form is now
181
+ parsed as the last step, after named aliases, so an alias always wins.
182
+ - **The command palette read a private second copy of the destination list.**
183
+ `commandRoutes` in `routes.ts` was not the list the palette rendered, so
184
+ repointing 작업 there changed nothing while the palette kept the old target —
185
+ one label meaning two different screens depending on how it was reached. The
186
+ palette now reads that single list, and its entries are i18n keys rather than
187
+ hardcoded English labels.
188
+ - **The menu's focus trap counted links the browser will not focus.** The menu
189
+ holds a copy of the management links that CSS hides once the topbar shows
190
+ them; those anchors stay in the DOM, so `querySelectorAll` put the trap's
191
+ first/last boundaries on hidden elements, and opening the menu could call
192
+ `focus()` on a `display:none` anchor — which silently does nothing, leaving
193
+ focus on the trigger. Both now filter to elements that are actually rendered.
194
+ - **Both the primary nav and the menu's list were named "화면 이동".** A landmark
195
+ list showed two navigations with the same name and no way to tell them apart.
196
+ The management list is now 관리 and shares the accessible name 관리 화면 이동
197
+ with its topbar twin, which is never visible at the same time.
198
+ - **Release frame 11 shipped a byte-identical copy of frame 06.** With the
199
+ pipeline moved out of its own tab, `#/pipeline` renders the same page as
200
+ `#/capture`, so the full-page capture produced the same file twice while the
201
+ README printed them as different tiles. `scripts/capture_release_evidence.mjs`
202
+ now frames the three-step card itself.
203
+
204
+ ### Testing
205
+ - `tests/visual/v3.spec.js` asserts the new hierarchy: Brain home is one card
206
+ containing composer, dock and dial; the graph is reached by button and not by
207
+ tab; Capture has no page-level tabs and keeps its three deep links; the active
208
+ model card precedes the tab strip; the shell link and the command palette open
209
+ the same screen for 작업.
210
+ - A ten-width sweep (390px–1440px) fails if a management link is visible in both
211
+ places or in neither, if the topbar overflows, or if the menu button is
212
+ clipped. A separate check requires every visible `<nav>` to carry a unique
213
+ accessible name.
214
+ - `tests/visual/mock_server.cjs` now serves `POST /models/load` and
215
+ `GET /knowledge-graph/local/health`, so the one-click model switch and the
216
+ folder-health card render during capture instead of being photographed as
217
+ placeholders.
218
+
7
219
  ## [10.5.0] - 2026-08-03
8
220
 
9
221
  A UI/UX pass for the person who did not build this. No feature was removed;
@@ -1,6 +1,6 @@
1
1
  # Community And Plugins
2
2
 
3
- Current release: **10.5.0 — Everyday Words**.
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.5.0 — Everyday Words**.
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.5.0 — Everyday Words**.
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.5.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.5.0 — Everyday Words**.
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.5.0 — Everyday Words**.
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.5.0 — Everyday Words**.
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.5.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(