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.
- package/README.md +62 -45
- package/docs/CHANGELOG.md +212 -0
- package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/ONBOARDING.md +1 -1
- package/docs/OPERATIONS.md +1 -1
- package/docs/TRUST_MODEL.md +1 -1
- package/docs/WHY_LATTICE.md +1 -1
- package/docs/kg-schema.md +1 -1
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/graph/_kg_contract.py +15 -0
- package/lattice_brain/graph/provenance.py +61 -5
- package/lattice_brain/graph/retrieval.py +16 -4
- package/lattice_brain/graph/retrieval_reads.py +136 -23
- package/lattice_brain/graph/retrieval_vector.py +156 -8
- package/lattice_brain/ingestion.py +14 -1
- package/lattice_brain/ingestion_jobs.py +255 -5
- package/lattice_brain/portability.py +5 -2
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/browser.py +8 -14
- package/latticeai/api/knowledge_graph.py +46 -16
- package/latticeai/api/workspace.py +4 -11
- package/latticeai/api/workspace_scope.py +125 -0
- package/latticeai/core/agent.py +68 -3
- package/latticeai/core/config.py +6 -0
- package/latticeai/core/csrf.py +293 -0
- package/latticeai/core/legacy_compatibility.py +1 -1
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/workspace_os_constants.py +1 -1
- package/latticeai/models/router.py +61 -16
- package/latticeai/runtime/build_phases.py +2 -0
- package/latticeai/runtime/config_runtime.py +2 -0
- package/latticeai/runtime/runtime_context.py +1 -0
- package/latticeai/runtime/web_runtime.py +31 -4
- package/latticeai/services/architecture_readiness.py +1 -1
- package/latticeai/services/product_readiness.py +1 -1
- package/latticeai/services/search_service.py +9 -1
- package/latticeai/services/tool_dispatch.py +13 -7
- package/latticeai/tools/__init__.py +1 -0
- package/latticeai/tools/documents.py +44 -13
- package/package.json +1 -1
- package/scripts/build_frontend_assets.mjs +21 -4
- package/scripts/capture_release_evidence.mjs +13 -5
- package/scripts/check_current_release_docs.mjs +1 -1
- package/scripts/check_frontend_build_freshness.mjs +170 -0
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/tauri.conf.json +1 -1
- package/static/app/asset-manifest.json +40 -38
- package/static/app/assets/{Act-CLsUUE4V.js → Act-aNud-lKL.js} +1 -1
- package/static/app/assets/AdminConsole-QiCTH68K.js +1 -0
- package/static/app/assets/Brain-CMFh5q6k.js +321 -0
- package/static/app/assets/{BrainHome-DMMhKb8j.js → BrainHome-B4Ar3atu.js} +2 -2
- package/static/app/assets/{BrainSignals-CcvYpBGn.js → BrainSignals-RVMlmTGC.js} +1 -1
- package/static/app/assets/Capture-DEBo-0vZ.js +1 -0
- package/static/app/assets/CommandPalette-bW5WxWWZ.js +1 -0
- package/static/app/assets/Library-5gFexm83.js +1 -0
- package/static/app/assets/{LivingBrain-BVFU-llN.js → LivingBrain-FlHFfu9i.js} +1 -1
- package/static/app/assets/ProductFlow-CFUNDOHu.js +1 -0
- package/static/app/assets/{ReviewCard-DODh3z_D.js → ReviewCard-BHj86h2Z.js} +2 -2
- package/static/app/assets/System-CeNHoZRu.js +1 -0
- package/static/app/assets/{activity-DYlZpTrt.js → activity-7_ZmZqN0.js} +1 -1
- package/static/app/assets/arrow-left-Dv2Tiwhe.js +1 -0
- package/static/app/assets/{bot-BBmfPYKv.js → bot-D1gX4xks.js} +1 -1
- package/static/app/assets/brain-Dn_bDfl4.js +1 -0
- package/static/app/assets/{button-DXSLgXUl.js → button-DPFZ9lGw.js} +1 -1
- package/static/app/assets/{circle-pause-B8TPRvnt.js → circle-pause-B2P72YOO.js} +1 -1
- package/static/app/assets/{circle-play-C5aQnYUv.js → circle-play-BiC8z2vg.js} +1 -1
- package/static/app/assets/{cpu-BvOUS0hg.js → cpu-Cx-wRR_V.js} +1 -1
- package/static/app/assets/{download-BM2Y4baa.js → download-CKxlzxsK.js} +1 -1
- package/static/app/assets/{folder-open-DATq5giq.js → folder-open-Bbjju0tt.js} +1 -1
- package/static/app/assets/{hard-drive-D2iumtyP.js → hard-drive-Bon1VBvq.js} +1 -1
- package/static/app/assets/index-DYUs0cWy.css +2 -0
- package/static/app/assets/index-mLP0-YNO.js +10 -0
- package/static/app/assets/{input-Ptpha52f.js → input-DrMc0Xns.js} +1 -1
- package/static/app/assets/{permissionCopy-DLSOKW1h.js → permissionCopy-BGUsI7vw.js} +1 -1
- package/static/app/assets/{primitives-BvxTiz4R.js → primitives-CEMTjBz1.js} +1 -1
- package/static/app/assets/search-HsIji1wY.js +1 -0
- package/static/app/assets/share-2-D7THHq5K.js +1 -0
- package/static/app/assets/{shield-alert-B6RHxSXi.js → shield-alert-BoU4_8r9.js} +1 -1
- package/static/app/assets/{textarea-CCZqVitr.js → textarea-rU1Lb7ia.js} +1 -1
- package/static/app/assets/{useFocusTrap-QR9BiUDX.js → useFocusTrap-BgvK4Nkx.js} +1 -1
- package/static/app/assets/{useQuery-B42HOaUm.js → useQuery-CT2ChyuU.js} +1 -1
- package/static/app/assets/{users-D9PLGmAE.js → users-DlbfHBQV.js} +1 -1
- package/static/app/assets/{utils-CAuwfT8i.js → utils-fEGWreKB.js} +1 -1
- package/static/app/assets/workspace-ClDBz_0f.js +1 -0
- package/static/app/index.html +4 -4
- package/static/sw.js +1 -1
- package/static/app/assets/AdminConsole-D1PBR5Mk.js +0 -1
- package/static/app/assets/Brain-Cnb_YCYH.js +0 -321
- package/static/app/assets/Capture-DU1HZWqB.js +0 -1
- package/static/app/assets/CommandPalette-tU229JbI.js +0 -1
- package/static/app/assets/Library-BeutJd8j.js +0 -1
- package/static/app/assets/ProductFlow-CZp8oHh_.js +0 -1
- package/static/app/assets/System-CTYfZXfv.js +0 -1
- package/static/app/assets/brain-RRpEg40e.js +0 -1
- package/static/app/assets/index-B1n87vO-.css +0 -2
- package/static/app/assets/index-D10Yd0gh.js +0 -10
- package/static/app/assets/search-DtMikKf0.js +0 -1
- package/static/app/assets/workspace-DT2v1d3T.js +0 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
[](https://github.com/TaeSooPark-PTS/LatticeAI/actions/workflows/ci.yml)
|
|
12
12
|
[](LICENSE)
|
|
13
13
|
|
|
14
|
-

|
|
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  | **See how knowledge connects** — a real relationship graph, not a file list  |
|
|
28
|
+
| **Capture anything** — files, whole folders, notes, screenshots, web pages  | **Automate with review** — agent changes become proposals you approve first  |
|
|
29
|
+
| **Pick a model in one click** — recommended local models for your hardware  | **Stay in control** — audit, roles, retention in a separate admin surface  |
|
|
30
|
+
| **Watch a file become memory** — three named steps, not a pipeline diagram  | **Say how much it may do alone** — one dial in plain words; dangerous actions stay blocked either way  |
|
|
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
|
-
|  |  |  |
|
|
62
62
|
|
|
63
63
|
Screenshot index and capture notes:
|
|
64
|
-
[output/release/v10.
|
|
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.
|
|
69
|
-
|
|
70
|
-
10.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
the
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
- **The
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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.
|
|
117
|
+
Expected artifacts for 10.6.2 release must use exact filenames:
|
|
104
118
|
|
|
105
|
-
- `dist/ltcai-10.
|
|
106
|
-
- `dist/ltcai-10.
|
|
107
|
-
- `ltcai-10.
|
|
108
|
-
- `dist/ltcai-10.
|
|
109
|
-
- `src-tauri/target/release/bundle/dmg/Lattice AI_10.
|
|
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.
|
|
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
|
package/docs/DEVELOPMENT.md
CHANGED
|
@@ -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
|
+
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
|
package/docs/ONBOARDING.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Lattice AI Onboarding
|
|
2
2
|
|
|
3
|
-
Current release: **10.
|
|
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
|
package/docs/OPERATIONS.md
CHANGED
package/docs/TRUST_MODEL.md
CHANGED
package/docs/WHY_LATTICE.md
CHANGED
package/docs/kg-schema.md
CHANGED
|
@@ -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 —
|
|
221
|
-
re-
|
|
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
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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(
|