cct-cli 0.7.9.0__py3-none-any.whl
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.
- calc_terminal/__init__.py +14 -0
- calc_terminal/__main__.py +14 -0
- calc_terminal/activity.py +1334 -0
- calc_terminal/agent.py +3387 -0
- calc_terminal/agent_runtime.py +519 -0
- calc_terminal/ai_context.py +447 -0
- calc_terminal/ai_modes.py +752 -0
- calc_terminal/ai_personalization.py +286 -0
- calc_terminal/ai_preview_feedback.py +213 -0
- calc_terminal/aicore.py +2572 -0
- calc_terminal/anim.py +367 -0
- calc_terminal/app.py +3685 -0
- calc_terminal/art.py +639 -0
- calc_terminal/atomsim.py +368 -0
- calc_terminal/attachments.py +743 -0
- calc_terminal/benchmark_system.py +414 -0
- calc_terminal/browser/__init__.py +36 -0
- calc_terminal/browser/browser_state.py +346 -0
- calc_terminal/browser/devserver.py +176 -0
- calc_terminal/browser/engine.py +494 -0
- calc_terminal/browser/navigation.py +84 -0
- calc_terminal/browser/preview.py +429 -0
- calc_terminal/browser/preview_entry.py +95 -0
- calc_terminal/browser/project_detector.py +144 -0
- calc_terminal/browser/server.py +449 -0
- calc_terminal/browser/state.py +75 -0
- calc_terminal/browser/watcher.py +99 -0
- calc_terminal/browser_gui/__init__.py +1 -0
- calc_terminal/browser_gui/__main__.py +3 -0
- calc_terminal/browser_gui/launcher.py +173 -0
- calc_terminal/browser_gui/playwright_browser.py +117 -0
- calc_terminal/browser_gui/qt_browser.py +1501 -0
- calc_terminal/browser_gui/webview_browser.py +57 -0
- calc_terminal/capabilities/__init__.py +35 -0
- calc_terminal/capabilities/adapters/__init__.py +33 -0
- calc_terminal/capabilities/adapters/bioinformatics.py +204 -0
- calc_terminal/capabilities/adapters/browser_adapter.py +205 -0
- calc_terminal/capabilities/adapters/filesystem.py +206 -0
- calc_terminal/capabilities/adapters/git_adapter.py +202 -0
- calc_terminal/capabilities/adapters/jupyter_adapter.py +138 -0
- calc_terminal/capabilities/adapters/ml_frameworks.py +158 -0
- calc_terminal/capabilities/adapters/platforms.py +200 -0
- calc_terminal/capabilities/adapters/python_exec.py +93 -0
- calc_terminal/capabilities/adapters/quantum_adapter.py +150 -0
- calc_terminal/capabilities/adapters/scientific_comp.py +123 -0
- calc_terminal/capabilities/adapters/structural_bio.py +161 -0
- calc_terminal/capabilities/adapters/terminal.py +99 -0
- calc_terminal/capabilities/bus.py +178 -0
- calc_terminal/capabilities/discovery.py +207 -0
- calc_terminal/capabilities/schema.py +221 -0
- calc_terminal/cat.ico +0 -0
- calc_terminal/cat_browser.py +2018 -0
- calc_terminal/chat_store.py +703 -0
- calc_terminal/cli.py +1178 -0
- calc_terminal/code_editor.py +640 -0
- calc_terminal/collaboration.py +723 -0
- calc_terminal/commands_data.py +139 -0
- calc_terminal/compatibility_engine.py +352 -0
- calc_terminal/compute/__init__.py +31 -0
- calc_terminal/compute/fabric.py +350 -0
- calc_terminal/config.py +227 -0
- calc_terminal/core/__init__.py +41 -0
- calc_terminal/core/checkpoint.py +156 -0
- calc_terminal/core/input/__init__.py +45 -0
- calc_terminal/core/mode_registry.py +300 -0
- calc_terminal/core/project_graph.py +172 -0
- calc_terminal/core/recovery.py +129 -0
- calc_terminal/core/security_layer.py +112 -0
- calc_terminal/core/task_graph.py +202 -0
- calc_terminal/core/unified_runtime.py +184 -0
- calc_terminal/core/verification.py +257 -0
- calc_terminal/customization.py +1566 -0
- calc_terminal/derivations.py +153 -0
- calc_terminal/device_control.py +263 -0
- calc_terminal/diagnostics/__init__.py +27 -0
- calc_terminal/diagnostics/doctor_engine.py +382 -0
- calc_terminal/diagnostics/self_test.py +247 -0
- calc_terminal/doctor.py +519 -0
- calc_terminal/easter_eggs.py +274 -0
- calc_terminal/editor/__init__.py +1 -0
- calc_terminal/editor/actions.py +263 -0
- calc_terminal/editor/commands.py +160 -0
- calc_terminal/editor/shortcuts.py +226 -0
- calc_terminal/engine.py +259 -0
- calc_terminal/errors.py +120 -0
- calc_terminal/event_stream.py +146 -0
- calc_terminal/eventbus.py +133 -0
- calc_terminal/extensions.py +733 -0
- calc_terminal/fallback_cli.py +1321 -0
- calc_terminal/first_run.py +265 -0
- calc_terminal/fomoji_auth.py +1043 -0
- calc_terminal/formulas.py +82 -0
- calc_terminal/fs_cache.py +121 -0
- calc_terminal/fs_watcher.py +277 -0
- calc_terminal/game.py +193 -0
- calc_terminal/gen1.py +5 -0
- calc_terminal/generators.py +245 -0
- calc_terminal/gestures/__init__.py +42 -0
- calc_terminal/gestures/bindings.py +175 -0
- calc_terminal/gestures/manager.py +477 -0
- calc_terminal/goodbye.py +363 -0
- calc_terminal/gpu3d.py +290 -0
- calc_terminal/graphs.py +358 -0
- calc_terminal/hardware_analyzer.py +440 -0
- calc_terminal/host/__init__.py +30 -0
- calc_terminal/host/browser_manager.py +187 -0
- calc_terminal/host/desktop.py +1386 -0
- calc_terminal/host/launcher.py +395 -0
- calc_terminal/host/terminal.py +279 -0
- calc_terminal/identity.py +216 -0
- calc_terminal/input/__init__.py +54 -0
- calc_terminal/input/capabilities.py +258 -0
- calc_terminal/input/focus.py +87 -0
- calc_terminal/input/gestures.py +64 -0
- calc_terminal/input/pointer.py +114 -0
- calc_terminal/input/touch.py +345 -0
- calc_terminal/keys.py +84 -0
- calc_terminal/live_automation.py +165 -0
- calc_terminal/mathtext.py +433 -0
- calc_terminal/mcp.py +386 -0
- calc_terminal/memory.py +337 -0
- calc_terminal/memory_v2.py +479 -0
- calc_terminal/metrics.py +333 -0
- calc_terminal/mode_detection.py +146 -0
- calc_terminal/model.py +2431 -0
- calc_terminal/model_router.py +665 -0
- calc_terminal/models/__init__.py +0 -0
- calc_terminal/models/active_state.py +187 -0
- calc_terminal/models/dynamic_registry.py +584 -0
- calc_terminal/models/manager.py +781 -0
- calc_terminal/models/model_metadata.json +3526 -0
- calc_terminal/models/profiles.py +194 -0
- calc_terminal/models/registry.py +265 -0
- calc_terminal/models/schema.py +197 -0
- calc_terminal/models/validator.py +287 -0
- calc_terminal/models/verification_engine.py +368 -0
- calc_terminal/native_picker.py +215 -0
- calc_terminal/ollama_catalog.py +279 -0
- calc_terminal/ollama_download.py +233 -0
- calc_terminal/orchestrator.py +304 -0
- calc_terminal/package_research.py +322 -0
- calc_terminal/packages.py +1024 -0
- calc_terminal/pc_specs.py +116 -0
- calc_terminal/permissions.py +334 -0
- calc_terminal/pet.py +106 -0
- calc_terminal/pipeline.py +505 -0
- calc_terminal/platform/__init__.py +491 -0
- calc_terminal/platform/desktop.py +491 -0
- calc_terminal/platform/web.py +781 -0
- calc_terminal/preview/__init__.py +1 -0
- calc_terminal/preview/dev_server.py +303 -0
- calc_terminal/preview/diagnostics.py +131 -0
- calc_terminal/preview/live_reload.py +66 -0
- calc_terminal/preview/manager.py +129 -0
- calc_terminal/project_stats.py +209 -0
- calc_terminal/projects.py +328 -0
- calc_terminal/providers/__init__.py +0 -0
- calc_terminal/providers/adapters/__init__.py +80 -0
- calc_terminal/providers/adapters/anthropic_adapter.py +127 -0
- calc_terminal/providers/adapters/base.py +106 -0
- calc_terminal/providers/adapters/chinese_adapters.py +420 -0
- calc_terminal/providers/adapters/gemini_adapter.py +101 -0
- calc_terminal/providers/adapters/ollama_adapter.py +83 -0
- calc_terminal/providers/adapters/openai_adapter.py +159 -0
- calc_terminal/providers/adapters/other_adapters.py +246 -0
- calc_terminal/providers/anthropic_provider.py +172 -0
- calc_terminal/providers/auto_update.py +416 -0
- calc_terminal/providers/base_provider.py +105 -0
- calc_terminal/providers/discovery_manager.py +207 -0
- calc_terminal/providers/gemini_provider.py +178 -0
- calc_terminal/providers/lifecycle.py +767 -0
- calc_terminal/providers/ollama_adapter.py +707 -0
- calc_terminal/providers/openai_provider.py +254 -0
- calc_terminal/providers/provider_manager.py +1827 -0
- calc_terminal/providers/providers.json +4075 -0
- calc_terminal/reactionsim.py +279 -0
- calc_terminal/registry.py +337 -0
- calc_terminal/report.py +162 -0
- calc_terminal/research/__init__.py +45 -0
- calc_terminal/research/artifact_intel.py +126 -0
- calc_terminal/research/data_lineage.py +123 -0
- calc_terminal/research/experiment_ledger.py +303 -0
- calc_terminal/research/reproducibility.py +131 -0
- calc_terminal/resilience/__init__.py +47 -0
- calc_terminal/resilience/agent_state.py +121 -0
- calc_terminal/resilience/capability_matcher.py +174 -0
- calc_terminal/resilience/circuit_breaker.py +158 -0
- calc_terminal/resilience/failover_engine.py +230 -0
- calc_terminal/resilience/health_monitor.py +192 -0
- calc_terminal/resilience/ollama_adapter.py +125 -0
- calc_terminal/resilience/orchestrator.py +312 -0
- calc_terminal/resilience/types.py +134 -0
- calc_terminal/sandbox.py +98 -0
- calc_terminal/scires.py +558 -0
- calc_terminal/security_scanner.py +126 -0
- calc_terminal/session.py +294 -0
- calc_terminal/sim3d.py +206 -0
- calc_terminal/solver.py +276 -0
- calc_terminal/sound.py +127 -0
- calc_terminal/task_reports.py +287 -0
- calc_terminal/terminal_host.py +201 -0
- calc_terminal/terminal_identity.py +411 -0
- calc_terminal/test_ai_mode_reliability.py +344 -0
- calc_terminal/test_browser.py +368 -0
- calc_terminal/test_code_editor_upgrade.py +485 -0
- calc_terminal/test_customization.py +1148 -0
- calc_terminal/test_customization_ui.py +612 -0
- calc_terminal/test_dynamic_registry.py +304 -0
- calc_terminal/test_extensions.py +436 -0
- calc_terminal/test_overhaul.py +557 -0
- calc_terminal/test_project_detect.py +255 -0
- calc_terminal/test_root_cause_fix.py +527 -0
- calc_terminal/test_stability.py +532 -0
- calc_terminal/test_terminal_identity.py +132 -0
- calc_terminal/test_v079_speed.py +460 -0
- calc_terminal/theme.py +1107 -0
- calc_terminal/timeline.py +139 -0
- calc_terminal/todos.py +246 -0
- calc_terminal/tool_call_normalizer.py +419 -0
- calc_terminal/tui.py +104 -0
- calc_terminal/ui/__init__.py +8 -0
- calc_terminal/ui/activity_panel.py +231 -0
- calc_terminal/ui/activity_stream_panel.py +238 -0
- calc_terminal/ui/animations.py +122 -0
- calc_terminal/ui/app.py +7271 -0
- calc_terminal/ui/attach_panel.py +597 -0
- calc_terminal/ui/attachments.py +424 -0
- calc_terminal/ui/backup_panel.py +810 -0
- calc_terminal/ui/browser_shell.py +887 -0
- calc_terminal/ui/cat_agent.py +357 -0
- calc_terminal/ui/chats_panel.py +899 -0
- calc_terminal/ui/command_palette.py +125 -0
- calc_terminal/ui/command_palette_modal.py +166 -0
- calc_terminal/ui/composer.py +1141 -0
- calc_terminal/ui/context_menu.py +197 -0
- calc_terminal/ui/conversation.py +1435 -0
- calc_terminal/ui/customization_panel.py +1229 -0
- calc_terminal/ui/dashboard.py +404 -0
- calc_terminal/ui/design_system.py +557 -0
- calc_terminal/ui/diff_panel.py +213 -0
- calc_terminal/ui/editor.py +2102 -0
- calc_terminal/ui/empty_state.py +302 -0
- calc_terminal/ui/events.py +487 -0
- calc_terminal/ui/extensions_panel.py +815 -0
- calc_terminal/ui/footer.py +166 -0
- calc_terminal/ui/gestures_panel.py +383 -0
- calc_terminal/ui/goodbye_screen.py +100 -0
- calc_terminal/ui/header.py +1034 -0
- calc_terminal/ui/help_panel.py +254 -0
- calc_terminal/ui/live_activities.py +914 -0
- calc_terminal/ui/mcp_panel.py +570 -0
- calc_terminal/ui/memory_center.py +524 -0
- calc_terminal/ui/mode_colors_panel.py +525 -0
- calc_terminal/ui/nav_screens.py +747 -0
- calc_terminal/ui/ollama_panel.py +536 -0
- calc_terminal/ui/palette.py +221 -0
- calc_terminal/ui/permission_panel.py +269 -0
- calc_terminal/ui/personalization_panel.py +517 -0
- calc_terminal/ui/personalize_center.py +1568 -0
- calc_terminal/ui/preview_panel.py +441 -0
- calc_terminal/ui/resizers.py +402 -0
- calc_terminal/ui/sidebar.py +1285 -0
- calc_terminal/ui/statusbar.py +168 -0
- calc_terminal/ui/theme_css.py +1396 -0
- calc_terminal/ui/thinking.py +226 -0
- calc_terminal/ui/timeline_panel.py +102 -0
- calc_terminal/ui/todo_panel.py +193 -0
- calc_terminal/ui/viewport.py +136 -0
- calc_terminal/ui/vision_panel.py +489 -0
- calc_terminal/ui/welcome_modal.py +343 -0
- calc_terminal/ui/widgets.py +160 -0
- calc_terminal/ui/workspace.py +831 -0
- calc_terminal/viewers/__init__.py +1 -0
- calc_terminal/viewers/document_viewer.py +252 -0
- calc_terminal/viewers/image_viewer.py +241 -0
- calc_terminal/viewers/pdf_viewer.py +203 -0
- calc_terminal/viewers/presentation_viewer.py +164 -0
- calc_terminal/viewers/registry.py +120 -0
- calc_terminal/viewers/spreadsheet_viewer.py +204 -0
- calc_terminal/vision/__init__.py +89 -0
- calc_terminal/vision/analysis.py +194 -0
- calc_terminal/vision/annotations.py +297 -0
- calc_terminal/vision/capture.py +171 -0
- calc_terminal/vision/context.py +231 -0
- calc_terminal/vision/correlation.py +169 -0
- calc_terminal/vision/cursor.py +258 -0
- calc_terminal/vision/events.py +66 -0
- calc_terminal/vision/frame_pipeline.py +259 -0
- calc_terminal/vision/priority.py +218 -0
- calc_terminal/vision/provider.py +180 -0
- calc_terminal/vision/safety.py +149 -0
- calc_terminal/vision/session.py +281 -0
- calc_terminal/vision/verify.py +162 -0
- calc_terminal/vision.py +514 -0
- calc_terminal/vscode_integration.py +113 -0
- calc_terminal/web/__init__.py +8 -0
- calc_terminal/web/cat_runtime.py +710 -0
- calc_terminal/web/server.py +2891 -0
- calc_terminal/web/static/css/app.css +3152 -0
- calc_terminal/web/static/icons/badge-72.png +0 -0
- calc_terminal/web/static/icons/cat.ico +0 -0
- calc_terminal/web/static/icons/icon-128.png +0 -0
- calc_terminal/web/static/icons/icon-144.png +0 -0
- calc_terminal/web/static/icons/icon-152.png +0 -0
- calc_terminal/web/static/icons/icon-192.png +0 -0
- calc_terminal/web/static/icons/icon-384.png +0 -0
- calc_terminal/web/static/icons/icon-512.png +0 -0
- calc_terminal/web/static/icons/icon-72.png +0 -0
- calc_terminal/web/static/icons/icon-96.png +0 -0
- calc_terminal/web/static/icons/icon.svg +34 -0
- calc_terminal/web/static/icons/new-project.png +0 -0
- calc_terminal/web/static/icons/open-project.png +0 -0
- calc_terminal/web/static/index.html +734 -0
- calc_terminal/web/static/js/app.js +2403 -0
- calc_terminal/web/static/manifest.json +88 -0
- calc_terminal/web/static/sw.js +230 -0
- calc_terminal/workflow_engine.py +769 -0
- calc_terminal/workspace.py +593 -0
- calc_terminal/workspace_index.py +385 -0
- cct_cli-0.7.9.0.dist-info/METADATA +210 -0
- cct_cli-0.7.9.0.dist-info/RECORD +325 -0
- cct_cli-0.7.9.0.dist-info/WHEEL +5 -0
- cct_cli-0.7.9.0.dist-info/entry_points.txt +4 -0
- cct_cli-0.7.9.0.dist-info/licenses/LICENSE +21 -0
- cct_cli-0.7.9.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CAT Diagnostic System ("CAT Doctor") per §32:
|
|
3
|
+
Comprehensive 19-dimension health check across runtime, environment, providers,
|
|
4
|
+
compute, security, scientific tools, and workspace with zero mock data.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import platform
|
|
12
|
+
import shutil
|
|
13
|
+
import socket
|
|
14
|
+
import subprocess
|
|
15
|
+
import sys
|
|
16
|
+
import time
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from typing import Any, Dict, List, Optional
|
|
19
|
+
|
|
20
|
+
from calc_terminal.hardware_analyzer import HardwareAnalyzer
|
|
21
|
+
from calc_terminal.capabilities.bus import capability_bus
|
|
22
|
+
from calc_terminal.core.mode_registry import mode_registry
|
|
23
|
+
from calc_terminal.core.security_layer import security_layer
|
|
24
|
+
from calc_terminal.research.experiment_ledger import experiment_ledger
|
|
25
|
+
from calc_terminal.research.data_lineage import data_lineage
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class DimensionHealth:
|
|
30
|
+
name: str
|
|
31
|
+
dimension_id: str
|
|
32
|
+
status: str # HEALTHY, DEGRADED, UNAVAILABLE, NOT_CONFIGURED
|
|
33
|
+
summary: str
|
|
34
|
+
details: Dict[str, Any] = field(default_factory=dict)
|
|
35
|
+
recommendation: Optional[str] = None
|
|
36
|
+
|
|
37
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
38
|
+
return {
|
|
39
|
+
"dimension_id": self.dimension_id,
|
|
40
|
+
"name": self.name,
|
|
41
|
+
"status": self.status,
|
|
42
|
+
"summary": self.summary,
|
|
43
|
+
"details": self.details,
|
|
44
|
+
"recommendation": self.recommendation,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class DoctorReport:
|
|
50
|
+
timestamp: float = field(default_factory=time.time)
|
|
51
|
+
overall_status: str = "HEALTHY"
|
|
52
|
+
dimensions: List[DimensionHealth] = field(default_factory=list)
|
|
53
|
+
|
|
54
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
55
|
+
return {
|
|
56
|
+
"timestamp": self.timestamp,
|
|
57
|
+
"overall_status": self.overall_status,
|
|
58
|
+
"dimensions": [d.to_dict() for d in self.dimensions],
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
def format_text(self) -> str:
|
|
62
|
+
lines = [
|
|
63
|
+
"============================================================",
|
|
64
|
+
" CAT SYSTEM DOCTOR REPORT ",
|
|
65
|
+
"============================================================",
|
|
66
|
+
f"Timestamp: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.timestamp))}",
|
|
67
|
+
f"Overall Health: {self.overall_status}",
|
|
68
|
+
"------------------------------------------------------------",
|
|
69
|
+
]
|
|
70
|
+
for d in self.dimensions:
|
|
71
|
+
icon = "✓" if d.status == "HEALTHY" else ("!" if d.status in ("DEGRADED", "NOT_CONFIGURED") else "✗")
|
|
72
|
+
lines.append(f"[{icon}] {d.name} ({d.dimension_id}): {d.status}")
|
|
73
|
+
lines.append(f" {d.summary}")
|
|
74
|
+
if d.recommendation:
|
|
75
|
+
lines.append(f" Action: {d.recommendation}")
|
|
76
|
+
lines.append("============================================================")
|
|
77
|
+
return "\n".join(lines)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class DoctorEngine:
|
|
81
|
+
"""Orchestrates comprehensive 19-dimension diagnostics."""
|
|
82
|
+
|
|
83
|
+
def run_diagnostics(self) -> DoctorReport:
|
|
84
|
+
dimensions = [
|
|
85
|
+
self._check_python_env(),
|
|
86
|
+
self._check_package_managers(),
|
|
87
|
+
self._check_git_repository(),
|
|
88
|
+
self._check_hardware_compute(),
|
|
89
|
+
self._check_local_inference(),
|
|
90
|
+
self._check_remote_providers(),
|
|
91
|
+
self._check_storage(),
|
|
92
|
+
self._check_browser_automation(),
|
|
93
|
+
self._check_terminal_emulator(),
|
|
94
|
+
self._check_mode_registry(),
|
|
95
|
+
self._check_capability_bus(),
|
|
96
|
+
self._check_task_runtime(),
|
|
97
|
+
self._check_experiment_ledger(),
|
|
98
|
+
self._check_data_lineage(),
|
|
99
|
+
self._check_security_policy(),
|
|
100
|
+
self._check_network_connectivity(),
|
|
101
|
+
self._check_scientific_software(),
|
|
102
|
+
self._check_quantum_sdks(),
|
|
103
|
+
self._check_workspace_health(),
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
has_unavail = any(d.status == "UNAVAILABLE" for d in dimensions)
|
|
107
|
+
has_degraded = any(d.status in ("DEGRADED", "NOT_CONFIGURED") for d in dimensions)
|
|
108
|
+
overall = "UNAVAILABLE" if has_unavail else ("DEGRADED" if has_degraded else "HEALTHY")
|
|
109
|
+
|
|
110
|
+
return DoctorReport(overall_status=overall, dimensions=dimensions)
|
|
111
|
+
|
|
112
|
+
def _check_python_env(self) -> DimensionHealth:
|
|
113
|
+
v = sys.version_info
|
|
114
|
+
is_ok = v.major == 3 and v.minor >= 10
|
|
115
|
+
status = "HEALTHY" if is_ok else "DEGRADED"
|
|
116
|
+
return DimensionHealth(
|
|
117
|
+
name="Python Environment",
|
|
118
|
+
dimension_id="python_env",
|
|
119
|
+
status=status,
|
|
120
|
+
summary=f"Python {v.major}.{v.minor}.{v.micro} on {platform.system()}",
|
|
121
|
+
details={"version": platform.python_version(), "executable": sys.executable},
|
|
122
|
+
recommendation="Upgrade to Python 3.10+ if using an older version." if not is_ok else None,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def _check_package_managers(self) -> DimensionHealth:
|
|
126
|
+
pips = shutil.which("pip") is not None
|
|
127
|
+
uvs = shutil.which("uv") is not None
|
|
128
|
+
condas = shutil.which("conda") is not None
|
|
129
|
+
found = []
|
|
130
|
+
if pips: found.append("pip")
|
|
131
|
+
if uvs: found.append("uv")
|
|
132
|
+
if condas: found.append("conda")
|
|
133
|
+
status = "HEALTHY" if found else "DEGRADED"
|
|
134
|
+
return DimensionHealth(
|
|
135
|
+
name="Package Managers",
|
|
136
|
+
dimension_id="package_managers",
|
|
137
|
+
status=status,
|
|
138
|
+
summary=f"Available: {', '.join(found) if found else 'None'}",
|
|
139
|
+
details={"pip": pips, "uv": uvs, "conda": condas},
|
|
140
|
+
recommendation="Install 'uv' or 'pip' for optimal package management." if not found else None,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def _check_git_repository(self) -> DimensionHealth:
|
|
144
|
+
git_bin = shutil.which("git")
|
|
145
|
+
if not git_bin:
|
|
146
|
+
return DimensionHealth(
|
|
147
|
+
name="Git Repository",
|
|
148
|
+
dimension_id="git_repo",
|
|
149
|
+
status="UNAVAILABLE",
|
|
150
|
+
summary="Git executable not found in PATH",
|
|
151
|
+
recommendation="Install Git and ensure it is in your system PATH.",
|
|
152
|
+
)
|
|
153
|
+
try:
|
|
154
|
+
res = subprocess.run(["git", "rev-parse", "--is-inside-work-tree"], capture_output=True, text=True, timeout=1.5)
|
|
155
|
+
is_repo = res.returncode == 0
|
|
156
|
+
summary = "Inside active Git repository" if is_repo else "Not inside a Git repository"
|
|
157
|
+
return DimensionHealth(
|
|
158
|
+
name="Git Repository",
|
|
159
|
+
dimension_id="git_repo",
|
|
160
|
+
status="HEALTHY" if is_repo else "DEGRADED",
|
|
161
|
+
summary=summary,
|
|
162
|
+
details={"git_binary": git_bin, "is_repo": is_repo},
|
|
163
|
+
recommendation="Run 'git init' to enable full versioning and diff checkpoints." if not is_repo else None,
|
|
164
|
+
)
|
|
165
|
+
except Exception:
|
|
166
|
+
return DimensionHealth(name="Git Repository", dimension_id="git_repo", status="DEGRADED", summary="Git check timed out")
|
|
167
|
+
|
|
168
|
+
def _check_hardware_compute(self) -> DimensionHealth:
|
|
169
|
+
profile = HardwareAnalyzer.analyze()
|
|
170
|
+
return DimensionHealth(
|
|
171
|
+
name="Hardware & Compute",
|
|
172
|
+
dimension_id="hardware_compute",
|
|
173
|
+
status="HEALTHY",
|
|
174
|
+
summary=f"{profile.cpu_threads} CPU cores, {round(profile.ram_total_gb, 1)}GB RAM, GPU: {profile.gpu_name}",
|
|
175
|
+
details={
|
|
176
|
+
"cpu_threads": profile.cpu_threads,
|
|
177
|
+
"ram_gb": profile.ram_total_gb,
|
|
178
|
+
"gpu_detected": profile.has_gpu,
|
|
179
|
+
"gpu_name": profile.gpu_name,
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
def _check_local_inference(self) -> DimensionHealth:
|
|
184
|
+
ollama_bin = shutil.which("ollama")
|
|
185
|
+
if not ollama_bin:
|
|
186
|
+
return DimensionHealth(
|
|
187
|
+
name="Local Inference",
|
|
188
|
+
dimension_id="local_inference",
|
|
189
|
+
status="NOT_CONFIGURED",
|
|
190
|
+
summary="Ollama not installed (optional local LLM runtime)",
|
|
191
|
+
recommendation="Install Ollama from https://ollama.ai if offline inference is desired.",
|
|
192
|
+
)
|
|
193
|
+
return DimensionHealth(
|
|
194
|
+
name="Local Inference",
|
|
195
|
+
dimension_id="local_inference",
|
|
196
|
+
status="HEALTHY",
|
|
197
|
+
summary=f"Ollama binary available at {ollama_bin}",
|
|
198
|
+
details={"ollama_path": ollama_bin},
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
def _check_remote_providers(self) -> DimensionHealth:
|
|
202
|
+
keys = []
|
|
203
|
+
for k in ["OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GEMINI_API_KEY", "GROQ_API_KEY", "OPENROUTER_API_KEY"]:
|
|
204
|
+
if os.environ.get(k):
|
|
205
|
+
keys.append(k.split("_")[0])
|
|
206
|
+
status = "HEALTHY" if keys else "NOT_CONFIGURED"
|
|
207
|
+
return DimensionHealth(
|
|
208
|
+
name="Remote AI Providers",
|
|
209
|
+
dimension_id="remote_providers",
|
|
210
|
+
status=status,
|
|
211
|
+
summary=f"Configured API keys: {', '.join(keys) if keys else 'None detected in environment'}",
|
|
212
|
+
details={"configured": keys},
|
|
213
|
+
recommendation="Set OPENAI_API_KEY or GEMINI_API_KEY for cloud model access." if not keys else None,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
def _check_storage(self) -> DimensionHealth:
|
|
217
|
+
try:
|
|
218
|
+
total, used, free = shutil.disk_usage(os.getcwd())
|
|
219
|
+
free_gb = round(free / (1024 ** 3), 2)
|
|
220
|
+
status = "HEALTHY" if free_gb > 2.0 else "DEGRADED"
|
|
221
|
+
return DimensionHealth(
|
|
222
|
+
name="Storage & Disk",
|
|
223
|
+
dimension_id="storage",
|
|
224
|
+
status=status,
|
|
225
|
+
summary=f"{free_gb} GB free disk space",
|
|
226
|
+
details={"free_gb": free_gb},
|
|
227
|
+
recommendation="Free up disk space for checkpoints and model weights." if free_gb <= 2.0 else None,
|
|
228
|
+
)
|
|
229
|
+
except Exception:
|
|
230
|
+
return DimensionHealth(name="Storage & Disk", dimension_id="storage", status="HEALTHY", summary="Storage accessible")
|
|
231
|
+
|
|
232
|
+
def _check_browser_automation(self) -> DimensionHealth:
|
|
233
|
+
has_playwright = False
|
|
234
|
+
try:
|
|
235
|
+
import playwright
|
|
236
|
+
has_playwright = True
|
|
237
|
+
except ImportError:
|
|
238
|
+
pass
|
|
239
|
+
status = "HEALTHY" if has_playwright else "NOT_CONFIGURED"
|
|
240
|
+
return DimensionHealth(
|
|
241
|
+
name="Browser Automation",
|
|
242
|
+
dimension_id="browser_automation",
|
|
243
|
+
status=status,
|
|
244
|
+
summary="Playwright installed" if has_playwright else "Playwright library not installed (optional)",
|
|
245
|
+
recommendation="Run 'pip install playwright && playwright install' to enable embedded web browsing." if not has_playwright else None,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
def _check_terminal_emulator(self) -> DimensionHealth:
|
|
249
|
+
shell = os.environ.get("SHELL") or os.environ.get("COMSPEC", "unknown")
|
|
250
|
+
enc = sys.getdefaultencoding()
|
|
251
|
+
return DimensionHealth(
|
|
252
|
+
name="Terminal Host",
|
|
253
|
+
dimension_id="terminal_host",
|
|
254
|
+
status="HEALTHY",
|
|
255
|
+
summary=f"Shell: {os.path.basename(shell)}, Encoding: {enc}",
|
|
256
|
+
details={"shell": shell, "encoding": enc},
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
def _check_mode_registry(self) -> DimensionHealth:
|
|
260
|
+
modes = mode_registry.list_modes()
|
|
261
|
+
custom_count = sum(1 for m in modes if not m.get("default"))
|
|
262
|
+
return DimensionHealth(
|
|
263
|
+
name="Mode Registry",
|
|
264
|
+
dimension_id="mode_registry",
|
|
265
|
+
status="HEALTHY",
|
|
266
|
+
summary=f"{len(modes)} modes active ({custom_count} custom)",
|
|
267
|
+
details={"mode_count": len(modes)},
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
def _check_capability_bus(self) -> DimensionHealth:
|
|
271
|
+
caps = capability_bus.list_capabilities()
|
|
272
|
+
return DimensionHealth(
|
|
273
|
+
name="Capability Bus",
|
|
274
|
+
dimension_id="capability_bus",
|
|
275
|
+
status="HEALTHY",
|
|
276
|
+
summary=f"{len(caps)} capabilities registered and ready",
|
|
277
|
+
details={"capability_count": len(caps)},
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
def _check_task_runtime(self) -> DimensionHealth:
|
|
281
|
+
return DimensionHealth(
|
|
282
|
+
name="Task Runtime",
|
|
283
|
+
dimension_id="task_runtime",
|
|
284
|
+
status="HEALTHY",
|
|
285
|
+
summary="Unified agent runtime and DAG task engine ready",
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
def _check_experiment_ledger(self) -> DimensionHealth:
|
|
289
|
+
count = len(experiment_ledger._experiments)
|
|
290
|
+
return DimensionHealth(
|
|
291
|
+
name="Experiment Ledger",
|
|
292
|
+
dimension_id="experiment_ledger",
|
|
293
|
+
status="HEALTHY",
|
|
294
|
+
summary=f"{count} experiments recorded in Scientific Time Machine",
|
|
295
|
+
details={"count": count, "storage": experiment_ledger.storage_path},
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
def _check_data_lineage(self) -> DimensionHealth:
|
|
299
|
+
count = len(data_lineage.nodes)
|
|
300
|
+
return DimensionHealth(
|
|
301
|
+
name="Data Lineage Graph",
|
|
302
|
+
dimension_id="data_lineage",
|
|
303
|
+
status="HEALTHY",
|
|
304
|
+
summary=f"{count} artifact transformations tracked in DAG",
|
|
305
|
+
details={"node_count": count},
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
def _check_security_policy(self) -> DimensionHealth:
|
|
309
|
+
tier = security_layer.current_tier
|
|
310
|
+
return DimensionHealth(
|
|
311
|
+
name="Security & Privacy",
|
|
312
|
+
dimension_id="security_policy",
|
|
313
|
+
status="HEALTHY",
|
|
314
|
+
summary=f"Active tier: {tier.value}, Secret redaction enabled",
|
|
315
|
+
details={"tier": tier.value},
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
def _check_network_connectivity(self) -> DimensionHealth:
|
|
319
|
+
connected = False
|
|
320
|
+
try:
|
|
321
|
+
socket.create_connection(("1.1.1.1", 53), timeout=1.0)
|
|
322
|
+
connected = True
|
|
323
|
+
except Exception:
|
|
324
|
+
try:
|
|
325
|
+
socket.create_connection(("8.8.8.8", 53), timeout=1.0)
|
|
326
|
+
connected = True
|
|
327
|
+
except Exception:
|
|
328
|
+
pass
|
|
329
|
+
status = "HEALTHY" if connected else "DEGRADED"
|
|
330
|
+
return DimensionHealth(
|
|
331
|
+
name="Network Connectivity",
|
|
332
|
+
dimension_id="network",
|
|
333
|
+
status=status,
|
|
334
|
+
summary="Internet connection active" if connected else "No active internet connection (offline mode)",
|
|
335
|
+
recommendation="Connect to internet if remote API or web browsing is needed." if not connected else None,
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
def _check_scientific_software(self) -> DimensionHealth:
|
|
339
|
+
tools = []
|
|
340
|
+
if shutil.which("pymol"): tools.append("PyMOL")
|
|
341
|
+
if shutil.which("chimerax"): tools.append("ChimeraX")
|
|
342
|
+
if shutil.which("blastn"): tools.append("BLAST")
|
|
343
|
+
if shutil.which("matlab"): tools.append("MATLAB")
|
|
344
|
+
status = "HEALTHY" if tools else "NOT_CONFIGURED"
|
|
345
|
+
return DimensionHealth(
|
|
346
|
+
name="Scientific Software",
|
|
347
|
+
dimension_id="scientific_software",
|
|
348
|
+
status=status,
|
|
349
|
+
summary=f"Detected: {', '.join(tools) if tools else 'None in PATH (optional)'}",
|
|
350
|
+
details={"detected_tools": tools},
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
def _check_quantum_sdks(self) -> DimensionHealth:
|
|
354
|
+
sdks = []
|
|
355
|
+
for pkg in ["qiskit", "cirq", "pennylane"]:
|
|
356
|
+
try:
|
|
357
|
+
__import__(pkg)
|
|
358
|
+
sdks.append(pkg)
|
|
359
|
+
except ImportError:
|
|
360
|
+
pass
|
|
361
|
+
status = "HEALTHY" if sdks else "NOT_CONFIGURED"
|
|
362
|
+
return DimensionHealth(
|
|
363
|
+
name="Quantum SDKs",
|
|
364
|
+
dimension_id="quantum_sdks",
|
|
365
|
+
status=status,
|
|
366
|
+
summary=f"Detected: {', '.join(sdks) if sdks else 'None installed (optional)'}",
|
|
367
|
+
details={"sdks": sdks},
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
def _check_workspace_health(self) -> DimensionHealth:
|
|
371
|
+
cwd = os.getcwd()
|
|
372
|
+
has_gitignore = os.path.exists(os.path.join(cwd, ".gitignore"))
|
|
373
|
+
return DimensionHealth(
|
|
374
|
+
name="Workspace Health",
|
|
375
|
+
dimension_id="workspace_health",
|
|
376
|
+
status="HEALTHY",
|
|
377
|
+
summary=f"Active directory: {os.path.basename(cwd)}, .gitignore present: {has_gitignore}",
|
|
378
|
+
details={"path": cwd, "has_gitignore": has_gitignore},
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
doctor_engine = DoctorEngine()
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CAT Automated Self-Test Suite per §33:
|
|
3
|
+
Fast automated validation across core architectural subsystems:
|
|
4
|
+
- Capability Bus
|
|
5
|
+
- Mode Registry
|
|
6
|
+
- Reality Engine
|
|
7
|
+
- Task Graph
|
|
8
|
+
- Checkpoints & Rollback
|
|
9
|
+
- Experiment Ledger
|
|
10
|
+
- Security Layer
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import tempfile
|
|
16
|
+
import time
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from typing import Any, Dict, List, Optional
|
|
19
|
+
|
|
20
|
+
from calc_terminal.capabilities.bus import capability_bus
|
|
21
|
+
from calc_terminal.core.mode_registry import mode_registry
|
|
22
|
+
from calc_terminal.core.verification import reality_engine
|
|
23
|
+
from calc_terminal.core.task_graph import TaskGraph, TaskStatus
|
|
24
|
+
from calc_terminal.core.checkpoint import CheckpointManager
|
|
25
|
+
from calc_terminal.core.security_layer import security_layer
|
|
26
|
+
from calc_terminal.research.experiment_ledger import ExperimentLedger
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class SubsystemTestResult:
|
|
31
|
+
subsystem: str
|
|
32
|
+
passed: bool
|
|
33
|
+
duration_ms: float
|
|
34
|
+
details: str = ""
|
|
35
|
+
error: Optional[str] = None
|
|
36
|
+
|
|
37
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
38
|
+
return {
|
|
39
|
+
"subsystem": self.subsystem,
|
|
40
|
+
"passed": self.passed,
|
|
41
|
+
"duration_ms": self.duration_ms,
|
|
42
|
+
"details": self.details,
|
|
43
|
+
"error": self.error,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class SelfTestReport:
|
|
49
|
+
timestamp: float = field(default_factory=time.time)
|
|
50
|
+
results: List[SubsystemTestResult] = field(default_factory=list)
|
|
51
|
+
all_passed: bool = True
|
|
52
|
+
|
|
53
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
54
|
+
return {
|
|
55
|
+
"timestamp": self.timestamp,
|
|
56
|
+
"all_passed": self.all_passed,
|
|
57
|
+
"results": [r.to_dict() for r in self.results],
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
def format_table(self) -> str:
|
|
61
|
+
lines = [
|
|
62
|
+
"============================================================",
|
|
63
|
+
" CAT AUTOMATED SELF-TEST SUITE ",
|
|
64
|
+
"============================================================",
|
|
65
|
+
f"{'Subsystem':<26} | {'Status':<8} | {'Time (ms)':<9} | Details",
|
|
66
|
+
"------------------------------------------------------------",
|
|
67
|
+
]
|
|
68
|
+
for r in self.results:
|
|
69
|
+
status_str = "PASS" if r.passed else "FAIL"
|
|
70
|
+
lines.append(f"{r.subsystem:<26} | {status_str:<8} | {r.duration_ms:<9.2f} | {r.details}")
|
|
71
|
+
lines.append("------------------------------------------------------------")
|
|
72
|
+
overall = "ALL SUBSYSTEMS OPERATIONAL" if self.all_passed else "SOME TESTS FAILED"
|
|
73
|
+
lines.append(f"Summary: {overall}")
|
|
74
|
+
lines.append("============================================================")
|
|
75
|
+
return "\n".join(lines)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def run_self_test() -> SelfTestReport:
|
|
79
|
+
"""Execute live automated self-tests across all core subsystems."""
|
|
80
|
+
results: List[SubsystemTestResult] = []
|
|
81
|
+
|
|
82
|
+
# 1. Capability Bus
|
|
83
|
+
t0 = time.time()
|
|
84
|
+
try:
|
|
85
|
+
caps = capability_bus.list_capabilities()
|
|
86
|
+
assert len(caps) >= 10
|
|
87
|
+
# Test filesystem.read tool registration
|
|
88
|
+
fs_read = capability_bus.get_capability("filesystem.read")
|
|
89
|
+
assert fs_read is not None
|
|
90
|
+
results.append(SubsystemTestResult(
|
|
91
|
+
subsystem="Capability Bus",
|
|
92
|
+
passed=True,
|
|
93
|
+
duration_ms=(time.time() - t0) * 1000,
|
|
94
|
+
details=f"{len(caps)} tools active & callable",
|
|
95
|
+
))
|
|
96
|
+
except Exception as ex:
|
|
97
|
+
results.append(SubsystemTestResult(
|
|
98
|
+
subsystem="Capability Bus",
|
|
99
|
+
passed=False,
|
|
100
|
+
duration_ms=(time.time() - t0) * 1000,
|
|
101
|
+
details="Capability bus verification failed",
|
|
102
|
+
error=str(ex),
|
|
103
|
+
))
|
|
104
|
+
|
|
105
|
+
# 2. Mode Registry
|
|
106
|
+
t0 = time.time()
|
|
107
|
+
try:
|
|
108
|
+
modes = mode_registry.list_modes()
|
|
109
|
+
assert len(modes) >= 5
|
|
110
|
+
resolved = mode_registry.resolve_mode_name("Use research mode")
|
|
111
|
+
assert resolved is not None
|
|
112
|
+
results.append(SubsystemTestResult(
|
|
113
|
+
subsystem="Mode Registry",
|
|
114
|
+
passed=True,
|
|
115
|
+
duration_ms=(time.time() - t0) * 1000,
|
|
116
|
+
details=f"{len(modes)} modes loaded, resolution OK",
|
|
117
|
+
))
|
|
118
|
+
except Exception as ex:
|
|
119
|
+
results.append(SubsystemTestResult(
|
|
120
|
+
subsystem="Mode Registry",
|
|
121
|
+
passed=False,
|
|
122
|
+
duration_ms=(time.time() - t0) * 1000,
|
|
123
|
+
details="Mode registry test failed",
|
|
124
|
+
error=str(ex),
|
|
125
|
+
))
|
|
126
|
+
|
|
127
|
+
# 3. Reality Engine
|
|
128
|
+
t0 = time.time()
|
|
129
|
+
try:
|
|
130
|
+
ast_ok = reality_engine.verify_ast_syntax("x = 1 + 2\n")
|
|
131
|
+
assert ast_ok.passed is True
|
|
132
|
+
ast_bad = reality_engine.verify_ast_syntax("def broken(:\n")
|
|
133
|
+
assert ast_bad.passed is False
|
|
134
|
+
results.append(SubsystemTestResult(
|
|
135
|
+
subsystem="Reality Engine",
|
|
136
|
+
passed=True,
|
|
137
|
+
duration_ms=(time.time() - t0) * 1000,
|
|
138
|
+
details="AST syntax & diff verification OK",
|
|
139
|
+
))
|
|
140
|
+
except Exception as ex:
|
|
141
|
+
results.append(SubsystemTestResult(
|
|
142
|
+
subsystem="Reality Engine",
|
|
143
|
+
passed=False,
|
|
144
|
+
duration_ms=(time.time() - t0) * 1000,
|
|
145
|
+
details="Reality engine verification failed",
|
|
146
|
+
error=str(ex),
|
|
147
|
+
))
|
|
148
|
+
|
|
149
|
+
# 4. Task Graph
|
|
150
|
+
t0 = time.time()
|
|
151
|
+
try:
|
|
152
|
+
tg = TaskGraph()
|
|
153
|
+
t1 = tg.add_task(title="Fetch data", task_id="task1")
|
|
154
|
+
t2 = tg.add_task(title="Train model", dependencies=["task1"], task_id="task2")
|
|
155
|
+
assert len(tg.get_ready_tasks()) == 1
|
|
156
|
+
tg.update_task_status("task1", TaskStatus.COMPLETED)
|
|
157
|
+
assert len(tg.get_ready_tasks()) == 1
|
|
158
|
+
assert tg.get_ready_tasks()[0].id == "task2"
|
|
159
|
+
results.append(SubsystemTestResult(
|
|
160
|
+
subsystem="Task Graph Engine",
|
|
161
|
+
passed=True,
|
|
162
|
+
duration_ms=(time.time() - t0) * 1000,
|
|
163
|
+
details="DAG dependency resolution OK",
|
|
164
|
+
))
|
|
165
|
+
except Exception as ex:
|
|
166
|
+
results.append(SubsystemTestResult(
|
|
167
|
+
subsystem="Task Graph Engine",
|
|
168
|
+
passed=False,
|
|
169
|
+
duration_ms=(time.time() - t0) * 1000,
|
|
170
|
+
details="Task graph failed",
|
|
171
|
+
error=str(ex),
|
|
172
|
+
))
|
|
173
|
+
|
|
174
|
+
# 5. Checkpoint & Rollback
|
|
175
|
+
t0 = time.time()
|
|
176
|
+
try:
|
|
177
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
178
|
+
chk_mgr = CheckpointManager(storage_dir=tmpdir)
|
|
179
|
+
test_f = f"{tmpdir}/file.txt"
|
|
180
|
+
with open(test_f, "w") as fp:
|
|
181
|
+
fp.write("initial")
|
|
182
|
+
chk = chk_mgr.create_checkpoint("test", [test_f])
|
|
183
|
+
with open(test_f, "w") as fp:
|
|
184
|
+
fp.write("changed")
|
|
185
|
+
success, _ = chk_mgr.rollback(chk.id)
|
|
186
|
+
assert success is True
|
|
187
|
+
with open(test_f, "r") as fp:
|
|
188
|
+
assert fp.read() == "initial"
|
|
189
|
+
results.append(SubsystemTestResult(
|
|
190
|
+
subsystem="Checkpoint & Rollback",
|
|
191
|
+
passed=True,
|
|
192
|
+
duration_ms=(time.time() - t0) * 1000,
|
|
193
|
+
details="Workspace snapshot & rollback OK",
|
|
194
|
+
))
|
|
195
|
+
except Exception as ex:
|
|
196
|
+
results.append(SubsystemTestResult(
|
|
197
|
+
subsystem="Checkpoint & Rollback",
|
|
198
|
+
passed=False,
|
|
199
|
+
duration_ms=(time.time() - t0) * 1000,
|
|
200
|
+
details="Checkpoint rollback test failed",
|
|
201
|
+
error=str(ex),
|
|
202
|
+
))
|
|
203
|
+
|
|
204
|
+
# 6. Experiment Ledger
|
|
205
|
+
t0 = time.time()
|
|
206
|
+
try:
|
|
207
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
208
|
+
ledger = ExperimentLedger(storage_path=f"{tmpdir}/exp.json")
|
|
209
|
+
exp = ledger.record_experiment(question="Self test question", dataset="test_ds")
|
|
210
|
+
assert exp.id.startswith("EXP-")
|
|
211
|
+
results.append(SubsystemTestResult(
|
|
212
|
+
subsystem="Experiment Ledger",
|
|
213
|
+
passed=True,
|
|
214
|
+
duration_ms=(time.time() - t0) * 1000,
|
|
215
|
+
details="Scientific Time Machine OK",
|
|
216
|
+
))
|
|
217
|
+
except Exception as ex:
|
|
218
|
+
results.append(SubsystemTestResult(
|
|
219
|
+
subsystem="Experiment Ledger",
|
|
220
|
+
passed=False,
|
|
221
|
+
duration_ms=(time.time() - t0) * 1000,
|
|
222
|
+
details="Experiment ledger test failed",
|
|
223
|
+
error=str(ex),
|
|
224
|
+
))
|
|
225
|
+
|
|
226
|
+
# 7. Security Layer
|
|
227
|
+
t0 = time.time()
|
|
228
|
+
try:
|
|
229
|
+
redacted = security_layer.redact_secrets("token: sk-abcdefghijklmnopqrstuvwxyz123456")
|
|
230
|
+
assert "sk-abcdefghijklmnopqrstuvwxyz123456" not in redacted
|
|
231
|
+
results.append(SubsystemTestResult(
|
|
232
|
+
subsystem="Security Layer",
|
|
233
|
+
passed=True,
|
|
234
|
+
duration_ms=(time.time() - t0) * 1000,
|
|
235
|
+
details="Credential redaction & guard OK",
|
|
236
|
+
))
|
|
237
|
+
except Exception as ex:
|
|
238
|
+
results.append(SubsystemTestResult(
|
|
239
|
+
subsystem="Security Layer",
|
|
240
|
+
passed=False,
|
|
241
|
+
duration_ms=(time.time() - t0) * 1000,
|
|
242
|
+
details="Security layer test failed",
|
|
243
|
+
error=str(ex),
|
|
244
|
+
))
|
|
245
|
+
|
|
246
|
+
all_passed = all(r.passed for r in results)
|
|
247
|
+
return SelfTestReport(results=results, all_passed=all_passed)
|