ltcai 11.7.0 → 11.9.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.
- package/README.md +73 -70
- package/docs/BENCHMARKS.md +9 -2
- package/docs/CHANGELOG.md +157 -0
- package/docs/CI_AND_RELEASE_GATES.md +126 -41
- package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
- package/docs/DEVELOPMENT.md +34 -14
- package/docs/LEGACY_COMPATIBILITY.md +10 -6
- package/docs/ONBOARDING.md +5 -3
- package/docs/OPERATIONS.md +5 -1
- package/docs/PERMISSION_MODE.md +14 -9
- package/docs/TRUST_MODEL.md +13 -6
- package/docs/USABILITY_AUDIT.md +5 -0
- package/docs/WHY_LATTICE.md +6 -4
- package/docs/kg-schema.md +7 -3
- package/docs/mcp-tools.md +73 -79
- package/docs/security-model.md +6 -3
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/graph/_kg_common/__init__.py +1 -54
- package/lattice_brain/graph/_kg_common/text.py +14 -450
- package/lattice_brain/ingestion/__init__.py +6 -3
- package/lattice_brain/multimodal/__init__.py +9 -3
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/agent_worker_seam.py +12 -1
- package/latticeai/api/models.py +18 -110
- package/latticeai/api/search.py +7 -30
- package/latticeai/api/worker_compute.py +53 -107
- package/latticeai/api/worker_seams.py +17 -2
- package/latticeai/core/http_origin.py +3 -3
- package/latticeai/core/messages.py +0 -5
- package/latticeai/core/policy.py +1 -6
- package/latticeai/core/quiet.py +1 -20
- package/latticeai/core/security.py +29 -83
- package/latticeai/core/sessions.py +95 -4
- package/latticeai/core/users.py +0 -38
- package/latticeai/models/router/catalog.py +2 -2
- package/latticeai/models/router/generation.py +81 -14
- package/latticeai/models/router/loading.py +41 -5
- package/latticeai/runtime/access_runtime.py +7 -4
- package/latticeai/runtime/build_phases/features.py +8 -31
- package/latticeai/runtime/build_phases/foundation.py +7 -16
- package/latticeai/runtime/build_phases/web.py +3 -3
- package/latticeai/runtime/build_phases/worker_profile.py +21 -28
- package/latticeai/runtime/runtime_context.py +0 -2
- package/latticeai/services/architecture_readiness.py +18 -19
- package/latticeai/services/process_audit.py +1 -22
- package/latticeai/services/product_readiness.py +33 -11
- package/latticeai/services/voice_capture.py +8 -28
- package/latticeai/tools/__init__.py +6 -46
- package/latticeai/tools/commands.py +9 -15
- package/latticeai/tools/knowledge.py +0 -6
- package/package.json +3 -5
- package/requirements.txt +0 -1
- package/scripts/check_current_release_docs.mjs +1 -1
- package/scripts/check_openapi_drift.mjs +3 -2
- package/scripts/check_server_i18n.mjs +5 -4
- package/scripts/compose_openapi.py +2 -1
- package/scripts/export_openapi.py +5 -4
- package/scripts/gen_worker_allowlist_fixture.py +2 -2
- package/scripts/openapi_route_families.json +14 -73
- package/scripts/release_screen_claims.json +131 -27
- package/src-tauri/Cargo.lock +11 -10
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/tauri.conf.json +1 -1
- package/static/app/asset-manifest.json +41 -41
- package/static/app/assets/{Act-BPcVAbOL.js → Act-B4WT81kh.js} +1 -1
- package/static/app/assets/AdminConsole--Wf71m-o.js +1 -0
- package/static/app/assets/{Brain-CT92Kos0.js → Brain-CtYaa26c.js} +2 -2
- package/static/app/assets/BrainHome-Be2VPJEc.js +2 -0
- package/static/app/assets/BrainSignals-C0__xgpG.js +1 -0
- package/static/app/assets/Capture-DdNi5Peb.js +1 -0
- package/static/app/assets/Chronicle-B3hNveeI.js +1 -0
- package/static/app/assets/CommandPalette-CIsnSsFL.js +1 -0
- package/static/app/assets/Library-Bl5XClFV.js +1 -0
- package/static/app/assets/LivingBrain-DjkTB_gh.js +1 -0
- package/static/app/assets/{ProductFlow-DXBC6brE.js → ProductFlow-DCUNRNHs.js} +1 -1
- package/static/app/assets/{ReviewCard-HXRle3qq.js → ReviewCard-CD3yWvUB.js} +2 -2
- package/static/app/assets/System-BJ6jQ_SL.js +1 -0
- package/static/app/assets/arrow-left-6_28Z0qH.js +1 -0
- package/static/app/assets/{bot-Cn8bWRuq.js → bot-Bc3Q27YR.js} +1 -1
- package/static/app/assets/brain-B9BDrMTe.js +1 -0
- package/static/app/assets/{button-Ct9f2_oT.js → button-D6JcpYcf.js} +1 -1
- package/static/app/assets/circle-check-BFu9lD-3.js +1 -0
- package/static/app/assets/{circle-pause-CmzC_apg.js → circle-pause-BGMiV8UU.js} +1 -1
- package/static/app/assets/{circle-play-D8mW2aQ7.js → circle-play-DoanLHnd.js} +1 -1
- package/static/app/assets/{cpu-DZcdd0PZ.js → cpu-DwzNf82m.js} +1 -1
- package/static/app/assets/{download-bv1KEPGQ.js → download-Ddw49yCV.js} +1 -1
- package/static/app/assets/{folder-open-d-Pip5gr.js → folder-open-Brd6Kvto.js} +1 -1
- package/static/app/assets/{hard-drive-D20iavUb.js → hard-drive-Bu-DTJdB.js} +1 -1
- package/static/app/assets/index-CGdg_aq9.css +2 -0
- package/static/app/assets/{index-Do83hDzJ.js → index-CWKRRsLW.js} +4 -4
- package/static/app/assets/input-CEqsxtil.js +1 -0
- package/static/app/assets/{link-2-BPJOFlAy.js → link-2-DZ4OA5tJ.js} +1 -1
- package/static/app/assets/{permissionCopy-ChdJd493.js → permissionCopy-D9TR0F8b.js} +1 -1
- package/static/app/assets/primitives-DORg7Z_7.js +1 -0
- package/static/app/assets/search-0NQ21wXe.js +1 -0
- package/static/app/assets/{share-2-YNX_NtMU.js → share-2-BC5FirFv.js} +1 -1
- package/static/app/assets/{shield-alert-DuQ3zrVL.js → shield-alert-DUbR2W2s.js} +1 -1
- package/static/app/assets/textarea-jtQcRSXo.js +1 -0
- package/static/app/assets/{useFocusTrap-ZVI98jaW.js → useFocusTrap-HRemcWId.js} +1 -1
- package/static/app/assets/{useMutation-CVC4qv_D.js → useMutation-DqlFE-Bw.js} +1 -1
- package/static/app/assets/{useQuery-C7BeG4HU.js → useQuery-BizqBNGw.js} +1 -1
- package/static/app/assets/utils-WgW4V69R.js +4 -0
- package/static/app/assets/workspace-DSek3jCY.js +1 -0
- package/static/app/index.html +4 -4
- package/static/sw.js +1 -1
- package/lattice_brain/ingestion/pipeline.py +0 -108
- package/latticeai/api/local_files.py +0 -44
- package/latticeai/api/tools.py +0 -126
- package/latticeai/api/voice_capture.py +0 -32
- package/latticeai/core/agent_permission.py +0 -85
- package/scripts/agent_eval.py +0 -34
- package/scripts/brain_quality_eval.py +0 -37
- package/scripts/check_legacy_debt.mjs +0 -91
- package/scripts/check_python.py +0 -100
- package/scripts/chunking_parity_corpus.py +0 -449
- package/scripts/generate_agent_parity_fixtures.py +0 -771
- package/scripts/generate_chunking_parity_fixtures.py +0 -259
- package/static/app/assets/AdminConsole-Bw1ATQL0.js +0 -1
- package/static/app/assets/BrainHome-CFBkt1K_.js +0 -2
- package/static/app/assets/BrainSignals-ReLWF2H8.js +0 -1
- package/static/app/assets/Capture-BsTokYkk.js +0 -1
- package/static/app/assets/Chronicle-B6f0T9id.js +0 -1
- package/static/app/assets/CommandPalette-CuvjTv1u.js +0 -1
- package/static/app/assets/Library-BGJbG9Hd.js +0 -1
- package/static/app/assets/LivingBrain-DGYK_Jsa.js +0 -1
- package/static/app/assets/System-CMHSO9qM.js +0 -1
- package/static/app/assets/arrow-left-BfmkskWx.js +0 -1
- package/static/app/assets/brain-CQJberbE.js +0 -1
- package/static/app/assets/circle-check-DruOxB-4.js +0 -1
- package/static/app/assets/index-D9x-kSNy.css +0 -2
- package/static/app/assets/input-BLXVNmj1.js +0 -1
- package/static/app/assets/primitives-Cv5tbZBY.js +0 -1
- package/static/app/assets/search-CT9aho2j.js +0 -1
- package/static/app/assets/textarea-DqwLnli4.js +0 -1
- package/static/app/assets/utils-CiFtIdZq.js +0 -4
- package/static/app/assets/workspace-DQz9vIId.js +0 -1
|
@@ -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
|
-
]
|
package/scripts/agent_eval.py
DELETED
|
@@ -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)");
|
package/scripts/check_python.py
DELETED
|
@@ -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:]))
|