ltcai 11.7.0 → 12.0.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 (174) hide show
  1. package/README.md +100 -76
  2. package/docs/BENCHMARKS.md +9 -2
  3. package/docs/CHANGELOG.md +249 -0
  4. package/docs/CI_AND_RELEASE_GATES.md +126 -41
  5. package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
  6. package/docs/DEVELOPMENT.md +271 -103
  7. package/docs/ENTERPRISE.md +1 -1
  8. package/docs/LEGACY_COMPATIBILITY.md +10 -6
  9. package/docs/MULTI_AGENT_RUNTIME.md +4 -4
  10. package/docs/ONBOARDING.md +16 -4
  11. package/docs/OPERATIONS.md +14 -1
  12. package/docs/PERMISSION_MODE.md +14 -9
  13. package/docs/REALTIME_COLLABORATION.md +1 -1
  14. package/docs/ROADMAP.md +113 -0
  15. package/docs/TRUST_MODEL.md +28 -7
  16. package/docs/USABILITY_AUDIT.md +5 -0
  17. package/docs/WHY_LATTICE.md +13 -5
  18. package/docs/WORKFLOW_DESIGNER.md +2 -2
  19. package/docs/kg-schema.md +57 -7
  20. package/docs/mcp-tools.md +93 -82
  21. package/docs/security-model.md +6 -3
  22. package/lattice_brain/__init__.py +1 -1
  23. package/lattice_brain/graph/_kg_common/__init__.py +1 -54
  24. package/lattice_brain/graph/_kg_common/extraction.py +459 -105
  25. package/lattice_brain/graph/_kg_common/normalize.py +305 -0
  26. package/lattice_brain/graph/_kg_common/patterns.py +275 -0
  27. package/lattice_brain/graph/_kg_common/relations.py +12 -3
  28. package/lattice_brain/graph/_kg_common/sections.py +107 -0
  29. package/lattice_brain/graph/_kg_common/text.py +14 -450
  30. package/lattice_brain/graph/_kg_constants.py +7 -0
  31. package/lattice_brain/ingestion/__init__.py +6 -3
  32. package/lattice_brain/multimodal/__init__.py +9 -3
  33. package/latticeai/__init__.py +1 -1
  34. package/latticeai/api/agent_worker_seam.py +44 -1
  35. package/latticeai/api/models.py +18 -110
  36. package/latticeai/api/search.py +7 -30
  37. package/latticeai/api/worker_compute.py +127 -106
  38. package/latticeai/api/worker_seams.py +17 -2
  39. package/latticeai/core/embedding_providers/__init__.py +16 -0
  40. package/latticeai/core/embedding_providers/autodetect.py +302 -0
  41. package/latticeai/core/embedding_providers/base.py +25 -0
  42. package/latticeai/core/embedding_providers/profiles.py +44 -0
  43. package/latticeai/core/embedding_providers/text.py +74 -8
  44. package/latticeai/core/http_origin.py +3 -3
  45. package/latticeai/core/messages.py +0 -5
  46. package/latticeai/core/policy.py +1 -6
  47. package/latticeai/core/quiet.py +1 -20
  48. package/latticeai/core/security.py +29 -83
  49. package/latticeai/core/sessions.py +95 -4
  50. package/latticeai/core/users.py +0 -38
  51. package/latticeai/core/vector_index/__init__.py +61 -0
  52. package/latticeai/core/vector_index/hnsw.py +383 -0
  53. package/latticeai/core/vector_index/sidecar.py +329 -0
  54. package/latticeai/models/router/catalog.py +2 -2
  55. package/latticeai/models/router/generation.py +176 -30
  56. package/latticeai/models/router/loading.py +150 -9
  57. package/latticeai/runtime/access_runtime.py +7 -4
  58. package/latticeai/runtime/brain_runtime.py +43 -9
  59. package/latticeai/runtime/build_phases/features.py +8 -31
  60. package/latticeai/runtime/build_phases/foundation.py +7 -16
  61. package/latticeai/runtime/build_phases/web.py +3 -3
  62. package/latticeai/runtime/build_phases/worker_profile.py +29 -27
  63. package/latticeai/runtime/runtime_context.py +0 -2
  64. package/latticeai/services/architecture_readiness.py +18 -19
  65. package/latticeai/services/process_audit.py +1 -22
  66. package/latticeai/services/product_readiness.py +39 -12
  67. package/latticeai/services/search_service.py +7 -0
  68. package/latticeai/services/voice_capture.py +8 -28
  69. package/latticeai/tools/__init__.py +12 -47
  70. package/latticeai/tools/commands.py +9 -15
  71. package/latticeai/tools/documents.py +12 -0
  72. package/latticeai/tools/knowledge.py +0 -6
  73. package/latticeai/tools/markup.py +152 -0
  74. package/package.json +4 -5
  75. package/requirements.txt +0 -1
  76. package/scripts/check_current_release_docs.mjs +1 -1
  77. package/scripts/check_openapi_drift.mjs +3 -2
  78. package/scripts/check_server_i18n.mjs +5 -4
  79. package/scripts/compose_openapi.py +4 -1
  80. package/scripts/export_openapi.py +5 -4
  81. package/scripts/gen_worker_allowlist_fixture.py +2 -2
  82. package/scripts/openapi_route_families.json +19 -74
  83. package/scripts/publish_release.mjs +157 -0
  84. package/scripts/release_screen_claims.json +144 -28
  85. package/src-tauri/Cargo.lock +45 -10
  86. package/src-tauri/Cargo.toml +1 -1
  87. package/src-tauri/tauri.conf.json +1 -1
  88. package/static/app/asset-manifest.json +47 -41
  89. package/static/app/assets/Act-Cf1L2709.js +2 -0
  90. package/static/app/assets/AdminConsole-DPAbLTYV.js +1 -0
  91. package/static/app/assets/Brain-DqamGrj-.js +2 -0
  92. package/static/app/assets/BrainHome-MHe2_RYs.js +2 -0
  93. package/static/app/assets/BrainSignals-CQPPfyyH.js +1 -0
  94. package/static/app/assets/Capture-DGdIH_Zc.js +1 -0
  95. package/static/app/assets/Chronicle-C-UlCJoJ.js +1 -0
  96. package/static/app/assets/CommandPalette-WNT4EqUX.js +1 -0
  97. package/static/app/assets/DigitalBrainExplorer-CEBH5Cwc.js +321 -0
  98. package/static/app/assets/Library-C6xd1dlf.js +1 -0
  99. package/static/app/assets/LivingBrain-BEk-0ohw.js +1 -0
  100. package/static/app/assets/ProductFlow-CZLm5iXh.js +1 -0
  101. package/static/app/assets/QueryClientProvider-B3OjqSyJ.js +1 -0
  102. package/static/app/assets/{ReviewCard-HXRle3qq.js → ReviewCard-CEHG6evf.js} +2 -2
  103. package/static/app/assets/RunsListPanel-CLtEJSRW.js +1 -0
  104. package/static/app/assets/System-CAxwBUXw.js +1 -0
  105. package/static/app/assets/WorkflowGraph-Dj10RuGE.js +1 -0
  106. package/static/app/assets/WorkflowsPanel-Kyeh_LIT.js +2 -0
  107. package/static/app/assets/actHelpers-CtSmK9Dw.js +1 -0
  108. package/static/app/assets/arrow-left-CRl5EO4D.js +1 -0
  109. package/static/app/assets/{bot-Cn8bWRuq.js → bot-DhUGRel2.js} +1 -1
  110. package/static/app/assets/brain-CLkhHsHF.js +1 -0
  111. package/static/app/assets/button-CmaEqG1T.js +1 -0
  112. package/static/app/assets/circle-check-CFgejkOS.js +1 -0
  113. package/static/app/assets/{circle-pause-CmzC_apg.js → circle-pause-l96izbxj.js} +1 -1
  114. package/static/app/assets/{circle-play-D8mW2aQ7.js → circle-play-CrZa25_q.js} +1 -1
  115. package/static/app/assets/{cpu-DZcdd0PZ.js → cpu-BaXudqwl.js} +1 -1
  116. package/static/app/assets/{download-bv1KEPGQ.js → download-hCVFPiyc.js} +1 -1
  117. package/static/app/assets/{folder-open-d-Pip5gr.js → folder-open-CHL82Yp7.js} +1 -1
  118. package/static/app/assets/{hard-drive-D20iavUb.js → hard-drive-DDzET7lk.js} +1 -1
  119. package/static/app/assets/index-CB93CZWW.css +2 -0
  120. package/static/app/assets/index-D2H-wSl6.js +13 -0
  121. package/static/app/assets/input-Df1CAY_I.js +1 -0
  122. package/static/app/assets/jsx-runtime-bzQ4Vb5N.js +1 -0
  123. package/static/app/assets/{link-2-BPJOFlAy.js → link-2-xNnTIX1_.js} +1 -1
  124. package/static/app/assets/{permissionCopy-ChdJd493.js → permissionCopy-D3aWHco-.js} +1 -1
  125. package/static/app/assets/primitives-BioD2slS.js +1 -0
  126. package/static/app/assets/search-BzBw8YcW.js +1 -0
  127. package/static/app/assets/{share-2-YNX_NtMU.js → share-2-FkzGf8Df.js} +1 -1
  128. package/static/app/assets/{shield-alert-DuQ3zrVL.js → shield-alert-B3dwzik4.js} +1 -1
  129. package/static/app/assets/sourceMeta-DQSY_tah.js +1 -0
  130. package/static/app/assets/textarea-P8o6pvOP.js +1 -0
  131. package/static/app/assets/useFocusTrap-hswOIkXE.js +1 -0
  132. package/static/app/assets/useMutation-OJLrYSRA.js +1 -0
  133. package/static/app/assets/workspace-BCuk3Ku9.js +1 -0
  134. package/static/app/index.html +4 -4
  135. package/static/sw.js +1 -1
  136. package/lattice_brain/ingestion/pipeline.py +0 -108
  137. package/latticeai/api/local_files.py +0 -44
  138. package/latticeai/api/tools.py +0 -126
  139. package/latticeai/api/voice_capture.py +0 -32
  140. package/latticeai/core/agent_permission.py +0 -85
  141. package/scripts/agent_eval.py +0 -34
  142. package/scripts/brain_quality_eval.py +0 -37
  143. package/scripts/check_legacy_debt.mjs +0 -91
  144. package/scripts/check_python.py +0 -100
  145. package/scripts/chunking_parity_corpus.py +0 -449
  146. package/scripts/generate_agent_parity_fixtures.py +0 -771
  147. package/scripts/generate_chunking_parity_fixtures.py +0 -259
  148. package/static/app/assets/Act-BPcVAbOL.js +0 -1
  149. package/static/app/assets/AdminConsole-Bw1ATQL0.js +0 -1
  150. package/static/app/assets/Brain-CT92Kos0.js +0 -321
  151. package/static/app/assets/BrainHome-CFBkt1K_.js +0 -2
  152. package/static/app/assets/BrainSignals-ReLWF2H8.js +0 -1
  153. package/static/app/assets/Capture-BsTokYkk.js +0 -1
  154. package/static/app/assets/Chronicle-B6f0T9id.js +0 -1
  155. package/static/app/assets/CommandPalette-CuvjTv1u.js +0 -1
  156. package/static/app/assets/Library-BGJbG9Hd.js +0 -1
  157. package/static/app/assets/LivingBrain-DGYK_Jsa.js +0 -1
  158. package/static/app/assets/ProductFlow-DXBC6brE.js +0 -1
  159. package/static/app/assets/System-CMHSO9qM.js +0 -1
  160. package/static/app/assets/arrow-left-BfmkskWx.js +0 -1
  161. package/static/app/assets/brain-CQJberbE.js +0 -1
  162. package/static/app/assets/button-Ct9f2_oT.js +0 -1
  163. package/static/app/assets/circle-check-DruOxB-4.js +0 -1
  164. package/static/app/assets/index-D9x-kSNy.css +0 -2
  165. package/static/app/assets/index-Do83hDzJ.js +0 -10
  166. package/static/app/assets/input-BLXVNmj1.js +0 -1
  167. package/static/app/assets/primitives-Cv5tbZBY.js +0 -1
  168. package/static/app/assets/search-CT9aho2j.js +0 -1
  169. package/static/app/assets/textarea-DqwLnli4.js +0 -1
  170. package/static/app/assets/useFocusTrap-ZVI98jaW.js +0 -1
  171. package/static/app/assets/useMutation-CVC4qv_D.js +0 -1
  172. package/static/app/assets/useQuery-C7BeG4HU.js +0 -1
  173. package/static/app/assets/utils-CiFtIdZq.js +0 -4
  174. package/static/app/assets/workspace-DQz9vIId.js +0 -1
@@ -1,126 +0,0 @@
1
- """The document parser routes — the two ``/tools/*`` that are pure compute.
2
-
3
- This module used to be the direct tool surface: ~30 ``/tools/*`` routes, the
4
- upload door, the MCP catalogue, computer-use, the permission gateway and the
5
- local-file browser, all composed into one router. Every one of those is a write,
6
- an OS actuator, an exec, or platform state, and v11.6.0 serves them from
7
- ``lattice-host``.
8
-
9
- Two survive, because parsing a document is compute and nothing else:
10
-
11
- ``POST /tools/read_document``
12
- Text plus metadata out of a ``.pdf`` / ``.docx`` / ``.xlsx`` / ``.pptx`` /
13
- plain-text file. The same parser matrix ``POST /worker/parse`` wraps.
14
-
15
- ``GET /tools/pdf_pages``
16
- Up to twenty PDF pages rasterised to base64 PNG through pypdfium2.
17
-
18
- **The approval hop is gone, and the confinement is now absolute.** Both routes
19
- used to accept an approval token minted by ``/permissions/*`` and, with one,
20
- read a file anywhere on the disk. ``/permissions/*`` is a native platform route
21
- now and the approval queue is native state, so a token this process is handed is
22
- one it cannot verify — and a check against an always-empty local queue is not a
23
- weaker guard, it is a guard that refuses everything while looking like it works.
24
- Both routes therefore refuse outright outside ``AGENT_ROOT``. Reading an
25
- arbitrary path on the user's behalf is the host's call to make, with the
26
- approval record it actually holds.
27
- """
28
-
29
- from __future__ import annotations
30
-
31
- import base64
32
- import io
33
- import logging
34
- from pathlib import Path
35
- from typing import Optional
36
-
37
- from fastapi import APIRouter, Request
38
- from pydantic import BaseModel
39
-
40
- from lattice_brain.runtime.hooks import dispatch_tool
41
- from latticeai.core.messages import http_error, resolve_language
42
- from latticeai.tools import AGENT_ROOT, ToolError, read_document
43
-
44
-
45
- class ToolPathRequest(BaseModel):
46
- path: str = "."
47
- approval_token: Optional[str] = None
48
-
49
-
50
- def create_tools_router(*, require_user) -> APIRouter:
51
- router = APIRouter()
52
-
53
- def _confined(request: Request, raw: str) -> Path:
54
- """Resolve ``raw`` under ``AGENT_ROOT`` or refuse.
55
-
56
- Relative paths are taken from the workspace root, which is what every
57
- caller of these two routes sends. An absolute path is accepted only when
58
- it is already inside the workspace, so ``../`` and ``/etc/passwd`` reach
59
- the same refusal by the same rule.
60
- """
61
- candidate = Path(raw).expanduser()
62
- target = (
63
- candidate.resolve()
64
- if candidate.is_absolute()
65
- else (AGENT_ROOT / candidate).resolve()
66
- )
67
- if target != AGENT_ROOT and AGENT_ROOT not in target.parents:
68
- raise http_error(403, "tools.path_outside_workspace", resolve_language(request))
69
- return target
70
-
71
- @router.post("/tools/read_document")
72
- async def tools_read_document(req: ToolPathRequest, request: Request):
73
- current_user = require_user(request)
74
- target = _confined(request, req.path)
75
- try:
76
- result = dispatch_tool(
77
- None,
78
- "read_document",
79
- {"path": str(target)},
80
- lambda: read_document(str(target)),
81
- user_email=current_user,
82
- source="workspace",
83
- )
84
- except ToolError as exc:
85
- raise http_error(400, "worker_compute.parse_failed", resolve_language(request)) from exc
86
- return {"status": "ok", "workspace": str(AGENT_ROOT), "result": result}
87
-
88
- @router.get("/tools/pdf_pages")
89
- async def tools_pdf_pages(path: str, request: Request, approval_token: Optional[str] = None):
90
- """Render PDF pages as base64 PNG images using pypdfium2 (Apache-2.0)."""
91
- require_user(request)
92
- # Accepted and ignored: the host still sends the token it minted, and
93
- # rejecting the request for carrying one would break a caller for a
94
- # field this process no longer has any way to check.
95
- _ = approval_token
96
- target = _confined(request, path)
97
- if not target.exists() or not target.is_file():
98
- raise http_error(404, "common.file_not_found", resolve_language(request))
99
- import pypdfium2 as pdfium
100
- doc = None
101
- try:
102
- doc = pdfium.PdfDocument(str(target))
103
- total = len(doc)
104
- pages = []
105
- for i in range(min(total, 20)): # 최대 20페이지
106
- page = doc[i]
107
- bitmap = page.render(scale=1.5)
108
- pil_image = bitmap.to_pil()
109
- buf = io.BytesIO()
110
- pil_image.save(buf, format="PNG")
111
- b64 = base64.b64encode(buf.getvalue()).decode()
112
- pages.append({"page": i + 1, "b64": b64})
113
- return {"total": total, "pages": pages}
114
- except Exception as e:
115
- raise http_error(500, "tools.pdf_render_failed", resolve_language(request), reason=e)
116
- finally:
117
- if doc is not None:
118
- try:
119
- doc.close()
120
- except Exception as e:
121
- logging.warning("pypdfium2 doc close failed: %s", e)
122
-
123
- return router
124
-
125
-
126
- __all__ = ["ToolPathRequest", "create_tools_router"]
@@ -1,32 +0,0 @@
1
- """Voice capability router (v9.9.7).
2
-
3
- ``GET /api/capture/voice/status`` — what this machine can actually do with a
4
- voice memo. Capture and storage are native (``POST /api/capture/voice`` moved
5
- to ``lattice-platform`` in §W3b, and calls ``POST /worker/asr`` for the
6
- transcript); whether a local transcriber exists at all is a fact about this
7
- process, so the probe stays here.
8
- """
9
-
10
- from __future__ import annotations
11
-
12
- from typing import Any, Callable
13
-
14
- from fastapi import APIRouter, Request
15
-
16
-
17
- def create_voice_capture_router(
18
- *,
19
- service: Any,
20
- require_user: Callable[[Request], Any],
21
- ) -> APIRouter:
22
- router = APIRouter()
23
-
24
- @router.get("/api/capture/voice/status")
25
- async def voice_status(request: Request):
26
- require_user(request)
27
- return service.status()
28
-
29
- return router
30
-
31
-
32
- __all__ = ["create_voice_capture_router"]
@@ -1,85 +0,0 @@
1
- """Agent-loop permission-mode gates (v9.9.8) — the two the parity fixtures pin.
2
-
3
- These were the Python loop's mode tables, called by ``SingleAgentRuntime`` and
4
- ``build_agent_runtime``. Orchestration moved to ``lattice-agent`` in v11.6.0 and
5
- the Python loop is gone, so **nothing in the worker imports this module**.
6
-
7
- It survives for one reason, stated so it is not read as a leftover:
8
- ``scripts/generate_agent_parity_fixtures.py`` produces the committed goldens
9
- ``rust/fixtures`` holds for the Rust loop's permission gates, and it derives
10
- them from :func:`block_reason_for_tool` and :func:`non_auto_plan_steps` here.
11
- Deleting these two would leave the Rust side asserting against fixtures no
12
- Python could regenerate — the fixtures would still pass, and would stop meaning
13
- anything. The rest of the module (``call_mode_source``, ``resolve_deps_mode``,
14
- ``approval_requirements_for``) went with the loop that called it.
15
- """
16
-
17
- from __future__ import annotations
18
-
19
- from typing import Any, List, Mapping, Optional, Sequence
20
-
21
- from latticeai.core.permission_mode import (
22
- PermissionMode,
23
- effective_auto_approve,
24
- is_circuit_breaker,
25
- normalize_mode,
26
- )
27
-
28
-
29
- def non_auto_plan_steps(
30
- mode: PermissionMode | str,
31
- steps: Sequence[Mapping[str, Any]],
32
- tool_governance: Mapping[str, Mapping[str, Any]],
33
- *,
34
- governed_tools: Optional[Any] = None,
35
- ) -> List[Any]:
36
- """Plan steps that still need approval under the active mode."""
37
- mode = normalize_mode(mode)
38
- governed = frozenset(governed_tools or ())
39
- non_auto: List[Any] = []
40
- for step in steps:
41
- name = step.get("action")
42
- if not name:
43
- continue
44
- if name in governed and mode == PermissionMode.STRICT:
45
- # Strict keeps governor tools out of the plan-level block (per-call).
46
- continue
47
- policy = dict(tool_governance.get(name) or {
48
- "auto_approve": False, "risk": "write", "destructive": False,
49
- "shell": False, "network": False, "sandbox": "workspace", "rollback": "none",
50
- })
51
- if effective_auto_approve(mode, str(name), policy):
52
- continue
53
- non_auto.append(name)
54
- return non_auto
55
-
56
-
57
- def block_reason_for_tool(
58
- mode: PermissionMode | str,
59
- name: str,
60
- policy: Mapping[str, Any],
61
- args: Mapping[str, Any],
62
- *,
63
- approved_by_human: bool = False,
64
- governor_allows_additive: bool = False,
65
- ) -> Optional[str]:
66
- """Return a block reason, or None when the call may proceed."""
67
- mode = normalize_mode(mode)
68
- breaker = is_circuit_breaker(name, policy, args)
69
- if breaker:
70
- return f"BLOCKED: {breaker}"
71
- if policy.get("risk") == "destructive" or policy.get("destructive"):
72
- return f"BLOCKED: destructive action '{name}' not permitted in agent mode."
73
- if approved_by_human or governor_allows_additive:
74
- return None
75
- if effective_auto_approve(mode, name, policy, args=args):
76
- return None
77
- if policy.get("auto_approve"):
78
- return None
79
- return f"BLOCKED: action '{name}' requires explicit approval (mode={mode.value})."
80
-
81
-
82
- __all__ = [
83
- "block_reason_for_tool",
84
- "non_auto_plan_steps",
85
- ]
@@ -1,34 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Agent-loop evaluation gate — native since v11.6.0.
3
-
4
- The deterministic scenario suite used to live in
5
- ``latticeai.core.agent_eval`` and drive ``SingleAgentRuntime``. That loop is
6
- ``lattice-agent`` now. CI still invokes this path; we verify the native suite
7
- is present rather than importing the deleted Python runtime.
8
-
9
- Last Python generating tree: commit fc65e60.
10
- Goldens: ``rust/fixtures/agent_loop/golden/`` (FROZEN).
11
- """
12
-
13
- from __future__ import annotations
14
-
15
- from pathlib import Path
16
-
17
- REPO = Path(__file__).resolve().parents[1]
18
- REQUIRED = (
19
- REPO / "rust" / "lattice-agent" / "tests" / "agent_loop.rs",
20
- REPO / "rust" / "fixtures" / "agent_loop" / "golden",
21
- )
22
-
23
-
24
- def main() -> int:
25
- missing = [str(path.relative_to(REPO)) for path in REQUIRED if not path.exists()]
26
- if missing:
27
- print("agent-loop-eval: FAIL missing native artifacts:", missing)
28
- return 1
29
- print("agent-loop-eval: native lattice-agent suite present (Python loop retired)")
30
- return 0
31
-
32
-
33
- if __name__ == "__main__":
34
- raise SystemExit(main())
@@ -1,37 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Brain recall / KG quality eval — native since v11.6.0.
3
-
4
- This used to import ``KnowledgeGraphStore``, ``RetrievalBenchmarkRunner`` and
5
- ``MemoryService`` and run the Python retrieval bench. Retrieval and the
6
- store are ``lattice-retrieval`` / ``lattice-core`` now. CI still invokes
7
- this path; we verify the native goldens and the worker embedder report
8
- are present.
9
-
10
- Last Python generating tree: commit fc65e60.
11
- Goldens: ``rust/fixtures/golden/`` (FROZEN).
12
- """
13
-
14
- from __future__ import annotations
15
-
16
- from pathlib import Path
17
-
18
- REPO = Path(__file__).resolve().parents[1]
19
- REQUIRED = (
20
- REPO / "rust" / "fixtures" / "golden" / "manifest.json",
21
- REPO / "rust" / "fixtures" / "parity_store.sqlite",
22
- REPO / "latticeai" / "api" / "search.py",
23
- REPO / "latticeai" / "services" / "search_service.py",
24
- )
25
-
26
-
27
- def main() -> int:
28
- missing = [str(path.relative_to(REPO)) for path in REQUIRED if not path.exists()]
29
- if missing:
30
- print("brain-quality-eval: FAIL missing native artifacts:", missing)
31
- return 1
32
- print("brain-quality-eval: native retrieval goldens present (Python store retired)")
33
- return 0
34
-
35
-
36
- if __name__ == "__main__":
37
- raise SystemExit(main())
@@ -1,91 +0,0 @@
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"];
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)");
@@ -1,100 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Discover-and-syntax-check every first-party Python module.
3
-
4
- Replaces the hand-maintained ``py_compile`` enumeration in CI and
5
- ``package.json``: walks the repository, skips vendored / virtualenv / build /
6
- cache / generated directories, and checks everything that remains. New
7
- modules are picked up automatically — there is nothing to update when a file is
8
- added, so the syntax gate can never silently fall behind the codebase.
9
-
10
- Unlike ``py_compile``, this script does not write ``.pyc`` files or create
11
- ``__pycache__`` directories. Validation should not dirty a developer's working
12
- tree or leave generated files for later lint passes to trip over.
13
-
14
- Usage::
15
-
16
- python scripts/check_python.py # syntax-check all discovered modules
17
- python scripts/check_python.py --list # just print what would be checked
18
-
19
- Exit code is non-zero if any module fails to parse.
20
- """
21
-
22
- from __future__ import annotations
23
-
24
- import sys
25
- from pathlib import Path
26
-
27
- ROOT = Path(__file__).resolve().parent.parent
28
-
29
- # Directory names excluded anywhere in the tree: virtualenvs, build/cache
30
- # artifacts, generated agent output, and vendored snapshots of older releases.
31
- EXCLUDE_DIRS = {
32
- ".git",
33
- ".venv",
34
- "venv",
35
- "env",
36
- ".build-venv",
37
- ".npm-cache",
38
- "build",
39
- "dist",
40
- "node_modules",
41
- "__pycache__",
42
- ".pytest_cache",
43
- ".mypy_cache",
44
- ".ruff_cache",
45
- "agent_workspace",
46
- "outputs",
47
- "playwright-report",
48
- "test-results",
49
- "ltcai.egg-info",
50
- "output",
51
- ".ltcai",
52
- ".ltcai-brain",
53
- ".ltcai-test",
54
- # Vendored snapshot of an older packaged release — not part of the build.
55
- "ltcai-0.3.1",
56
- }
57
-
58
-
59
- def iter_modules():
60
- for path in ROOT.rglob("*.py"):
61
- parts = path.relative_to(ROOT).parts
62
- if any(part in EXCLUDE_DIRS for part in parts):
63
- continue
64
- yield path
65
-
66
-
67
- def check_syntax(path: Path) -> str | None:
68
- """Return a human-readable failure string, or ``None`` when syntax is valid."""
69
-
70
- try:
71
- compile(path.read_bytes(), str(path), "exec", dont_inherit=True)
72
- except (OSError, SyntaxError, ValueError) as exc:
73
- return f"{path.relative_to(ROOT)}: {exc}"
74
- return None
75
-
76
-
77
- def main(argv: list[str]) -> int:
78
- modules = sorted(iter_modules())
79
- if "--list" in argv:
80
- for path in modules:
81
- print(path.relative_to(ROOT))
82
- return 0
83
-
84
- failures: list[str] = []
85
- for path in modules:
86
- failure = check_syntax(path)
87
- if failure:
88
- failures.append(failure)
89
-
90
- if failures:
91
- print("\n".join(failures))
92
- print(f"check:python FAILED — {len(failures)} of {len(modules)} module(s) did not parse")
93
- return 1
94
-
95
- print(f"check:python OK — syntax-checked {len(modules)} modules")
96
- return 0
97
-
98
-
99
- if __name__ == "__main__":
100
- raise SystemExit(main(sys.argv[1:]))