ltcai 11.0.1 → 11.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/README.md +55 -43
  2. package/docs/CHANGELOG.md +61 -0
  3. package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
  4. package/docs/DEVELOPMENT.md +1 -1
  5. package/docs/FEATURE_AUDIT_v11.2.0.md +393 -0
  6. package/docs/LAYOUT_REBUILD_SPEC.md +9 -1
  7. package/docs/ONBOARDING.md +1 -1
  8. package/docs/OPERATIONS.md +1 -1
  9. package/docs/PERFORMANCE.md +71 -18
  10. package/docs/TRUST_MODEL.md +1 -1
  11. package/docs/WHY_LATTICE.md +1 -1
  12. package/docs/architecture.md +6 -2
  13. package/docs/kg-schema.md +1 -1
  14. package/lattice_brain/__init__.py +1 -1
  15. package/lattice_brain/embeddings.py +12 -37
  16. package/lattice_brain/gates.py +125 -0
  17. package/lattice_brain/graph/discovery_index.py +30 -32
  18. package/lattice_brain/graph/fusion.py +35 -4
  19. package/lattice_brain/graph/image_vectors.py +230 -0
  20. package/lattice_brain/graph/ingest.py +11 -5
  21. package/lattice_brain/graph/projection.py +66 -8
  22. package/lattice_brain/graph/provenance.py +27 -2
  23. package/lattice_brain/graph/retrieval.py +113 -2
  24. package/lattice_brain/graph/retrieval_docgen.py +6 -6
  25. package/lattice_brain/graph/schema.py +18 -0
  26. package/lattice_brain/graph/store.py +9 -0
  27. package/lattice_brain/graph/vector_index/selector.py +32 -2
  28. package/lattice_brain/ingestion.py +363 -10
  29. package/lattice_brain/multimodal.py +1258 -0
  30. package/lattice_brain/portability.py +169 -32
  31. package/lattice_brain/runtime/multi_agent.py +1 -1
  32. package/lattice_brain/sealed_box.py +244 -0
  33. package/lattice_brain/self_model.py +77 -22
  34. package/lattice_brain/synthesis.py +24 -1
  35. package/latticeai/__init__.py +1 -1
  36. package/latticeai/api/brain_intelligence.py +4 -0
  37. package/latticeai/api/chat.py +11 -0
  38. package/latticeai/api/chat_helpers.py +16 -3
  39. package/latticeai/api/chat_hybrid.py +32 -1
  40. package/latticeai/api/features.py +70 -0
  41. package/latticeai/api/local_files.py +102 -0
  42. package/latticeai/api/memory.py +128 -1
  43. package/latticeai/api/portability.py +39 -4
  44. package/latticeai/api/review_queue.py +126 -0
  45. package/latticeai/api/search.py +16 -2
  46. package/latticeai/core/agent.py +59 -2
  47. package/latticeai/core/agent_prompts.py +66 -0
  48. package/latticeai/core/config.py +4 -1
  49. package/latticeai/core/context_builder.py +98 -11
  50. package/latticeai/core/embedding_providers.py +528 -0
  51. package/latticeai/core/legacy_compatibility.py +1 -1
  52. package/latticeai/core/marketplace.py +1 -1
  53. package/latticeai/core/messages.py +180 -0
  54. package/latticeai/core/model_compat.py +73 -2
  55. package/latticeai/core/workspace_os.py +43 -0
  56. package/latticeai/core/workspace_os_constants.py +1 -1
  57. package/latticeai/core/workspace_reorganization.py +335 -0
  58. package/latticeai/models/model_providers.py +12 -4
  59. package/latticeai/runtime/build_phases.py +33 -2
  60. package/latticeai/runtime/chat_wiring.py +4 -0
  61. package/latticeai/runtime/feature_toggle_wiring.py +163 -0
  62. package/latticeai/runtime/persistence_runtime.py +41 -4
  63. package/latticeai/runtime/router_registration.py +11 -0
  64. package/latticeai/runtime/runtime_context.py +1 -0
  65. package/latticeai/services/app_context.py +8 -0
  66. package/latticeai/services/architecture_readiness.py +1 -1
  67. package/latticeai/services/automation_intelligence.py +22 -2
  68. package/latticeai/services/brain_intelligence.py +123 -7
  69. package/latticeai/services/change_proposals.py +50 -10
  70. package/latticeai/services/command_center.py +10 -4
  71. package/latticeai/services/feature_toggles.py +502 -0
  72. package/latticeai/services/folder_watch.py +122 -1
  73. package/latticeai/services/hybrid_chat.py +56 -5
  74. package/latticeai/services/interop_bridges.py +978 -0
  75. package/latticeai/services/memory_service.py +34 -0
  76. package/latticeai/services/model_capability_registry.py +434 -261
  77. package/latticeai/services/model_catalog.py +95 -61
  78. package/latticeai/services/model_recommendation.py +18 -11
  79. package/latticeai/services/model_runtime.py +1 -1
  80. package/latticeai/services/multimodal_ports.py +112 -0
  81. package/latticeai/services/obsidian_bridge.py +16 -25
  82. package/latticeai/services/product_readiness.py +1 -1
  83. package/latticeai/services/search_service.py +149 -2
  84. package/latticeai/services/self_model_service.py +171 -0
  85. package/latticeai/services/tool_dispatch.py +4 -0
  86. package/latticeai/services/voice_capture.py +27 -1
  87. package/latticeai/setup/auto_setup.py +27 -30
  88. package/latticeai/setup/wizard.py +77 -44
  89. package/package.json +1 -1
  90. package/scripts/check_current_release_docs.mjs +1 -1
  91. package/scripts/check_server_i18n.mjs +1 -0
  92. package/scripts/release_screen_claims.json +22 -0
  93. package/scripts/verify_hf_model_registry.py +253 -218
  94. package/src-tauri/Cargo.lock +1 -1
  95. package/src-tauri/Cargo.toml +1 -1
  96. package/src-tauri/tauri.conf.json +1 -1
  97. package/static/app/asset-manifest.json +37 -37
  98. package/static/app/assets/{Act-D4zSxFR-.js → Act-AWf0SAKp.js} +1 -1
  99. package/static/app/assets/{AdminConsole-w5jBfPt2.js → AdminConsole-D0u8Tiyj.js} +1 -1
  100. package/static/app/assets/{Brain-C2EqQg74.js → Brain-tuhI4sOC.js} +1 -1
  101. package/static/app/assets/BrainHome-Ts7G_Ila.js +2 -0
  102. package/static/app/assets/BrainSignals-jMYgQ2Ar.js +1 -0
  103. package/static/app/assets/{Capture-DPqpGK8d.js → Capture-CqOSzyPr.js} +1 -1
  104. package/static/app/assets/{CommandPalette-CNf7h5fp.js → CommandPalette-DC0Bzh-I.js} +1 -1
  105. package/static/app/assets/{Library-BN0HYOfc.js → Library-CX-bbhmK.js} +1 -1
  106. package/static/app/assets/{LivingBrain-Dfq_wEDI.js → LivingBrain-DBwhto14.js} +1 -1
  107. package/static/app/assets/{ProductFlow-B-3O0rNV.js → ProductFlow-BHA2cfKI.js} +1 -1
  108. package/static/app/assets/{ReviewCard-gZ-tdqFM.js → ReviewCard-BUhCKRNM.js} +1 -1
  109. package/static/app/assets/{System-BElUcSSw.js → System-Bu2t5hn1.js} +1 -1
  110. package/static/app/assets/arrow-left-Dzwa5zRb.js +1 -0
  111. package/static/app/assets/{bot--qYHMtkP.js → bot-Cia42c2h.js} +1 -1
  112. package/static/app/assets/brain-DJMoqrwx.js +1 -0
  113. package/static/app/assets/{button-51Z3rsuv.js → button-2j2Ijzgq.js} +1 -1
  114. package/static/app/assets/{circle-pause-CMIiMaQl.js → circle-pause-BEFeWpVW.js} +1 -1
  115. package/static/app/assets/{circle-play-DZoO_cfG.js → circle-play-ujXMcHxl.js} +1 -1
  116. package/static/app/assets/{cpu-Bs6uc9W9.js → cpu-k4awryFq.js} +1 -1
  117. package/static/app/assets/{download-G-2olkWz.js → download-DFbLJ_ig.js} +1 -1
  118. package/static/app/assets/{folder-open-CTOspnmb.js → folder-open-7y_b6xkM.js} +1 -1
  119. package/static/app/assets/{hard-drive-CewHWJhn.js → hard-drive-Bidh02Kr.js} +1 -1
  120. package/static/app/assets/{index-D7Rr-J2Y.js → index-BpYkzcVm.js} +3 -3
  121. package/static/app/assets/index-DwDl9-8Y.css +2 -0
  122. package/static/app/assets/{input-D4w_BZWl.js → input-DSlJJxRs.js} +1 -1
  123. package/static/app/assets/{permissionCopy-CosBEXAZ.js → permissionCopy-Bpb83Hx9.js} +1 -1
  124. package/static/app/assets/{primitives-d0g9pvzS.js → primitives-BCx6TvfG.js} +1 -1
  125. package/static/app/assets/search-Cgy8cCFJ.js +1 -0
  126. package/static/app/assets/{share-2-NmD7e_oV.js → share-2-BH1M-WNi.js} +1 -1
  127. package/static/app/assets/{shield-alert-CcQeMuju.js → shield-alert-BlKdBXcG.js} +1 -1
  128. package/static/app/assets/{textarea-BPAJDc-0.js → textarea-CCWbUfFB.js} +1 -1
  129. package/static/app/assets/{useFocusTrap-C7YLdTBC.js → useFocusTrap-YdHQ7pJ1.js} +1 -1
  130. package/static/app/assets/{useQuery-DRyD9opW.js → useQuery-CXQiwbVT.js} +1 -1
  131. package/static/app/assets/{utils-DG1_ExrP.js → utils-zqPZJxdx.js} +2 -2
  132. package/static/app/assets/{workspace-CWVf3gsI.js → workspace-DXTihhfU.js} +1 -1
  133. package/static/app/index.html +4 -4
  134. package/static/sw.js +1 -1
  135. package/static/app/assets/BrainHome-CvXS6XiQ.js +0 -2
  136. package/static/app/assets/BrainSignals-DOE_KhOU.js +0 -1
  137. package/static/app/assets/arrow-left-CFNIMjhv.js +0 -1
  138. package/static/app/assets/brain-DDCLjRqO.js +0 -1
  139. package/static/app/assets/index-CkzokZAj.css +0 -2
  140. package/static/app/assets/search-BLCYt75v.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
- ![v11.0.1 Living Brain walkthrough](output/release/v11.0.1/gifs/v11.0.1-living-brain-walkthrough.gif)
14
+ ![v11.2.0 Living Brain walkthrough](output/release/v11.2.0/gifs/v11.2.0-living-brain-walkthrough.gif)
15
15
 
16
16
  Chat, files, folders, notes, and web pages all flow into one durable knowledge
17
17
  graph on your computer. Any model — local MLX or cloud — can speak with that
@@ -24,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/v11.0.1/screenshots/04-brain-chat-home.png) | **See how knowledge connects** — a real relationship graph, not a file list ![Memory Graph](output/release/v11.0.1/screenshots/05-memory-graph.png) |
28
- | **Capture anything** — files, whole folders, notes, screenshots, web pages ![Capture](output/release/v11.0.1/screenshots/06-capture.png) | **Automate with review** — agent changes become proposals you approve first ![Review Center](output/release/v11.0.1/screenshots/12-review-center.png) |
29
- | **Pick a model in one click** — recommended local models for your hardware ![Recommended Models](output/release/v11.0.1/screenshots/02-recommended-models.png) | **Stay in control** — audit, roles, retention in a separate admin surface ![Admin Console](output/release/v11.0.1/screenshots/10-admin-console.png) |
30
- | **Watch a file become memory** — three named steps, not a pipeline diagram ![Material to memory](output/release/v11.0.1/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/v11.0.1/screenshots/08-system.png) |
27
+ | **Chat with a Brain that remembers** — every conversation grows durable, source-linked memory ![Brain Chat](output/release/v11.2.0/screenshots/04-brain-chat-home.png) | **See how knowledge connects** — a real relationship graph, not a file list ![Memory Graph](output/release/v11.2.0/screenshots/05-memory-graph.png) |
28
+ | **Capture anything** — files, whole folders, notes, screenshots, web pages ![Capture](output/release/v11.2.0/screenshots/06-capture.png) | **Automate with review** — agent changes become proposals you approve first ![Review Center](output/release/v11.2.0/screenshots/12-review-center.png) |
29
+ | **Pick a model in one click** — recommended local models for your hardware ![Recommended Models](output/release/v11.2.0/screenshots/02-recommended-models.png) | **Stay in control** — audit, roles, retention in a separate admin surface ![Admin Console](output/release/v11.2.0/screenshots/10-admin-console.png) |
30
+ | **Watch a file become memory** — three named steps, not a pipeline diagram ![Material to memory](output/release/v11.2.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/v11.2.0/screenshots/08-system.png) |
31
31
 
32
32
  ## Why Lattice AI
33
33
 
@@ -58,53 +58,63 @@ First-run flow — wake the Brain, pick the owner, load a recommended model:
58
58
 
59
59
  | | | |
60
60
  | --- | --- | --- |
61
- | ![Login](output/release/v11.0.1/screenshots/01-login.png) | ![Model install](output/release/v11.0.1/screenshots/03-install-load-progress.png) | ![Model library](output/release/v11.0.1/screenshots/07-model-library.png) |
61
+ | ![Login](output/release/v11.2.0/screenshots/01-login.png) | ![Model install](output/release/v11.2.0/screenshots/03-install-load-progress.png) | ![Model library](output/release/v11.2.0/screenshots/07-model-library.png) |
62
62
 
63
63
  Screenshot index and capture notes:
64
- [output/release/v11.0.1/SCREENSHOT_INDEX.md](output/release/v11.0.1/SCREENSHOT_INDEX.md)
64
+ [output/release/v11.2.0/SCREENSHOT_INDEX.md](output/release/v11.2.0/SCREENSHOT_INDEX.md)
65
65
 
66
66
  ## Current Release
67
67
 
68
- The current release is **11.0.1 — Both Branches**:
69
-
70
- 11.0.0 put every line under test and documented, unfixed, the defects that
71
- work surfaced. 11.0.1 is the settling of that account: **all eleven
72
- documented defects are fixed, the code they proved dead is gone, and the CI
73
- floor now holds 100% of branch arcs as well as lines.**
74
-
75
- - **Every 11.0.0 finding is fixed.** The Telegram helper that sent the local
76
- server's auth header to api.telegram.org, the unload-all that fabricated
77
- success, the HTML inspector whose stylesheet collection could never fire,
78
- the review-item id that collided within one second, the vLLM zombie that
79
- read as "already running", the security dashboard's listing/detail
80
- redaction gaps and invalid-JSON exports, the embedding `model_id` frozen at
81
- the wrong dimension, the fast-path model-name mismatch, and the workspace
82
- routes' dead literal and unreachable 404 arms. Each fix flipped the test
83
- that had pinned the broken behaviour and gained a regression test.
84
- - **Branch coverage joined the floor.** `branch = true` is now in the
85
- coverage config: both directions of all 9,828 conditionals execute under
86
- the suite (5,798 tests, up from 5,426), and `fail_under = 100` fails CI on
87
- a single missed arc. Exclusions stay honest: eight reasoned
88
- `pragma: no cover` lines and two reasoned `pragma: no branch` lines.
89
- - **Dead code left the tree.** The unused auto-approved write-policy factory,
90
- an unused snapshot-import normalizer, the workspace 404 arms shadowed by
91
- the anti-enumeration 403, a provably-dead version guard, a provably-dead
92
- MLX condition, and the vLLM silent-success recheck — each removal proved by
93
- AST/reference scan before deletion.
94
- - **Verified where CI runs.** The full suite passes with the branch gate on
95
- macOS 3.14, a fresh-resolve python 3.11 environment (fastapi 0.141), and a
96
- clean linux python:3.14 container — the three environments that caught
97
- 11.0.0's release-day failures.
68
+ The current release is **11.2.0 — All Systems On**:
69
+
70
+ 11.1.0 built the intelligence layer; 11.2.0 makes sure **every part of it
71
+ actually works, ships today's models, and puts the switches in your hand.**
72
+
73
+ - **The model catalog is current and verified.** Every recommended model
74
+ was checked against the live Hugging Face API — no weights downloaded, no
75
+ model loaded, nothing run on your machine. Two models that had vanished
76
+ from the Hub (phi-3.5-vision, moondream2), four gated repos and three
77
+ obsolete generations left the catalog; the ten recommendations are all
78
+ 2025–2026 builds (Gemma 4 line, Qwen3.5/3.6, gpt-oss-20b, LFM2.5 with
79
+ Korean support), RAM-tiered, with exact-case ids, real byte sizes (one
80
+ entry claimed 11.8 GB for a 61 GB repo) and a static-loadability verdict
81
+ whose limits are stated, not hidden. Already-downloaded older models stay
82
+ recognised so nobody's local weights become orphans.
83
+ - **Every switch is yours now.** A new 기능 drawer on the Brain home's dock
84
+ lists every opt-in capability — multimodal memories, video, brain
85
+ network sharing, vault watch, photo semantic search, RRF fusion, graph
86
+ expansion, automatic synthesis, background indexing, and the vector
87
+ backend choice — each with a plain-language line, its current source
88
+ (default / env / you), and a live switch. Toggles apply at runtime
89
+ through injected gate seams; an uninstalled backend says "install
90
+ needed" instead of pretending.
91
+ - **The scope-outs are in.** Notion-export, Git-history and mail/calendar
92
+ bridges (all through the single ingestion gate with approval and
93
+ dry-run), recipient public-key encryption for subgraph shares (X25519
94
+ sealed box; passphrase mode stays), video ingestion via guarded ffmpeg
95
+ keyframes with subtitle companions, vault watch mode, bulk review
96
+ approval, the Self-Model summary reaching the agent loop, and text→image
97
+ semantic photo search.
98
+ - **A 58-row evidence audit** verified every documented feature against
99
+ the running app: 51 worked as documented; the rest were fixed (a
100
+ permanently blank briefing health section, cloud memory write-back that
101
+ never reached the Review Center, an empty Brain scoring 100/excellent)
102
+ or the docs were lowered to the truth. The audit ships as
103
+ [docs/FEATURE_AUDIT_v11.2.0.md](docs/FEATURE_AUDIT_v11.2.0.md).
104
+
105
+ All of it lands with the floor intact: **6,490 tests, 100.00% of 39,054
106
+ statements and 11,014 branches**, verified on macOS 3.14, a fresh-resolve
107
+ python 3.11 environment, and a clean linux python:3.14 container.
98
108
 
99
109
  Release notes: [RELEASE.md](RELEASE.md) · Full history: [docs/CHANGELOG.md](docs/CHANGELOG.md)
100
110
 
101
- Expected artifacts for 11.0.1 release must use exact filenames:
111
+ Expected artifacts for 11.2.0 release must use exact filenames:
102
112
 
103
- - `dist/ltcai-11.0.1-py3-none-any.whl`
104
- - `dist/ltcai-11.0.1.tar.gz`
105
- - `ltcai-11.0.1.tgz`
106
- - `dist/ltcai-11.0.1.vsix`
107
- - `src-tauri/target/release/bundle/dmg/Lattice AI_11.0.1_aarch64.dmg`
113
+ - `dist/ltcai-11.2.0-py3-none-any.whl`
114
+ - `dist/ltcai-11.2.0.tar.gz`
115
+ - `ltcai-11.2.0.tgz`
116
+ - `dist/ltcai-11.2.0.vsix`
117
+ - `src-tauri/target/release/bundle/dmg/Lattice AI_11.2.0_aarch64.dmg`
108
118
 
109
119
  Do not use wildcard artifact uploads. Package registry publishing remains owner-run.
110
120
 
@@ -139,6 +149,8 @@ See [ARCHITECTURE.md](ARCHITECTURE.md) for details and
139
149
 
140
150
  | Version | Theme |
141
151
  | --- | --- |
152
+ | 11.2.0 | All Systems On |
153
+ | 11.1.0 | Product Intelligence |
142
154
  | 11.0.1 | Both Branches |
143
155
  | 11.0.0 | Full Measure |
144
156
  | 10.10.0 | Quiet Station |
package/docs/CHANGELOG.md CHANGED
@@ -4,6 +4,67 @@ 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
+ ## [11.2.0] - 2026-08-11 — All Systems On
8
+
9
+ ### Changed
10
+ - 모델 카탈로그 전수 최신화: HF API 무부하 검증(가중치 다운로드 0, 실로드
11
+ 0) 후 추천 10종을 2025–2026 세대(Gemma 4 · Qwen3.5/3.6 · gpt-oss-20b ·
12
+ LFM2.5)로 재구성. Hub 소멸 2종·gated 3종·vllm 전용·구세대 제거(완전
13
+ 삭제), 기존 다운로드 모델은 인식 전용 목록으로 보호. 검증기에서
14
+ `--deep`/`--test-load` 제거(무부하 원칙 구조화). 크기 오기(11.8→61.1GB)
15
+ 등 부수 결함 8건 수정.
16
+ - 빈 Brain이 가용 1차원만으로 100/excellent를 받던 건강 채점을 정직화
17
+ (측정 불가 차원은 사유와 함께 unavailable, 전무 시 등급 없음).
18
+
19
+ ### Added
20
+ - Brain 홈 dock **기능 서랍**: opt-in 10종(멀티모달·비디오·브레인 네트워크
21
+ ·볼트 감시·사진 의미 검색·RRF·이웃 확장·자동 합성·배경 인덱싱·벡터
22
+ 백엔드)의 서버 카탈로그 라이브 토글 — `GET/POST /api/features`,
23
+ FeatureGate 시임으로 재시작 없이 반영, 사용자>env>기본.
24
+ - Interop 브릿지: Notion export · Git 히스토리 · 메일(.eml)/캘린더(.ics)
25
+ — 단일 인제스트 게이트+승인+dry_run(`/api/ingestion/interop`).
26
+ - 서브그래프 공유 수신자 공개키 암호화(X25519 sealed box, ephemeral 키)
27
+ + `GET /api/knowledge-graph/share/recipient-key`.
28
+ - 비디오 인제스트(ffmpeg 가드 키프레임 + .srt/.vtt, NodeType.VIDEO),
29
+ 볼트 감시 모드, 일괄 리뷰 승인/기각, Self-Model 요약의 에이전트 루프
30
+ 도달, 텍스트→이미지 의미 검색(`/api/search/image-query`).
31
+ - 58행 증거 기반 기능 감사 문서 docs/FEATURE_AUDIT_v11.2.0.md.
32
+
33
+ ### Fixed
34
+ - 오늘의 브리핑 건강 섹션 영구 공백(스키마 불일치), 클라우드 유래 지식이
35
+ 리뷰 센터에 도달하지 않던 write-back 미배선(+auto_commit 정책 연결),
36
+ vector-freshness 세분화 미노출, 멀티모달 context_quality 신호 미배선,
37
+ `kgv2_edges`가 canonical 엣지 타입을 ''로 읽던 뷰(`COALESCE(NULLIF)`),
38
+ 죽은 자동화 저신뢰 게이트, 사어 grok id(→grok-4.5).
39
+
40
+ ## [11.1.0] - 2026-08-10 — Product Intelligence Layer
41
+
42
+ ### Added
43
+ - 플러그형 벡터 인덱스 레이어(`lattice_brain/graph/vector_index/`):
44
+ BruteForce 기본, int8 Quantized·HNSW(`ltcai[hnsw]`) 옵트인, 영속 배경
45
+ 임베딩 큐(`vector_jobs`), `vector_freshness_breakdown()`, RRF 융합·이웃
46
+ 후보 확장 옵션. 하이브리드 p50 10k 299ms → 10.1ms, 50k 43.9ms
47
+ (recall@10 0.987, docs/PERFORMANCE.md).
48
+ - Temporal 지식 모델: `valid_from`/`valid_to`/`superseded_by`(멱등 제자리
49
+ 승급, NULL 규약) + `as_of(timestamp)` 슬라이스. 모순 감지 → 리뷰 제안
50
+ → 승인 시 temporal 스탬프. 이벤트 기반 합성(상위 개념·누락 엣지·
51
+ proactive Brief 제안, 25개 인제스트마다, 격리 배선) + 중요도/정리 제안.
52
+ 새 API: `/api/brain/proactive-brief|importance|synthesize|contradictions/*`.
53
+ - 멀티모달 1등 시민(`allow_multimodal`, 기본 꺼짐 — 꺼짐=바이트 동일):
54
+ Image/Audio 1등 노드, OCR·실캡션만(`caption_status`), 별도 이미지 벡터
55
+ 공간+late fusion, Evidence 인라인 썸네일(승인 게이트 비우회), 비디오는
56
+ 정직 거부.
57
+ - Self-Model 서브그래프(제안-우선 생성, 사용자 직접 소유), 컨텍스트
58
+ 예산 주입, 삭제 불가능 구조의 폴더 재구성 제안,
59
+ `/api/memory/self-model*` 5종.
60
+ - Obsidian vault 브릿지(`POST /api/ingestion/obsidian`, 승인 게이트,
61
+ 위키링크→엣지, 멱등) + 서명·암호화 선택적 서브그래프 공유 프로토타입
62
+ (`LATTICEAI_BRAIN_NETWORK` 기본 꺼짐, 수신=리뷰 제안).
63
+
64
+ ### Removed
65
+ - `VisionStub`/`get_vision_embedder` — 파일명으로 캡션을 합성하고 그
66
+ 문자열의 해시를 이미지 임베딩으로 저장하던 경로(정직성 위반) 삭제.
67
+
7
68
  ## [11.0.1] - 2026-08-10
8
69
 
9
70
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  # Community And Plugins
2
2
 
3
- Current release: **11.0.1 — Both Branches**.
3
+ Current release: **11.2.0 — All Systems On**.
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: **11.0.1 — Both Branches**.
6
+ Current release: **11.2.0 — All Systems On**.
7
7
 
8
8
  This document is for contributors working on the local-first Digital Brain
9
9
  codebase. Product positioning and quick start stay in `README.md`; release
@@ -0,0 +1,393 @@
1
+ # Feature Audit — v11.2.0
2
+
3
+ > **Status: reference** — a point-in-time, evidence-based sweep of every row in
4
+ > [`FEATURE_STATUS.md`](../FEATURE_STATUS.md). It records what was true when it
5
+ > was run, not what the current release claims; the canonical feature state
6
+ > stays in `FEATURE_STATUS.md`.
7
+
8
+ Audited against the `release/v11.2.0` working tree on 2026-08-10, starting from
9
+ the 11.1.0 feature table. Continues the evidence-based audit tradition started
10
+ in 3.3.0: **every row gets a verdict and a way to reproduce it**, and a row
11
+ whose sentence outruns its code is lowered rather than defended.
12
+
13
+ ## Method
14
+
15
+ Claims were checked by running the product, not by reading about it.
16
+
17
+ * **Hermetic full-app boot.** `latticeai.app_factory.build_runtime()` inside
18
+ `scripts/export_openapi.py`'s `isolated_runtime_environment`, which points
19
+ `HOME`, `LATTICEAI_DATA_DIR`, `LATTICEAI_BRAIN_DIR`, the agent workspace, the
20
+ vault dir and the static dir at a throwaway temp tree. No real HOME, no real
21
+ network, no live server. Endpoints were exercised through `TestClient`.
22
+ * **Three passes.** (1) anonymous read surface, (2) corrected paths with
23
+ redirect-following off plus API introspection, (3) a registered-and-signed-in
24
+ session driving the local-file approval dance end to end.
25
+ * **Route inventory** from the app's own route table and its generated OpenAPI
26
+ schema, cross-checked against the checked-in `frontend/openapi.json` that
27
+ `scripts/check_openapi_drift.mjs` already proves is regenerated from the live
28
+ app.
29
+ * **Code-level verification** for claims an endpoint cannot show (constants,
30
+ defaults, guard order, which collaborator writes which key).
31
+
32
+ Where a claim is pinned by an existing regression test, that test is the
33
+ evidence: it is cheaper to keep true than a paragraph.
34
+
35
+ ## Cross-cutting smoke
36
+
37
+ Measured on the hermetic boot described above.
38
+
39
+ | Measure | Value |
40
+ | --- | --- |
41
+ | Routers registered (`include_router` calls, all distinct) | 35 |
42
+ | Total routes on the app | 451 |
43
+ | Of those, API routes | 446 |
44
+ | Non-API routes | 4 (`/openapi.json`, `/docs`, `/docs/oauth2-redirect`, `/redoc`) |
45
+ | Static mounts | `/static` always; `/icons` only when `static/icons/` exists (it does in the repo; it did not in the sandboxed static dir) |
46
+ | OpenAPI paths | 404 |
47
+ | OpenAPI operations | 445 |
48
+ | `frontend/openapi.json` drift | clean (`node scripts/check_openapi_drift.mjs`) |
49
+ | Health snapshot | `status: ok`, `version 11.1.0`, `mode local`, `graph_enabled true`, `telegram_enabled false`, `autoload_models false` |
50
+ | Probes run | 70 (pass 1) + 42 (pass 2) + the signed-in pass; **zero 5xx, zero unhandled exceptions** |
51
+
52
+ Every non-200 in the sweep was a *designed* refusal, and each named its
53
+ reason: `403` on the share routes while `LATTICEAI_BRAIN_NETWORK` is off,
54
+ `401` on local-file routes without a session, `405` on a POST-only path,
55
+ `422` on a malformed body.
56
+
57
+ ### Frontend ↔ real routes
58
+
59
+ 320 server-path literals were extracted from `frontend/src`. **Every one
60
+ resolves to a real route.** Ten looked missing on a first pass and all ten are
61
+ template routes the client builds with an interpolated id (`/models/switch` →
62
+ `/models/switch/{model_id:path}`, `/mcp/connectors` → `/mcp/connectors/{mcp_id}`,
63
+ and so on); the eleventh, `/workspace-admin`, is a client-side hash route, not a
64
+ server path.
65
+
66
+ ### mock_server ↔ real routes
67
+
68
+ `tests/visual/mock_server.cjs` serves the twelve release-capture screens.
69
+
70
+ | Measure | Value |
71
+ | --- | --- |
72
+ | Exact path literals in the mock | 132 |
73
+ | Matching a real route (exact or template) | 131 |
74
+ | Mock-only | **1** — `/v3`, a legacy alias for `/app` that the server no longer serves |
75
+ | Prefix handlers in the mock | 17, all resolving to real routes or mounts |
76
+ | Real routes the frontend can call that the mock does not serve | 183 |
77
+
78
+ The 183 are capture-fidelity headroom, not product defects: the mock answers
79
+ what the twelve screens need and falls through for the rest. The gap is bounded
80
+ by a real gate — `tests/visual/v3.spec.js` asserts
81
+ `service-unavailable-banner` count is zero across all ten plain-mode routes, so
82
+ a mock gap that reaches a core panel fails the visual suite rather than shipping
83
+ a screenshot of a working feature rendered as broken.
84
+
85
+ ## Verdict table
86
+
87
+ `WORKS` — the claim was exercised and held. `PARTIAL` — the mechanism exists
88
+ and is tested, but a surface the sentence implies does not reach it.
89
+ `MISMATCH` — the doc said something the code does not do.
90
+
91
+ | # | Row | Verdict | Evidence |
92
+ | --- | --- | --- | --- |
93
+ | 1 | Brain Home | **MISMATCH → doc fixed** | Living Brain + composer in-viewport asserted at 1280×800 and 390×780 (`tests/visual/v3.spec.js`); the Brain Brief renders only inside `BrainHomeDock`'s `stats` tab, and `BrainHome.test.tsx` asserts the dock opens closed |
94
+ | 2 | Automation Intelligence | WORKS | `GET /api/automation/{overview,patterns,suggestions}` → 200 with `consent.default_state: "draft_disabled"`, `requires_user_enable: true`, `review_before_run: true` |
95
+ | 3 | Brain Intelligence | **PARTIAL → code fixed** | `GET /api/brain/health` → 200 with all four dimensions; on an empty Brain three read `unavailable` yet the composite reported `overall_score: 100, grade: "excellent"` (see Finding 3) |
96
+ | 4 | Temporal Knowledge | WORKS | `KnowledgeGraphStore.as_of(timestamp, …)` and `neighbors(…, as_of=None)` present with the documented defaults; `tests/unit/test_t2_temporal_schema.py`, `test_t2_temporal_reads.py`, `test_kg_temporal.py` |
97
+ | 5 | Proactive Synthesis | WORKS | `POST /api/brain/synthesize` → 200 with the four proposal kinds; `SYNTHESIS_THRESHOLD_ENV` / `DEFAULT_SYNTHESIS_THRESHOLD = 25`; `tests/unit/test_t2_synthesis_job.py` |
98
+ | 6 | Memory Decay | WORKS | `GET /api/brain/importance` → 200 (`half_life_days: 30.0`, `access_source: "metadata"`); `/api/brain/quality-report` carries `importance` + `tidying`; `tests/unit/test_t2_importance_decay.py` |
99
+ | 7 | Hybrid Recall | WORKS | `POST /api/memory/recall` → 200 with `quality_gate.gate == "hybrid-evidence/v2"`; `GET /api/search/hybrid` reports per-channel weights and recall detail |
100
+ | 8 | Folder Ingestion | WORKS | `GET /api/ingestion/jobs` → 200; `/api/ingestion/folder`, `/jobs/{id}`, `/jobs/{id}/resume` present |
101
+ | 9 | Extraction Quality | WORKS | `assess_extraction_quality` + observe-mode `_observe_quality_gate` (every failure path returns `None` and the ingest proceeds); `tests/unit/test_ingestion_quality_jobs.py` |
102
+ | 10 | Vector Freshness | **PARTIAL → code fixed** | `GET /api/brain/vector-freshness` returned only `{status, pending_items, total_items, detail}` while `vector_freshness_breakdown()` had **no product caller** (see Finding 4) |
103
+ | 11 | Vector Index Backends | WORKS | `resolve_vector_index({})` → `brute`/exact; an unknown name falls back **and says why** in `detail`; choices are exactly `brute`/`quantized`/`hnsw` |
104
+ | 12 | Background Embedding | WORKS | `IngestionPipeline.drain_vector_queue` present; the Known Limitation that nothing schedules it is accurate |
105
+ | 13 | Fusion Strategy | WORKS (off) | `LATTICEAI_FUSION_STRATEGY` unset in a clean boot; alpha fusion is what `/api/search/hybrid` reports |
106
+ | 14 | Graph Candidate Expansion | WORKS (off) | `graph_expansion_enabled()` false with no env; `GRAPH_EXPANSION_ENV` as documented |
107
+ | 15 | Self-Model (Personal Ontology) | WORKS | `POST /api/memory/self-model/propose` with `저는 …를 선호합니다` returned 1 candidate and **one pending review item**, nothing written; `apply` calls `review_queue.approve()` before `_write_fact`; summary capped at half the context budget |
108
+ | 16 | Workspace Reorganization | WORKS | one `folder_reorganization` proposal, targets always `topics/<주제>/`, `unplaced` reasons, **no delete path exists in the module**, skip-and-report on apply |
109
+ | 17 | Change Governance | WORKS | `MUTATING_TOOL_INVENTORY` + `assert_governance_coverage`; base-SHA conflict → 409; `_atomic_write` via `os.replace` under a lock; verifier fails closed to `NEEDS_REVIEW` |
110
+ | 18 | Brain Brief | WORKS | `/api/memory/brain-brief` and `/api/brain/proactive-brief` → 200; the proactive brief counts existing proposals and raises none |
111
+ | 19 | Conversation | WORKS | `tests/unit/test_chat_no_model.py`; the loaded-model list is empty in a clean boot and chat refuses rather than inventing output |
112
+ | 20 | Knowledge Graph | WORKS | `/api/graph`, `/knowledge-graph/stats`, `/api/knowledge-graph/provenance` → 200; v2 schema reported alongside v1 |
113
+ | 21 | Source Capture | WORKS | unified pipeline reachable from files/folders/notes/web; provenance keyed by origin (no wall-clock component) |
114
+ | 22 | Local Models | WORKS | `GET /models`, `/models/recommendations` → 200 with a real device profile; `allow_model_downloads: false` in a clean boot |
115
+ | 23 | Installer Audit | WORKS | `auto_setup` refuses to apply on a confirmation-token mismatch (`CommandConfirmationError`); `tests/unit/test_setup_wizard.py`, `test_cov_wp04_wizard_install.py` |
116
+ | 24 | Cloud Models | WORKS | `_load_cloud_model` raises `Missing API key env var: …` **before** the client is constructed; egress is audited before any call |
117
+ | 25 | Telegram | WORKS | `run_bot` returns without polling on any of: no token, empty allowlist, no server session; ACL check precedes `register_chat_id` on both message and callback paths |
118
+ | 26 | Agent Runtime | WORKS | `/agents/api/runtime/health` → `status: "unavailable"` with the honest reason ("No LLM-backed model is loaded; product execution API refuses simulation runs"); preview records no run; unknown roles rejected |
119
+ | 27 | Tool Registry / MCP | WORKS | `/tools/registry/diagnostics` → `registered_tools 47 / governed_tools 47`, empty `governance_without_handler` and `handler_without_governance`; `/mcp/installed`, `/mcp/tools` → 200 |
120
+ | 28 | Workspaces | WORKS | `/workspace/os` → 200 with the personal workspace default; admin surfaces on separate routes |
121
+ | 29 | VS Code Extension | WORKS | `/workspace/vscode/status` → 200 (`connected:false, status:"offline"`); `POST /agent` `stream:true` dispatches `agent_step`; 4xx never reads as success |
122
+ | 30 | Browser Extension | WORKS | every outbound call is `http://127.0.0.1:${port}`; an absent verdict renders `근거 확인 불가`; no approve/reject call exists in the extension |
123
+ | 31 | Telegram Review | WORKS | `/review` reads the same `/api/proposals`; 409 → "아무것도 쓰지 않았습니다", no retry |
124
+ | 32 | Knowledge Garden | WORKS | `GET /api/brain/garden` → 200 with exactly four beds; an unavailable graph yields empty beds |
125
+ | 33 | Agent File Tasks | WORKS | `executor_prompt_for(base, *, profile, self_model_summary)`; `EXECUTOR_PROMPT` itself carries no file-task hints; summary injected only when passed |
126
+ | 34 | Agent Profiles | WORKS | `COMPACT_MAX_PARAMS_B = 4.0`; an id naming no size keeps `standard`; a failed direct-path write is reported as not written |
127
+ | 35 | Folder Memory State | WORKS | `GET /knowledge-graph/local/health` → 200; vector freshness reported once and labelled `vector_freshness_global` |
128
+ | 36 | Voice Capture | WORKS | `GET /api/capture/voice/status` → `capture:true, transcription:false` with a plain-language reason; the transcriber really is one shared port (`ctx.MULTIMODAL_PORTS.transcriber` feeds both the pipeline and `VoiceCaptureService`) |
129
+ | 37 | Multi-modal Memories | WORKS | `ALLOW_MULTIMODAL_ENV` default `"0"`; both modes asserted in `tests/unit/test_t3_ingest_routing.py`; `NodeType.AUDIO` and `IMAGE` are first-class |
130
+ | 38 | Vision OCR / Caption / Embedding | WORKS | `VisionStub` is gone from product code and from the shipped wheel/sdist, with a test asserting its absence; `resolve_vision_embedder` has no hash fallback |
131
+ | 39 | Image Retrieval | **PARTIAL → code fixed** | late fusion, the separate `image_embeddings` table, width-mismatch skipping and the `multimodal` result block are all real and tested — but no production caller passed `multimodal=` to `context_quality_signal` (see Finding 5) |
132
+ | 40 | Evidence Thumbnails | WORKS | `THUMBNAIL_EDGE = 96`; the 24,000-char cap is enforced on write **and** on read; only `data:image/…` is accepted, server-side and client-side |
133
+ | 41 | Video Ingestion | WORKS | ingest returns `status:"unavailable"`, `indexing_status:"skipped"`, `node_id: None`; nothing is stored |
134
+ | 42 | Frontend Reliability | WORKS | `CoreServiceUnavailableBanner`; `ActionButton` does not fire success callbacks for `ok:false`; vitest 100% on all four metrics |
135
+ | 43 | Trusted Agent Loop | WORKS | `LoopTrace`, the `loop` payload on both the streaming and non-streaming terminals, python-literal repair, the agent-eval CI gate |
136
+ | 44 | Command Center | **MISMATCH → code fixed** | `/api/command/briefing` returned `health: {available:false, grade:null, score:null}` on a Brain reporting `grade: "excellent"` (see Finding 1) |
137
+ | 45 | Evidence → Action | WORKS | `POST /api/evidence/actions` with no resolvable citations → 200 with an explicit reason, never a silent empty list |
138
+ | 46 | Run Explanation | WORKS | `"ok": code == "done"`, reachable only from `state == "DONE"`; rendered on web, VS Code and Telegram |
139
+ | 47 | Project Sessions | WORKS | `GET /api/projects` → 200; `/agent` accepts `project_id`; a `NEEDS_REVIEW` run never becomes a project `done` |
140
+ | 48 | Citation Precision | WORKS | `tests/unit/test_typed_chunking.py`, `tests/unit/test_citation_grounding.py`; `plain` chunking stays byte-identical to the legacy walk |
141
+ | 49 | Graph Relation Evidence | WORKS | `tests/unit/test_relation_evidence.py` |
142
+ | 50 | Funnel Alerts | WORKS | `GET /api/admin/funnel-metrics` → 200 with named counters, `rates: null` and `alerts: []` below the 10-sample floor |
143
+ | 51 | Frontend Payload | **MISMATCH → doc fixed** | `npm run check:bundle` measures **103.0 KiB** gzip initial JS against the 150 KiB budget; the doc had read "~99 KiB" since 9.9.9 (see Finding 2) |
144
+ | 52 | Permission Modes | WORKS | `GET /api/permission-mode` → `strict` default with `proposal_first: true`; circuit breakers mode-invariant; the selector renders the server catalog and blocks `bypass` until acknowledged. One wording nuance: the home dial uses a confirmation dialog rather than a checkbox — consent is still explicit |
145
+ | 53 | Network Boundary | WORKS | `local_only` default; `hard_block_metadata_flags` = `do_not_share/local_only/private/sensitive` applied with no mode branch; `POST /api/network-boundary/preview` answers in `local_only` and reports `would_block` |
146
+ | 54 | Hybrid Cloud Chat | WORKS | all seven named modules exist; the adapter raises before opening a socket without `LATTICEAI_CLOUD_API_KEY`; the local path is a pure early return |
147
+ | 55 | Cloud Memory Write-Back | **MISMATCH → code fixed** | the enqueue was implemented and unit-tested, but both live call sites constructed `CloudResponseIngestor(store=knowledge_graph)` with no review sink (see Finding 6) |
148
+ | 56 | Obsidian Vault Bridge | WORKS | driven end to end in the signed-in pass: `permission_required` + token → `/permissions/approve/{token}` → `status: "dry_run"`, `scanned 2 / notes 2 / tags 1 / links.resolved 1` (see Finding 7 for the anonymous case) |
149
+ | 57 | Selective Brain Network | WORKS (flag off) | `GET /api/knowledge-graph/share` → `enabled:false` with the flag named; all four mutating share routes → 403 with the reason. **The encryption half of this row was being changed by a parallel track while this audit ran — see Caveat** |
150
+ | 58 | Release Assets | WORKS | package/pyproject/tauri/vsix all 11.1.0; `check_current_release_docs`, `check_doc_status`, `validate_release_artifacts`, `check_release_evidence_bound` all clean. Rolling the markers to 11.2.0 is the release lead's step, not this audit's |
151
+
152
+ **Totals as swept: 51 WORKS, 3 PARTIAL, 4 MISMATCH.** Of the four mismatches,
153
+ one was fixed in code during the sweep (Command Center), two were fixed in the
154
+ docs (Brain Home, Frontend Payload) and one was reported to its owning track
155
+ (Cloud Memory Write-Back). All three `PARTIAL` rows were reported rather than
156
+ changed: each needed a code change in a file another track owned this sprint,
157
+ or a behaviour decision that does not belong inside an audit. One Known
158
+ Limitation was retired as no longer true.
159
+
160
+ **Dispositions after the sweep (v11.2.0 integration close).** The four rows the
161
+ audit could not touch itself were closed once the parallel tracks had landed —
162
+ Findings 3, 4, 5 and 6 are all fixed in code, each with a regression test that
163
+ drives the shipped entry point rather than the collaborator (see
164
+ `tests/unit/test_t8_integration_close.py`). The verdict column above keeps the
165
+ *swept* verdict with its disposition appended, because a point-in-time audit
166
+ that silently rewrites itself is worth nothing; `FEATURE_STATUS.md` carries the
167
+ current state.
168
+
169
+ ## Findings
170
+
171
+ ### Finding 1 — the Command Center's health section was reading a key nothing writes (FIXED)
172
+
173
+ `CommandCenterService._health_section` read the Brain's health out of a nested
174
+ `overall` block:
175
+
176
+ ```python
177
+ overall = report.get("overall") or {}
178
+ "available": overall.get("score") is not None,
179
+ ```
180
+
181
+ `BrainIntelligenceService.health_report()` — the only producer — publishes
182
+ `overall_score` and `grade` at the **top level**. So the briefing's health
183
+ section was permanently `{available: false, grade: null, score: null}` while
184
+ `/api/brain/health` reported `grade: "excellent"` on the very same graph, and
185
+ the `check-health` quick action below it could never fire.
186
+
187
+ User-visible: `DailyBriefingPanel` renders
188
+ `{health.available && health.grade ? String(health.grade) : "—"}`, so Today's
189
+ Briefing showed a permanent dash with no reason beside it — the exact failure
190
+ mode the 9.9.7 rule ("a `—` must state why") exists to prevent.
191
+
192
+ Why the suite stayed green: `tests/unit/test_command_center.py`'s `FakeBrain`
193
+ returned `{"overall": {"score": …, "grade": …}}` — a shape no real collaborator
194
+ has ever produced. The test and the code agreed with each other and neither
195
+ agreed with production.
196
+
197
+ Fixed in `latticeai/services/command_center.py` (read the real shape), the fake
198
+ corrected to match its real counterpart, and pinned by
199
+ `tests/unit/test_a3_feature_audit.py`, which wires the two **real** services
200
+ together — the only arrangement that can catch this class of drift.
201
+
202
+ ### Finding 2 — the initial-JS figure had not been re-measured since 9.9.9 (FIXED)
203
+
204
+ FEATURE_STATUS claimed "~99 KiB gzip". `node scripts/check_bundle_budget.mjs`
205
+ measures **103.0 KiB** (index 66.1 + utils 29.2 + useQuery 7.6), against a build
206
+ `scripts/check_frontend_build_freshness.mjs` confirms is byte-identical to
207
+ source. The budget gate itself passes with room to spare — only the sentence was
208
+ stale. Lowered to the measured figure with the tool that measures it named.
209
+ The same stale number also appears in `ARCHITECTURE.md`.
210
+
211
+ ### Finding 3 — an empty Brain grades itself 100 / "excellent" (FIXED after the sweep)
212
+
213
+ `health_report()` averages only the dimensions it could measure. On a
214
+ first-run Brain, freshness, connectivity and consistency all report
215
+ `status: "unavailable", score: null` and embedding coverage reports 100 (zero
216
+ of zero indexed) — so the composite is `overall_score: 100, grade: "excellent"`
217
+ from a single dimension over an empty graph.
218
+
219
+ Each dimension is individually honest; the composite is not. This is a
220
+ behaviour question rather than a documentation one (the row does not promise a
221
+ specific empty-Brain grade), and changing the score would move a number the
222
+ frontend and its tests render — so it is named here rather than changed inside
223
+ an audit.
224
+
225
+ **Disposition.** The lie was upstream of the composite: an index holding
226
+ nothing reports `coverage_ratio: 1.0`, which is 100% of nothing. That dimension
227
+ now reads `unavailable` with the reason `"no indexable items yet"` whenever
228
+ `source_items` is zero, so the empty Brain has *no* measurable dimension and
229
+ the existing `scores or None` path already returns `overall_score: None,
230
+ grade: null` — which the frontend already renders as 데이터 없음 / "No data
231
+ yet". Two additions keep the partial cases honest too: every unavailable
232
+ dimension now carries a `reason` (the graph could not be read vs. nothing saved
233
+ yet vs. no relationships yet), and the report gains a `coverage` block
234
+ (`measured` / `total` / `unavailable` / `partial`) so a verdict drawn from two
235
+ of four checks says so. When nothing could be measured a top-level `reason`
236
+ names each gap — the 9.9.7 rule that a "—" must state why.
237
+
238
+ ### Finding 4 — `vector_freshness_breakdown()` has no surface (FIXED after the sweep)
239
+
240
+ The Vector Freshness row explains that "12 pending" hides the difference
241
+ between twelve never-embedded imports and twelve edits whose current answers
242
+ are quietly wrong, and names `vector_freshness_breakdown()` as the answer. The
243
+ method exists on the store and is tested — and has **zero product callers**.
244
+ `GET /api/brain/vector-freshness` still returns the aggregate
245
+ (`{status, pending_items, total_items, detail}`), so the distinction the
246
+ sentence sells is not visible anywhere in the product.
247
+
248
+ Either the endpoint should carry the breakdown or the row should say the split
249
+ is store-level only. The fix lives in store/API files owned by another track
250
+ this sprint, so it is reported rather than edited.
251
+
252
+ **Disposition.** The endpoint carries it. `BrainIntelligenceService.vector_freshness`
253
+ now returns the four-key contract from an unchanged private method and attaches
254
+ `breakdown` beside it when the store can compute one. The key is **omitted**
255
+ rather than zero-filled for every reason it cannot be had — graph off, an older
256
+ store without the method, an unreadable index, an empty answer — because a
257
+ zeroed backlog nobody measured is exactly the dishonesty the split exists to
258
+ remove. The frontend chip reads `pending_items` and is untouched.
259
+
260
+ ### Finding 5 — `context_quality` never gains its `multimodal` key in production (FIXED after the sweep)
261
+
262
+ The Image Retrieval row says `context_quality` "gains a `multimodal` key only
263
+ when image nodes are really in the context". The mechanism is real and tested,
264
+ but the only call site that passes `multimodal=` to `context_quality_signal` is
265
+ `context_for_query(with_meta=True)`, which has no production caller. Both live
266
+ producers of `context_quality` — `api/chat_helpers.build_context_quality` and
267
+ `core/context_builder` — discard the hybrid result's `multimodal` block. Net
268
+ effect: on every shipped surface `context_quality` keeps the four-key shape
269
+ even when the answer cites `Image` nodes. Owned by the multi-modal track.
270
+
271
+ **Disposition.** Both producers pass it now. `build_context_quality` calls
272
+ `multimodal_signal(matches)` on **both** of its arms — the hybrid store and the
273
+ lexical-only fallback, since a picture found by keyword is still a picture —
274
+ and `retrieve_context_for_generation` does the same for the document path. The
275
+ present-only-when-true contract is unchanged: `multimodal_signal` returns
276
+ `None` for an all-text result set and `context_quality_signal` omits the key,
277
+ so an all-text answer is byte-identical to what 11.1.0 returned.
278
+
279
+ ### Finding 6 — cloud write-back is never enqueued on the live path (FIXED after the sweep)
280
+
281
+ `CloudResponseIngestor.ingest` stages a `change_proposal` **only when a
282
+ `review_queue` sink is bound**. Both production call sites bind none:
283
+
284
+ ```
285
+ latticeai/services/hybrid_chat.py: ingestor = CloudResponseIngestor(store=knowledge_graph)
286
+ latticeai/services/hybrid_chat.py: ingest_status = CloudResponseIngestor(store=knowledge_graph).ingest(plan)
287
+ ```
288
+
289
+ With `plan.auto_commit` hardcoded `False`, `ingest()` returns
290
+ `{"status": "staged", "review_item_id": None, "written_nodes": 0}` — knowledge
291
+ extracted from a cloud answer is neither written nor queued. It is discarded
292
+ after the `hybrid_done` frame. The unit tests pass a `review_queue` directly to
293
+ the ingestor, so the wiring gap is invisible to CI. Related: the hybrid policy's
294
+ `auto_commit` flag is settable through `POST /api/network-boundary/policy` but
295
+ has no consumer.
296
+
297
+ The row's "is enqueued as a Review Center `change_proposal`" is therefore not
298
+ true as shipped. Threading a review sink through
299
+ `stream_hybrid_cloud_turn` / `run_hybrid_cloud_turn` / `maybe_hybrid_stream_response`
300
+ touches the chat router and the review-queue seam, both of which other tracks
301
+ were editing during this sweep — reported, not changed.
302
+
303
+ **Disposition.** Threaded, exactly there. Both turn functions take
304
+ `review_queue` and `auto_commit` as arguments (defaults `None` / `False`, so a
305
+ headless caller still stages nothing), `maybe_hybrid_stream_response` forwards
306
+ the sink and resolves the flag through the new
307
+ `chat_hybrid.resolve_hybrid_auto_commit`, and `/chat` supplies the live queue
308
+ through a new `AppContext.review_queue` **provider** — a callable rather than a
309
+ value, because `REVIEW_QUEUE` is produced in `phase_platform_features`, two
310
+ phases after the context is built, and a captured value would have been `None`
311
+ forever. `resolve_hybrid_auto_commit` treats an unreadable policy as the
312
+ default rather than as permission.
313
+
314
+ The reason CI could not see this — the unit tests handed the ingestor its
315
+ collaborator directly — is also the reason the new test drives `POST /chat`
316
+ with the dial on `cloud_allowed` and a fake adapter, and asserts the item
317
+ landed in the queue with `source: "change_proposal"`.
318
+
319
+ ### Finding 7 — the local-file 401 is a gate, not a defect (CLEARED)
320
+
321
+ Worth recording because it looks like a defect and is not. In the default local
322
+ profile (`require_auth = false`), `POST /api/ingestion/obsidian` and every other
323
+ local-file route answer **401** to an anonymous caller:
324
+ `PermissionGateway.require_local_user` resolves the raw session identity and
325
+ does **not** fall through to the anonymous local owner that `require_user`
326
+ returns. Reading only that line suggests the vault bridge is unreachable out of
327
+ the box.
328
+
329
+ It is not. Driven end to end in the signed-in pass — register (first user
330
+ becomes admin) → login → `POST /api/ingestion/obsidian` →
331
+ `permission_required: true` with an `approval_token` →
332
+ `POST /permissions/approve/{token}` → re-post with the token →
333
+ `status: "dry_run", scanned: 2, notes: 2, tags: 1, links.resolved: 1`. That
334
+ sequence *is* the "standard local-read approval dance" the row describes.
335
+ Filesystem access deliberately demands a named account even where the rest of
336
+ the local API accepts the anonymous owner. Verdict: WORKS.
337
+
338
+ ### Finding 8 — a retired Known Limitation
339
+
340
+ "The approval card under 작업 → 실행 still labels raw payload fields (`Action`,
341
+ `Action Label`, `User Email`) in plain mode" is no longer true. `Act.tsx`'s
342
+ `HumanPermissionDetails` renders a translated action badge, the target
343
+ filename, a free-text reason and a labelled requester line, with a regression
344
+ test forbidding raw i18n keys from reaching the DOM; the repo carries no
345
+ rendered `Action Label` / `User Email` literal. The screenshot the bullet cited
346
+ as proof — `output/release/v10.6.3/screenshots/09-automation-runs.png` — no
347
+ longer exists (release evidence now starts at v11.0.0). The bullet was removed;
348
+ carrying a fixed defect as a standing limitation is its own kind of dishonesty.
349
+
350
+ ## prefix-static hermeticity (wp19 / wp36) — re-confirmed
351
+
352
+ `Config.from_env` resolves a *missing* static dir to `sys.prefix/static` when
353
+ that exists, which on a pip-installed machine it genuinely does. `_empty_prefix`
354
+ in `tests/unit/test_cov_wp19_build_phases.py` and
355
+ `tests/unit/test_cov_wp36_build_phases_late.py` neutralizes that so the
356
+ assertions describe the resolver rather than the runner.
357
+
358
+ Re-verified by simulating the release-runner condition for a whole session: a
359
+ pytest plugin pointed `sys.prefix` at a temp directory that **does** carry
360
+ `static/` (with a sentinel file), and the two suites plus
361
+ `test_cov_wp19_cli_entrypoint.py` were run under it.
362
+
363
+ * **81 passed.**
364
+ * The simulated prefix's `static/` still contained only the sentinel afterwards
365
+ — no test wrote into it.
366
+ * The hazard itself was confirmed live under the same plugin: with a missing
367
+ configured static dir, `Config.from_env` resolves to the fake prefix's
368
+ `static/`. The hermeticity is load-bearing, not decorative.
369
+
370
+ ## Caveat — a concurrently mutating tree
371
+
372
+ This sweep ran while two other tracks were writing to the same working tree.
373
+ Files under `lattice_brain/` (portability, sealed box, multimodal, ingestion,
374
+ graph schema) and `latticeai/services/model_*` were changing during the run, and
375
+ one intermediate state was observed directly (a partially-rewired
376
+ `latticeai/services/obsidian_bridge.py` raising `NameError` mid-refactor, and a
377
+ `KnowledgeGraphStore` attribute warning at boot). Rows 37–41, 55 and 57 are
378
+ therefore reported **as of this snapshot**; their owning tracks are the
379
+ authority on where they land. Everything measured for rows outside those files
380
+ was stable across repeated runs.
381
+
382
+ ## Changes this audit made
383
+
384
+ * `latticeai/services/command_center.py` — read the health shape the Brain
385
+ actually publishes (Finding 1).
386
+ * `tests/unit/test_command_center.py` — corrected the `FakeBrain` fixture to
387
+ its real counterpart's shape.
388
+ * `tests/unit/test_a3_feature_audit.py` — new. Pins the two drift classes this
389
+ audit found: a doc naming an endpoint the app does not serve, and the
390
+ briefing reading a health score the Brain never published.
391
+ * `FEATURE_STATUS.md` — Brain Home (Brain Brief is on the dock, not the first
392
+ screen), Frontend Payload (103 KiB, measured), and the retired approval-card
393
+ limitation.