ltcai 9.9.0 → 9.9.1

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 (96) hide show
  1. package/README.md +40 -35
  2. package/bin/ltcai.js +1 -1
  3. package/desktop/electron/main.cjs +1 -1
  4. package/docs/CHANGELOG.md +47 -0
  5. package/docs/CI_AND_RELEASE_GATES.md +1 -1
  6. package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
  7. package/docs/DEVELOPMENT.md +1 -1
  8. package/docs/LEGACY_COMPATIBILITY.md +48 -49
  9. package/docs/ONBOARDING.md +1 -1
  10. package/docs/OPERATIONS.md +1 -1
  11. package/docs/TRUST_MODEL.md +1 -1
  12. package/docs/WHY_LATTICE.md +1 -1
  13. package/docs/kg-schema.md +6 -6
  14. package/lattice_brain/__init__.py +1 -1
  15. package/lattice_brain/graph/schema.py +1 -1
  16. package/lattice_brain/runtime/multi_agent.py +1 -1
  17. package/latticeai/__init__.py +1 -1
  18. package/latticeai/core/legacy_compatibility.py +71 -47
  19. package/latticeai/core/marketplace.py +1 -1
  20. package/latticeai/core/workspace_os.py +1 -1
  21. package/latticeai/services/architecture_readiness.py +1 -1
  22. package/latticeai/services/product_readiness.py +2 -2
  23. package/latticeai/tools/__init__.py +1 -1
  24. package/latticeai/tools/knowledge.py +1 -1
  25. package/package.json +6 -16
  26. package/scripts/check_current_release_docs.mjs +20 -1
  27. package/scripts/check_legacy_debt.mjs +91 -0
  28. package/scripts/prune_release_evidence.mjs +38 -0
  29. package/scripts/wheel_smoke.py +22 -6
  30. package/src-tauri/Cargo.lock +1 -1
  31. package/src-tauri/Cargo.toml +1 -1
  32. package/src-tauri/src/main.rs +12 -12
  33. package/src-tauri/tauri.conf.json +1 -13
  34. package/static/app/asset-manifest.json +27 -27
  35. package/static/app/assets/Act-CcSWTQsx.js +1 -0
  36. package/static/app/assets/{Brain-Cgkh0Hpn.js → Brain-CSCDZEHd.js} +2 -2
  37. package/static/app/assets/BrainHome-B5QpJ1Fx.js +2 -0
  38. package/static/app/assets/BrainSignals-CmFD8PWk.js +1 -0
  39. package/static/app/assets/Capture-BiuerOio.js +1 -0
  40. package/static/app/assets/CommandPalette-BrqpT7K2.js +1 -0
  41. package/static/app/assets/Library-BcaiPbum.js +1 -0
  42. package/static/app/assets/LivingBrain-6FvA0H5T.js +1 -0
  43. package/static/app/assets/ProductFlow-Cr8bkQPw.js +1 -0
  44. package/static/app/assets/ReviewCard-D56TMypk.js +1 -0
  45. package/static/app/assets/System-D6uJacqu.js +1 -0
  46. package/static/app/assets/bot-DgC7xgUl.js +1 -0
  47. package/static/app/assets/cpu-DDGb4XHl.js +1 -0
  48. package/static/app/assets/download-C-Ei3GFa.js +1 -0
  49. package/static/app/assets/folder-open-BbVuSO9M.js +1 -0
  50. package/static/app/assets/hard-drive-yDZR6XYz.js +1 -0
  51. package/static/app/assets/index-CFRhNBuH.css +2 -0
  52. package/static/app/assets/{index-BjaXCeOZ.js → index-DrZSkwrE.js} +3 -3
  53. package/static/app/assets/input-DPIkTs9n.js +1 -0
  54. package/static/app/assets/navigation-B1mkmyUe.js +1 -0
  55. package/static/app/assets/network-0XDI1Uf_.js +1 -0
  56. package/static/app/assets/primitives-sIQYKyf5.js +1 -0
  57. package/static/app/assets/sunrise-DXMjueec.js +1 -0
  58. package/static/app/assets/textarea-DvnwpnqS.js +1 -0
  59. package/static/app/assets/utils-BrfFHAJl.js +7 -0
  60. package/static/app/index.html +4 -4
  61. package/static/sw.js +1 -1
  62. package/auto_setup.py +0 -21
  63. package/kg_schema.py +0 -13
  64. package/knowledge_graph.py +0 -51
  65. package/knowledge_graph_api.py +0 -24
  66. package/llm_router.py +0 -20
  67. package/local_knowledge_api.py +0 -16
  68. package/ltcai_cli.py +0 -21
  69. package/mcp_registry.py +0 -20
  70. package/p_reinforce.py +0 -16
  71. package/setup_wizard.py +0 -16
  72. package/static/app/assets/Act-CfOPUKPs.js +0 -2
  73. package/static/app/assets/BrainHome-D8e3wQiW.js +0 -3
  74. package/static/app/assets/BrainSignals-BjRBA18L.js +0 -1
  75. package/static/app/assets/Capture-CQQYQ1Ga.js +0 -1
  76. package/static/app/assets/CommandPalette-pss56Mg4.js +0 -1
  77. package/static/app/assets/Library-CoI3xYJk.js +0 -1
  78. package/static/app/assets/LivingBrain-CaU_hCxQ.js +0 -1
  79. package/static/app/assets/ProductFlow-Db-1O71-.js +0 -1
  80. package/static/app/assets/System-Dfocn0zN.js +0 -1
  81. package/static/app/assets/bot-Bp2x1i6F.js +0 -1
  82. package/static/app/assets/circle-check-CyGal42W.js +0 -1
  83. package/static/app/assets/cpu-DQFk82hG.js +0 -1
  84. package/static/app/assets/download-C70gKQVi.js +0 -1
  85. package/static/app/assets/folder-open-DNGytsVw.js +0 -1
  86. package/static/app/assets/hard-drive-CYo_bEl4.js +0 -1
  87. package/static/app/assets/i18n-BBMJshCW.js +0 -7
  88. package/static/app/assets/index-BqRcLZR3.css +0 -2
  89. package/static/app/assets/input-ChWD-Fsh.js +0 -1
  90. package/static/app/assets/navigation-D9D9_FPC.js +0 -1
  91. package/static/app/assets/network-DzLDGmtV.js +0 -1
  92. package/static/app/assets/primitives-CmaSAyGG.js +0 -1
  93. package/static/app/assets/sparkles-DXiaM1NS.js +0 -1
  94. package/static/app/assets/textarea-CV43-o79.js +0 -1
  95. package/telegram_bot.py +0 -16
  96. package/tools/__init__.py +0 -29
@@ -14,7 +14,7 @@ from pathlib import Path
14
14
  from typing import Any, Dict, List
15
15
 
16
16
 
17
- LEGACY_COMPATIBILITY_VERSION = "9.9.0"
17
+ LEGACY_COMPATIBILITY_VERSION = "9.9.1"
18
18
 
19
19
 
20
20
  @dataclass(frozen=True)
@@ -40,106 +40,130 @@ class LegacyShim:
40
40
 
41
41
 
42
42
  LEGACY_SHIMS: List[LegacyShim] = [
43
+ LegacyShim(
44
+ path="server.py",
45
+ owner="latticeai.server_app",
46
+ replacement="from latticeai.server_app import app, main",
47
+ reason="Deployment docs, Docker CMD, start scripts, and uvicorn targets still use server:app.",
48
+ removal_phase="major-release-after-9.x",
49
+ ),
50
+ ]
51
+
52
+ # Shim layers that have completed their compatibility window and were
53
+ # physically deleted. Kept in the report so consumers (docs, tests, release
54
+ # notes) can tell "removed on purpose" apart from "missing by accident".
55
+ # Removing the internal-only layers is the first step of extracting
56
+ # ``lattice_brain`` as a standalone Brain Core package: the package now has
57
+ # exactly one import surface (``lattice_brain.*`` physical paths).
58
+ REMOVED_SHIMS: List[LegacyShim] = [
43
59
  LegacyShim(
44
60
  path="knowledge_graph.py",
45
- owner="lattice_brain.graph",
61
+ owner="lattice_brain.graph.store",
46
62
  replacement="from lattice_brain.graph.store import KnowledgeGraphStore",
47
- reason="Historical scripts imported the graph store from the repo root.",
48
- removal_phase="major-release-after-8.x",
63
+ reason="Root compatibility shims completed their window; removed in 9.9.1.",
64
+ removal_phase="removed-9.9.1",
65
+ layer="root",
66
+ status="removed",
49
67
  ),
50
68
  LegacyShim(
51
69
  path="knowledge_graph_api.py",
52
70
  owner="latticeai.api.knowledge_graph",
53
71
  replacement="from latticeai.api.knowledge_graph import create_knowledge_graph_router",
54
- reason="Older API composition roots imported the knowledge graph router from the repo root.",
55
- removal_phase="major-release-after-8.x",
72
+ reason="Root compatibility shims completed their window; removed in 9.9.1.",
73
+ removal_phase="removed-9.9.1",
74
+ layer="root",
75
+ status="removed",
56
76
  ),
57
77
  LegacyShim(
58
78
  path="kg_schema.py",
59
79
  owner="lattice_brain.graph.schema",
60
80
  replacement="from lattice_brain.graph.schema import ...",
61
- reason="Historical graph schema tests and tools referenced root schema symbols.",
62
- removal_phase="major-release-after-8.x",
81
+ reason="Root compatibility shims completed their window; removed in 9.9.1.",
82
+ removal_phase="removed-9.9.1",
83
+ layer="root",
84
+ status="removed",
63
85
  ),
64
86
  LegacyShim(
65
87
  path="ltcai_cli.py",
66
88
  owner="latticeai.cli.entrypoint",
67
89
  replacement="from latticeai.cli.entrypoint import main",
68
- reason="Console entry points and older package installs imported the CLI root module.",
69
- removal_phase="major-release-after-8.x",
90
+ reason="Root compatibility shims completed their window; removed in 9.9.1.",
91
+ removal_phase="removed-9.9.1",
92
+ layer="root",
93
+ status="removed",
70
94
  ),
71
95
  LegacyShim(
72
96
  path="telegram_bot.py",
73
97
  owner="latticeai.integrations.telegram_bot",
74
98
  replacement="from latticeai.integrations.telegram_bot import run_bot",
75
- reason="Existing Telegram automation setups still import the legacy root module.",
76
- removal_phase="major-release-after-8.x",
99
+ reason="Root compatibility shims completed their window; removed in 9.9.1.",
100
+ removal_phase="removed-9.9.1",
101
+ layer="root",
102
+ status="removed",
77
103
  ),
78
104
  LegacyShim(
79
105
  path="p_reinforce.py",
80
106
  owner="latticeai.services.p_reinforce",
81
107
  replacement="from latticeai.services.p_reinforce import PReinforceGardener",
82
- reason="Old gardener scripts referenced the root reinforcement helper.",
83
- removal_phase="major-release-after-8.x",
108
+ reason="Root compatibility shims completed their window; removed in 9.9.1.",
109
+ removal_phase="removed-9.9.1",
110
+ layer="root",
111
+ status="removed",
84
112
  ),
85
113
  LegacyShim(
86
114
  path="mcp_registry.py",
87
115
  owner="latticeai.core.mcp_registry",
88
116
  replacement="import latticeai.core.mcp_registry",
89
- reason="Older integrations imported the MCP registry from the repo root.",
90
- removal_phase="major-release-after-9.x",
117
+ reason="Root compatibility shims completed their window; removed in 9.9.1.",
118
+ removal_phase="removed-9.9.1",
119
+ layer="root",
120
+ status="removed",
91
121
  ),
92
122
  LegacyShim(
93
123
  path="llm_router.py",
94
124
  owner="latticeai.models.router",
95
125
  replacement="import latticeai.models.router",
96
- reason="Historical scripts and tests imported the LLM router from the repo root.",
97
- removal_phase="major-release-after-9.x",
98
- ),
99
- LegacyShim(
100
- path="server.py",
101
- owner="latticeai.server_app",
102
- replacement="from latticeai.server_app import app, main",
103
- reason="Deployment docs and local launch scripts historically targeted server.py.",
104
- removal_phase="major-release-after-8.x",
105
- ),
106
- LegacyShim(
107
- path="tools/",
108
- owner="latticeai.tools",
109
- replacement="from latticeai.tools import execute_tool",
110
- reason="Existing integrations and tests import the historical root tools package.",
111
- removal_phase="major-release-after-9.x",
126
+ reason="Root compatibility shims completed their window; removed in 9.9.1.",
127
+ removal_phase="removed-9.9.1",
128
+ layer="root",
129
+ status="removed",
112
130
  ),
113
131
  LegacyShim(
114
132
  path="local_knowledge_api.py",
115
133
  owner="latticeai.services.local_knowledge",
116
134
  replacement="from latticeai.services.local_knowledge import create_local_knowledge_router",
117
- reason="Local folder ingestion integrations used the root local knowledge API.",
118
- removal_phase="requires-api-route-migration",
135
+ reason="Root compatibility shims completed their window; removed in 9.9.1.",
136
+ removal_phase="removed-9.9.1",
137
+ layer="root",
138
+ status="removed",
119
139
  ),
120
140
  LegacyShim(
121
141
  path="auto_setup.py",
122
142
  owner="latticeai.setup.auto_setup",
123
143
  replacement="from latticeai.setup.auto_setup import probe, recommend",
124
- reason="Historical scripts invoke the zero-config setup module from the repo root.",
125
- removal_phase="major-release-after-9.x",
144
+ reason="Root compatibility shims completed their window; removed in 9.9.1.",
145
+ removal_phase="removed-9.9.1",
146
+ layer="root",
147
+ status="removed",
126
148
  ),
127
149
  LegacyShim(
128
150
  path="setup_wizard.py",
129
151
  owner="latticeai.setup.wizard",
130
152
  replacement="from latticeai.setup.wizard import scan_environment",
131
- reason="Older integrations imported the setup wizard from the repo root.",
132
- removal_phase="major-release-after-9.x",
153
+ reason="Root compatibility shims completed their window; removed in 9.9.1.",
154
+ removal_phase="removed-9.9.1",
155
+ layer="root",
156
+ status="removed",
157
+ ),
158
+ LegacyShim(
159
+ path="tools/",
160
+ owner="latticeai.tools",
161
+ replacement="from latticeai.tools import execute_tool",
162
+ reason="Root compatibility shims completed their window; removed in 9.9.1.",
163
+ removal_phase="removed-9.9.1",
164
+ layer="root",
165
+ status="removed",
133
166
  ),
134
- ]
135
-
136
- # Shim layers that have completed their compatibility window and were
137
- # physically deleted. Kept in the report so consumers (docs, tests, release
138
- # notes) can tell "removed on purpose" apart from "missing by accident".
139
- # Removing the internal-only layers is the first step of extracting
140
- # ``lattice_brain`` as a standalone Brain Core package: the package now has
141
- # exactly one import surface (``lattice_brain.*`` physical paths).
142
- REMOVED_SHIMS: List[LegacyShim] = [
143
167
  LegacyShim(
144
168
  path="lattice_brain/store.py",
145
169
  owner="lattice_brain.graph.store",
@@ -11,7 +11,7 @@ from copy import deepcopy
11
11
  from typing import Any, Dict, List, Optional
12
12
 
13
13
 
14
- MARKETPLACE_VERSION = "9.9.0"
14
+ MARKETPLACE_VERSION = "9.9.1"
15
15
  TEMPLATE_KINDS = ("plugin", "workflow", "agent", "ingestion_bridge")
16
16
 
17
17
 
@@ -49,7 +49,7 @@ __all__ = [
49
49
  "remove_skill_directory",
50
50
  ]
51
51
 
52
- WORKSPACE_OS_VERSION = "9.9.0"
52
+ WORKSPACE_OS_VERSION = "9.9.1"
53
53
 
54
54
  # Workspace types separate single-user Personal workspaces from shared
55
55
  # Organization workspaces. Both keep the same local-first JSON store; the type
@@ -17,7 +17,7 @@ from typing import Any, Dict, List
17
17
  from latticeai.core.legacy_compatibility import legacy_shim_report
18
18
 
19
19
 
20
- ARCHITECTURE_VERSION_TARGET = "9.9.0"
20
+ ARCHITECTURE_VERSION_TARGET = "9.9.1"
21
21
 
22
22
  PREFERRED_REFACTORING_ORDER = [
23
23
  "agent-runtime",
@@ -18,7 +18,7 @@ from typing import Any, Dict, List
18
18
 
19
19
  from latticeai.services.architecture_readiness import architecture_readiness
20
20
 
21
- PRODUCT_VERSION_TARGET = "9.9.0"
21
+ PRODUCT_VERSION_TARGET = "9.9.1"
22
22
 
23
23
 
24
24
  @dataclass(frozen=True)
@@ -140,7 +140,7 @@ PRODUCT_GATES: List[ProductGate] = [
140
140
  evidence=[
141
141
  "scripts/brain_quality_eval.py",
142
142
  "scripts/product_readiness.py",
143
- "tests/unit/test_v78_product_readiness.py",
143
+ "tests/unit/test_product_readiness.py",
144
144
  "tests/visual/v3.spec.js::Brain Chat Home",
145
145
  ".github/workflows/ci.yml::scripts/product_readiness.py",
146
146
  ".github/workflows/release.yml::npm run lint",
@@ -23,7 +23,7 @@ from pathlib import Path
23
23
  from typing import Any, Callable, Dict, List, Optional
24
24
 
25
25
  from latticeai.core.tool_registry import ToolRegistry
26
- from p_reinforce import BRAIN_DIR, STRUCTURE
26
+ from latticeai.services.p_reinforce import BRAIN_DIR, STRUCTURE
27
27
 
28
28
  _PLATFORM = platform.system() # "Darwin" | "Windows" | "Linux"
29
29
 
@@ -6,7 +6,7 @@ import hashlib
6
6
  from pathlib import Path
7
7
  from typing import Any, Dict, List, Optional
8
8
 
9
- from p_reinforce import BRAIN_DIR, STRUCTURE
9
+ from latticeai.services.p_reinforce import BRAIN_DIR, STRUCTURE
10
10
  from latticeai.tools import MAX_FILE_BYTES, ToolError
11
11
 
12
12
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ltcai",
3
- "version": "9.9.0",
3
+ "version": "9.9.1",
4
4
  "description": "Lattice AI — local-first Digital Brain that keeps your knowledge durable across any AI model.",
5
5
  "homepage": "https://github.com/TaeSooPark-PTS/LatticeAI#readme",
6
6
  "repository": {
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "scripts": {
18
18
  "start": "LTCAI",
19
- "dev": "python3 ltcai_cli.py --reload",
19
+ "dev": "python3 -m latticeai.cli.entrypoint --reload",
20
20
  "build": "npm run build:assets && npm run build:python",
21
21
  "frontend:dev": "vite --host 127.0.0.1",
22
22
  "frontend:openapi": "node scripts/run_python.mjs scripts/export_openapi.py frontend/openapi.json && npx openapi-typescript frontend/openapi.json -o frontend/src/api/openapi.ts",
@@ -24,7 +24,8 @@
24
24
  "build:assets": "vite build && node scripts/build_frontend_assets.mjs",
25
25
  "build:python": "node scripts/run_python.mjs -m build",
26
26
  "check:python": "node scripts/run_python.mjs scripts/check_python.py",
27
- "lint": "npm run lint:python && node --check tests/visual/mock_server.cjs && node --check tests/visual/v3.spec.js && npm run lint:frontend && npm run frontend:openapi:check && node scripts/check_i18n_literals.mjs && npm run check:bundle && npm run test:browser-extension",
27
+ "lint": "npm run lint:python && node --check tests/visual/mock_server.cjs && node --check tests/visual/v3.spec.js && npm run lint:frontend && npm run frontend:openapi:check && node scripts/check_i18n_literals.mjs && npm run check:bundle && npm run check:legacy-debt && npm run test:browser-extension",
28
+ "check:legacy-debt": "node scripts/check_legacy_debt.mjs",
28
29
  "lint:python": "node scripts/run_python.mjs -m ruff check .",
29
30
  "lint:frontend": "node scripts/lint_frontend.mjs",
30
31
  "check:bundle": "node scripts/check_bundle_budget.mjs",
@@ -46,7 +47,8 @@
46
47
  "desktop:tauri:check": "cd src-tauri && cargo check",
47
48
  "desktop:electron": "electron desktop/electron/main.cjs",
48
49
  "package:vsix": "node scripts/build_vsix.mjs",
49
- "release:evidence": "node scripts/capture_release_evidence.mjs",
50
+ "release:evidence": "node scripts/capture_release_evidence.mjs && node scripts/prune_release_evidence.mjs",
51
+ "release:evidence:prune": "node scripts/prune_release_evidence.mjs",
50
52
  "release:artifacts": "node scripts/clean_release_artifacts.mjs $npm_package_version && npm run build:assets && npm run build:python && npm pack && npm run package:vsix && npm run desktop:tauri:build",
51
53
  "release:validate": "node scripts/run_python.mjs scripts/validate_release_artifacts.py $npm_package_version --require-vsix --require-tgz --require-dmg",
52
54
  "release:smoke": "node scripts/run_python.mjs scripts/release_smoke.py $npm_package_version",
@@ -76,17 +78,7 @@
76
78
  "bin/ltcai.js",
77
79
  "LICENSE",
78
80
  "requirements.txt",
79
- "ltcai_cli.py",
80
- "auto_setup.py",
81
81
  "server.py",
82
- "kg_schema.py",
83
- "knowledge_graph.py",
84
- "local_knowledge_api.py",
85
- "llm_router.py",
86
- "p_reinforce.py",
87
- "telegram_bot.py",
88
- "tools/",
89
- "mcp_registry.py",
90
82
  "latticeai/**/*.py",
91
83
  "lattice_brain/**/*.py",
92
84
  "skills/",
@@ -113,8 +105,6 @@
113
105
  "!**/*.pyc",
114
106
  "!**/*.map",
115
107
  "README.md",
116
- "setup_wizard.py",
117
- "knowledge_graph_api.py",
118
108
  "static/vendor/",
119
109
  "src-tauri/",
120
110
  "!src-tauri/gen/",
@@ -6,7 +6,7 @@ const root = process.cwd();
6
6
  const pkg = JSON.parse(readFileSync(path.join(root, "package.json"), "utf8"));
7
7
  const version = pkg.version;
8
8
  const releaseDir = `output/release/v${version}`;
9
- const releaseTheme = "Fail-Closed Trust";
9
+ const releaseTheme = "Clean Foundations";
10
10
  const title = `${version} — ${releaseTheme}`;
11
11
  const escapedVersion = version.replaceAll(".", "\\.");
12
12
 
@@ -71,6 +71,25 @@ for (const rel of [
71
71
  requireIncludes(rel, version);
72
72
  }
73
73
 
74
+ // The Release Artifact Map in ARCHITECTURE.md must name current-version
75
+ // artifacts exactly — this block drifted silently before 9.9.1.
76
+ requireIncludes("ARCHITECTURE.md", `${version} exact artifact names:`);
77
+ for (const artifact of [
78
+ `dist/ltcai-${version}-py3-none-any.whl`,
79
+ `dist/ltcai-${version}.tar.gz`,
80
+ `ltcai-${version}.tgz`,
81
+ `dist/ltcai-${version}.vsix`,
82
+ `src-tauri/target/release/bundle/dmg/Lattice AI_${version}_aarch64.dmg`,
83
+ ]) {
84
+ requireIncludes("ARCHITECTURE.md", artifact);
85
+ }
86
+ const architecture = read("ARCHITECTURE.md");
87
+ for (const match of architecture.matchAll(/ltcai-(\d+\.\d+\.\d+)/g)) {
88
+ if (match[1] !== version) {
89
+ errors.push(`ARCHITECTURE.md: stale artifact version reference ${match[0]}`);
90
+ }
91
+ }
92
+
74
93
  requireIncludes("README.md", `The current release is **${title}**`);
75
94
  requireIncludes("README.md", `![v${version} Living Brain walkthrough]`);
76
95
  requireIncludes("RELEASE.md", `## v${version} — ${releaseTheme}`);
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+ // Legacy debt gate (9.9.1): the root shim layer stays removed.
3
+ //
4
+ // Fails when:
5
+ // 1. a Python module other than server.py appears at the repository root;
6
+ // 2. any source tree imports a removed root shim module;
7
+ // 3. a removed shim directory (tools/) reappears at the root.
8
+ //
9
+ // Mirror of tests/unit/test_legacy_root_shims.py, but static and runnable in
10
+ // `npm run lint` without a Python environment.
11
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
12
+ import { join, relative } from "node:path";
13
+
14
+ const repo = join(import.meta.dirname, "..");
15
+
16
+ const ALLOWED_ROOT_MODULES = new Set(["server.py"]);
17
+ const REMOVED_ROOT_DIRS = ["tools"];
18
+ const REMOVED_MODULES = [
19
+ "ltcai_cli",
20
+ "auto_setup",
21
+ "setup_wizard",
22
+ "mcp_registry",
23
+ "kg_schema",
24
+ "knowledge_graph",
25
+ "knowledge_graph_api",
26
+ "local_knowledge_api",
27
+ "llm_router",
28
+ "p_reinforce",
29
+ "telegram_bot",
30
+ "tools",
31
+ ];
32
+ // Trees that must not import removed shims. static/app and node_modules are
33
+ // build output; src-tauri/target is compiler output.
34
+ const SOURCE_TREES = ["latticeai", "lattice_brain", "tests", "scripts", "bin", "desktop/electron"];
35
+
36
+ const failures = [];
37
+
38
+ for (const entry of readdirSync(repo)) {
39
+ if (entry.endsWith(".py") && !ALLOWED_ROOT_MODULES.has(entry)) {
40
+ failures.push(`root module reappeared: ${entry} — put code in latticeai/ or lattice_brain/`);
41
+ }
42
+ }
43
+ for (const dir of REMOVED_ROOT_DIRS) {
44
+ if (existsSync(join(repo, dir))) {
45
+ failures.push(`removed root package reappeared: ${dir}/ — use latticeai.${dir} instead`);
46
+ }
47
+ }
48
+
49
+ const modulePattern = REMOVED_MODULES.join("|");
50
+ const pyImport = new RegExp(`^\\s*(?:import\\s+(?:${modulePattern})\\b|from\\s+(?:${modulePattern})\\s+import\\b)`);
51
+ const jsSpawn = new RegExp(`(?:${modulePattern})\\.py\\b`);
52
+
53
+ function* walk(dir) {
54
+ for (const entry of readdirSync(dir)) {
55
+ if (entry === "__pycache__" || entry === "node_modules" || entry === ".venv") continue;
56
+ const full = join(dir, entry);
57
+ const st = statSync(full);
58
+ if (st.isDirectory()) yield* walk(full);
59
+ else yield full;
60
+ }
61
+ }
62
+
63
+ for (const tree of SOURCE_TREES) {
64
+ const base = join(repo, tree);
65
+ if (!existsSync(base)) continue;
66
+ for (const file of walk(base)) {
67
+ const rel = relative(repo, file);
68
+ if (/\.py$/.test(file)) {
69
+ const lines = readFileSync(file, "utf8").split("\n");
70
+ lines.forEach((line, i) => {
71
+ if (pyImport.test(line)) {
72
+ failures.push(`${rel}:${i + 1} imports a removed root shim: ${line.trim()}`);
73
+ }
74
+ });
75
+ } else if (/\.(mjs|cjs|js)$/.test(file) && !/\.min\.js$/.test(file)) {
76
+ const lines = readFileSync(file, "utf8").split("\n");
77
+ lines.forEach((line, i) => {
78
+ if (jsSpawn.test(line) && !line.includes("check_legacy_debt")) {
79
+ failures.push(`${rel}:${i + 1} references a removed root shim file: ${line.trim()}`);
80
+ }
81
+ });
82
+ }
83
+ }
84
+ }
85
+
86
+ if (failures.length > 0) {
87
+ console.error("legacy debt gate FAILED:");
88
+ for (const failure of failures) console.error(` - ${failure}`);
89
+ process.exit(1);
90
+ }
91
+ console.log("legacy debt gate ok: root shim layer stays removed (server.py only)");
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ // Retention policy for output/release/: keep the newest N versioned evidence
3
+ // directories (default 3, override with LTCAI_RELEASE_EVIDENCE_KEEP or argv).
4
+ // Older captures are reproducible on demand via `npm run release:evidence`,
5
+ // so keeping the full history only burns disk (13 versions ≈ 90MB).
6
+ import { existsSync, readdirSync, rmSync, statSync } from "node:fs";
7
+ import { join } from "node:path";
8
+
9
+ const repo = join(import.meta.dirname, "..");
10
+ const releaseDir = join(repo, "output", "release");
11
+ const keep = Math.max(1, Number(process.argv[2] || process.env.LTCAI_RELEASE_EVIDENCE_KEEP || 3));
12
+
13
+ if (!existsSync(releaseDir)) {
14
+ console.log(`release evidence: nothing to prune (${releaseDir} missing)`);
15
+ process.exit(0);
16
+ }
17
+
18
+ const semver = (name) => {
19
+ const m = /^v(\d+)\.(\d+)\.(\d+)$/.exec(name);
20
+ return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
21
+ };
22
+
23
+ const versioned = readdirSync(releaseDir)
24
+ .filter((name) => semver(name) && statSync(join(releaseDir, name)).isDirectory())
25
+ .sort((a, b) => {
26
+ const [a1, a2, a3] = semver(a);
27
+ const [b1, b2, b3] = semver(b);
28
+ return b1 - a1 || b2 - a2 || b3 - a3;
29
+ });
30
+
31
+ const stale = versioned.slice(keep);
32
+ for (const name of stale) {
33
+ rmSync(join(releaseDir, name), { recursive: true, force: true });
34
+ console.log(`release evidence: pruned ${name}`);
35
+ }
36
+ console.log(
37
+ `release evidence: keeping ${Math.min(keep, versioned.length)} of ${versioned.length} versions (policy: newest ${keep})`,
38
+ );
@@ -32,7 +32,6 @@ REPO_ROOT = Path(__file__).resolve().parents[1]
32
32
 
33
33
  # Every importable module the wheel ships (pyproject py-modules + packages).
34
34
  WHEEL_MODULES = [
35
- "setup_wizard",
36
35
  "lattice_brain",
37
36
  "lattice_brain.graph",
38
37
  "lattice_brain.graph.store",
@@ -55,9 +54,18 @@ WHEEL_MODULES = [
55
54
  "latticeai.services.p_reinforce",
56
55
  "latticeai.tools",
57
56
  "latticeai.tools.knowledge",
57
+ "latticeai.setup.auto_setup",
58
+ "latticeai.setup.wizard",
59
+ "latticeai.services.local_knowledge",
60
+ "lattice_brain.graph.schema",
61
+ "server",
62
+ ]
63
+
64
+ # Root shims removed in 9.9.1 — the wheel must NOT ship them anymore.
65
+ REMOVED_ROOT_MODULES = [
58
66
  "ltcai_cli",
59
67
  "auto_setup",
60
- "server",
68
+ "setup_wizard",
61
69
  "mcp_registry",
62
70
  "kg_schema",
63
71
  "knowledge_graph",
@@ -67,15 +75,23 @@ WHEEL_MODULES = [
67
75
  "p_reinforce",
68
76
  "telegram_bot",
69
77
  "tools",
70
- "tools.knowledge",
71
78
  ]
72
79
 
73
80
  IMPORT_CHECK = (
74
81
  "import importlib\n"
75
82
  + "".join(f"importlib.import_module({mod!r})\n" for mod in WHEEL_MODULES)
76
- + "assert importlib.import_module('tools') is importlib.import_module('latticeai.tools')\n"
77
- + "assert importlib.import_module('tools.knowledge') is importlib.import_module('latticeai.tools.knowledge')\n"
78
- + f"print('wheel imports ok: {len(WHEEL_MODULES)} modules')\n"
83
+ + "".join(
84
+ f"""
85
+ try:
86
+ importlib.import_module({mod!r})
87
+ except ImportError:
88
+ pass
89
+ else:
90
+ raise AssertionError('removed root shim {mod} is still shipped in the wheel')
91
+ """
92
+ for mod in REMOVED_ROOT_MODULES
93
+ )
94
+ + f"print('wheel imports ok: {len(WHEEL_MODULES)} modules, {len(REMOVED_ROOT_MODULES)} shims gone')\n"
79
95
  )
80
96
 
81
97
  HEALTH_CHECK = """
@@ -1584,7 +1584,7 @@ dependencies = [
1584
1584
 
1585
1585
  [[package]]
1586
1586
  name = "lattice-ai-desktop"
1587
- version = "9.9.0"
1587
+ version = "9.9.1"
1588
1588
  dependencies = [
1589
1589
  "plist",
1590
1590
  "serde",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "lattice-ai-desktop"
3
- version = "9.9.0"
3
+ version = "9.9.1"
4
4
  description = "Lattice AI Digital Brain desktop shell"
5
5
  authors = ["TaeSoo Park"]
6
6
  edition = "2021"
@@ -164,9 +164,9 @@ fn resource_dir() -> Option<PathBuf> {
164
164
  fn bundled_python_root() -> Option<PathBuf> {
165
165
  let resources = resource_dir()?;
166
166
  let up = resources.join("_up_");
167
- if up.join("ltcai_cli.py").is_file() {
167
+ if up.join("latticeai").join("cli").join("entrypoint.py").is_file() {
168
168
  Some(up)
169
- } else if resources.join("ltcai_cli.py").is_file() {
169
+ } else if resources.join("latticeai").join("cli").join("entrypoint.py").is_file() {
170
170
  Some(resources)
171
171
  } else {
172
172
  None
@@ -222,13 +222,13 @@ fn backend_launch(origin: &str) -> BackendLaunch {
222
222
  }
223
223
 
224
224
  for python in python_candidates() {
225
- if module_importable(&python, "ltcai_cli") {
225
+ if module_importable(&python, "latticeai.cli.entrypoint") {
226
226
  return BackendLaunch {
227
- command: format!("{python} -m ltcai_cli --host 127.0.0.1 --port {port}"),
227
+ command: format!("{python} -m latticeai.cli.entrypoint --host 127.0.0.1 --port {port}"),
228
228
  program: python,
229
229
  args: vec![
230
230
  "-m".into(),
231
- "ltcai_cli".into(),
231
+ "latticeai.cli.entrypoint".into(),
232
232
  "--host".into(),
233
233
  "127.0.0.1".into(),
234
234
  "--port".into(),
@@ -240,27 +240,27 @@ fn backend_launch(origin: &str) -> BackendLaunch {
240
240
  }
241
241
 
242
242
  if let Some(resources) = bundled_python_root() {
243
- let launcher = resources.join("ltcai_cli.py");
244
- if launcher.is_file() {
243
+ if resources.join("latticeai").join("cli").join("entrypoint.py").is_file() {
245
244
  if let Some(python) = python_candidates().into_iter().next() {
246
245
  return BackendLaunch {
247
- command: format!("{python} {} --host 127.0.0.1 --port {port}", launcher.display()),
246
+ command: format!("{python} -m latticeai.cli.entrypoint --host 127.0.0.1 --port {port}"),
248
247
  program: python,
249
248
  args: vec![
250
- launcher.to_string_lossy().to_string(),
249
+ "-m".into(),
250
+ "latticeai.cli.entrypoint".into(),
251
251
  "--host".into(),
252
252
  "127.0.0.1".into(),
253
253
  "--port".into(),
254
254
  port,
255
255
  ],
256
- cwd: None,
256
+ cwd: Some(resources),
257
257
  };
258
258
  }
259
259
  }
260
260
  }
261
261
 
262
262
  BackendLaunch {
263
- command: "unavailable: LTCAI executable or importable ltcai_cli module not found".to_string(),
263
+ command: "unavailable: LTCAI executable or importable latticeai.cli.entrypoint module not found".to_string(),
264
264
  program: String::new(),
265
265
  args: Vec::new(),
266
266
  cwd: None,
@@ -278,7 +278,7 @@ fn spawn_backend(origin: &str, launch: &BackendLaunch) -> Result<Option<Child>,
278
278
  return Ok(None);
279
279
  }
280
280
  if launch.program.is_empty() {
281
- return Err("Desktop backend unavailable: LTCAI executable or importable ltcai_cli module not found.".to_string());
281
+ return Err("Desktop backend unavailable: LTCAI executable or importable latticeai.cli.entrypoint module not found.".to_string());
282
282
  }
283
283
 
284
284
  let mut cmd = Command::new(&launch.program);