hyperflash 1.0.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.
hyperflash/core.py ADDED
@@ -0,0 +1,419 @@
1
+ """
2
+ Core HyperFlash runtime logic: invariant enforcement, AST symbol graph generation,
3
+ contract state management, and hierarchical verification runner.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ import ast
8
+ import json
9
+ import py_compile
10
+ import re
11
+ import shutil
12
+ import subprocess
13
+ import time
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+ BANNED_ELISION_PATTERNS: list[str] = [
18
+ r"//\s*\.\.\.\s*(rest|existing|unchanged|remaining|code)",
19
+ r"#\s*\.\.\.\s*(rest|existing|unchanged|remaining|code)",
20
+ r"/\*\s*\.\.\.\s*.*?\*/",
21
+ r"TODO:\s*implement",
22
+ r"//\s*\.\.\.\s*$",
23
+ r"#\s*\.\.\.\s*$",
24
+ r"/\*\s*existing code.*?\*/",
25
+ r"<!--\s*\.\.\.\s*-->",
26
+ ]
27
+
28
+ IGNORE_DIRS: set[str] = {
29
+ ".git",
30
+ ".agents",
31
+ "node_modules",
32
+ "__pycache__",
33
+ ".venv",
34
+ "venv",
35
+ "build",
36
+ "dist",
37
+ ".pytest_cache",
38
+ ".ruff_cache",
39
+ }
40
+
41
+ DEFAULT_CONTRACT: dict[str, Any] = {
42
+ "mode": "implement",
43
+ "effort_profile": "balanced",
44
+ "planning": "compact",
45
+ "context_depth": "dependency",
46
+ "verification_depth": "impact",
47
+ "delegation": "conditional",
48
+ "reason_codes": [],
49
+ "allowed_write_scope": [],
50
+ "acceptance_criteria": [],
51
+ "files_read_in_session": [],
52
+ "last_mutation_ts": 0.0,
53
+ "last_verified_ts": 0.0,
54
+ "last_verdict": "PENDING",
55
+ }
56
+
57
+
58
+ def check_for_elision(text: str) -> tuple[bool, str]:
59
+ """
60
+ Scans text for prohibited lazy truncation and placeholder patterns.
61
+
62
+ Returns:
63
+ tuple[bool, str]: (True, matched_pattern) if elision found, else (False, "")
64
+ """
65
+ for pattern in BANNED_ELISION_PATTERNS:
66
+ match = re.search(pattern, text, re.IGNORECASE | re.MULTILINE)
67
+ if match:
68
+ return True, match.group(0)
69
+ return False, ""
70
+
71
+
72
+ def enforce_elision_guard(text: str) -> None:
73
+ """
74
+ Raises ValueError if prohibited elision patterns are detected in the given text.
75
+ """
76
+ has_elision, pattern = check_for_elision(text)
77
+ if has_elision:
78
+ raise ValueError(
79
+ f"Invariant 7 Violation (Code Truncation Blocked): Prohibited elision pattern '{pattern}' detected. "
80
+ "HyperFlash requires 100% complete, fully implemented code with zero shortcuts."
81
+ )
82
+
83
+
84
+ def index_python_source(source_code: str, file_label: str = "<source>") -> dict[str, Any]:
85
+ """
86
+ Parses Python source code into an AST and extracts structured symbol metadata.
87
+ """
88
+ try:
89
+ tree = ast.parse(source_code, filename=file_label)
90
+ except SyntaxError:
91
+ return {}
92
+
93
+ symbols: dict[str, Any] = {
94
+ "classes": {},
95
+ "functions": [],
96
+ "imports": [],
97
+ }
98
+
99
+ for node in tree.body:
100
+ if isinstance(node, (ast.Import, ast.ImportFrom)):
101
+ try:
102
+ symbols["imports"].append(ast.unparse(node))
103
+ except Exception:
104
+ pass
105
+ elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
106
+ args: list[str] = [a.arg for a in node.args.args]
107
+ ret_type: str = "Untyped"
108
+ if node.returns is not None:
109
+ try:
110
+ ret_type = ast.unparse(node.returns)
111
+ except Exception:
112
+ ret_type = "Complex"
113
+ prefix: str = "async def" if isinstance(node, ast.AsyncFunctionDef) else "def"
114
+ symbols["functions"].append(
115
+ f"L{node.lineno}: {prefix} {node.name}({', '.join(args)}) -> {ret_type}"
116
+ )
117
+ elif isinstance(node, ast.ClassDef):
118
+ methods: list[str] = []
119
+ for item in node.body:
120
+ if isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef)):
121
+ m_prefix: str = "async " if isinstance(item, ast.AsyncFunctionDef) else ""
122
+ m_args: list[str] = [a.arg for a in item.args.args]
123
+ m_ret: str = "Untyped"
124
+ if item.returns is not None:
125
+ try:
126
+ m_ret = ast.unparse(item.returns)
127
+ except Exception:
128
+ m_ret = "Complex"
129
+ methods.append(f"L{item.lineno}:{m_prefix}{item.name}({', '.join(m_args)}) -> {m_ret}")
130
+ symbols["classes"][f"L{node.lineno}:{node.name}"] = methods
131
+
132
+ return {k: v for k, v in symbols.items() if v}
133
+
134
+
135
+ def index_python_file(path: Path) -> dict[str, Any]:
136
+ """
137
+ Indexes a single Python file on disk into symbol metadata.
138
+ """
139
+ if not path.is_file():
140
+ return {}
141
+ try:
142
+ content: str = path.read_text(encoding="utf-8", errors="ignore")
143
+ return index_python_source(content, str(path))
144
+ except Exception:
145
+ return {}
146
+
147
+
148
+ def generate_symbol_graph(root_dir: Path | str = ".") -> dict[str, dict[str, Any]]:
149
+ """
150
+ Crawls root_dir and indexes all Python files into an AST symbol graph.
151
+ """
152
+ base_path = Path(root_dir).resolve()
153
+ graph: dict[str, dict[str, Any]] = {}
154
+
155
+ if not base_path.exists():
156
+ return graph
157
+
158
+ for py_file in base_path.rglob("*.py"):
159
+ if any(part in IGNORE_DIRS for part in py_file.parts):
160
+ continue
161
+ info = index_python_file(py_file)
162
+ if info:
163
+ try:
164
+ rel_path = py_file.relative_to(base_path).as_posix()
165
+ except ValueError:
166
+ rel_path = py_file.as_posix()
167
+ graph[rel_path] = info
168
+
169
+ return graph
170
+
171
+
172
+ def build_symbol_graph(
173
+ root_dir: Path | str = ".",
174
+ output_file: Path | str = ".agents/state/symbol_graph.json",
175
+ ) -> dict[str, dict[str, Any]]:
176
+ """
177
+ Generates symbol graph and writes it to output_file on disk.
178
+ """
179
+ base_path = Path(root_dir)
180
+ out_path = Path(output_file) if Path(output_file).is_absolute() else base_path / output_file
181
+ graph = generate_symbol_graph(base_path)
182
+
183
+ out_path.parent.mkdir(parents=True, exist_ok=True)
184
+ out_path.write_text(json.dumps(graph, indent=2), encoding="utf-8")
185
+ return graph
186
+
187
+
188
+ def load_contract(
189
+ contract_path: Path | str = ".agents/state/contract.json",
190
+ ) -> dict[str, Any]:
191
+ """
192
+ Loads contract state safely from JSON file, returning DEFAULT_CONTRACT on error.
193
+ """
194
+ path = Path(contract_path)
195
+ if not path.is_file():
196
+ return dict(DEFAULT_CONTRACT)
197
+ try:
198
+ data = json.loads(path.read_text(encoding="utf-8"))
199
+ merged = dict(DEFAULT_CONTRACT)
200
+ merged.update(data)
201
+ return merged
202
+ except Exception:
203
+ return dict(DEFAULT_CONTRACT)
204
+
205
+
206
+ def save_contract(
207
+ contract_data: dict[str, Any],
208
+ contract_path: Path | str = ".agents/state/contract.json",
209
+ ) -> None:
210
+ """
211
+ Safely serializes and saves contract data to contract_path.
212
+ """
213
+ path = Path(contract_path)
214
+ path.parent.mkdir(parents=True, exist_ok=True)
215
+ path.write_text(json.dumps(contract_data, indent=2), encoding="utf-8")
216
+
217
+
218
+ def run_cmd(cmd: list[str], cwd: Path, timeout: int = 120) -> tuple[int, str]:
219
+ """
220
+ Executes a shell command safely, capturing stdout and stderr.
221
+ """
222
+ try:
223
+ res = subprocess.run(
224
+ cmd,
225
+ cwd=str(cwd),
226
+ capture_output=True,
227
+ text=True,
228
+ timeout=timeout,
229
+ )
230
+ combined: str = (res.stdout + "\n" + res.stderr).strip()
231
+ truncated: str = combined[-4000:] if len(combined) > 4000 else combined
232
+ return res.returncode, truncated
233
+ except Exception as exc:
234
+ return 1, str(exc)
235
+
236
+
237
+ def run_verification(
238
+ tier: int | None = None,
239
+ custom_cmd: str = "",
240
+ workspace_dir: Path | str = ".",
241
+ ) -> dict[str, Any]:
242
+ """
243
+ Executes the HyperFlash deterministic verification hierarchy and updates contract.json.
244
+ """
245
+ workspace = Path(workspace_dir).resolve()
246
+ contract_path = workspace / ".agents/state/contract.json"
247
+ diagnostics: list[dict[str, Any]] = []
248
+ passed_all: bool = True
249
+
250
+ def tier_active(t: int) -> bool:
251
+ return tier is None or t <= tier
252
+
253
+ # Gather Python source files
254
+ py_files: list[Path] = [
255
+ p for p in workspace.rglob("*.py")
256
+ if not any(part in IGNORE_DIRS for part in p.parts)
257
+ ]
258
+
259
+ # Tier 1: Syntax Integrity (py_compile)
260
+ if tier_active(1) and py_files:
261
+ for py_f in py_files:
262
+ try:
263
+ py_compile.compile(str(py_f), doraise=True)
264
+ except py_compile.PyCompileError as err:
265
+ passed_all = False
266
+ diagnostics.append({
267
+ "tier": 1,
268
+ "name": "syntax_integrity",
269
+ "tool": "py_compile",
270
+ "file": str(py_f.relative_to(workspace) if py_f.is_relative_to(workspace) else py_f),
271
+ "error": str(err),
272
+ })
273
+
274
+ # Tier 3: Static Type Soundness (tsc / mypy)
275
+ if tier_active(3):
276
+ tsconfig = workspace / "tsconfig.json"
277
+ if tsconfig.exists() and shutil.which("npx"):
278
+ code, out = run_cmd(["npx", "tsc", "--noEmit"], cwd=workspace)
279
+ diagnostics.append({
280
+ "tier": 3,
281
+ "name": "type_soundness",
282
+ "tool": "tsc",
283
+ "exit_code": code,
284
+ "output": out,
285
+ })
286
+ if code != 0:
287
+ passed_all = False
288
+
289
+ # Tier 5: Static Analysis / Linting (ruff)
290
+ if tier_active(5) and py_files and shutil.which("ruff"):
291
+ code, out = run_cmd(["ruff", "check", "."], cwd=workspace)
292
+ diagnostics.append({
293
+ "tier": 5,
294
+ "name": "static_analysis",
295
+ "tool": "ruff",
296
+ "exit_code": code,
297
+ "output": out,
298
+ })
299
+ if code != 0:
300
+ passed_all = False
301
+
302
+ # Tier 6: Workspace Test Suite (pytest)
303
+ has_py_tests = any(p.name.startswith("test_") or p.name.endswith("_test.py") for p in py_files)
304
+ if tier_active(6) and has_py_tests and shutil.which("pytest"):
305
+ code, out = run_cmd(["pytest", "-q", "--tb=short"], cwd=workspace)
306
+ diagnostics.append({
307
+ "tier": 6,
308
+ "name": "workspace_test_suite",
309
+ "tool": "pytest",
310
+ "exit_code": code,
311
+ "output": out,
312
+ })
313
+ if code != 0:
314
+ passed_all = False
315
+
316
+ # Custom Command if provided
317
+ if custom_cmd:
318
+ code, out = run_cmd(custom_cmd.split(), cwd=workspace)
319
+ diagnostics.append({
320
+ "tier": 0,
321
+ "name": "custom_validator",
322
+ "command": custom_cmd,
323
+ "exit_code": code,
324
+ "output": out,
325
+ })
326
+ if code != 0:
327
+ passed_all = False
328
+
329
+ verdict_str: str = "VERDICT: PASS" if passed_all else "VERDICT: FAIL -- Verification checks failed"
330
+
331
+ # Update contract state
332
+ contract = load_contract(contract_path)
333
+ if passed_all:
334
+ contract["last_verified_ts"] = time.time()
335
+ contract["last_verdict"] = "PASS"
336
+ else:
337
+ contract["last_verdict"] = "FAIL"
338
+ save_contract(contract, contract_path)
339
+
340
+ return {
341
+ "verdict": verdict_str,
342
+ "passed": passed_all,
343
+ "diagnostics": diagnostics,
344
+ "timestamp": time.time(),
345
+ }
346
+
347
+
348
+ class HyperFlashRuntime:
349
+ """
350
+ High-level API for interacting with HyperFlash runtime within Python.
351
+ """
352
+
353
+ def __init__(self, workspace_dir: Path | str = ".") -> None:
354
+ self.workspace_dir: Path = Path(workspace_dir).resolve()
355
+ self.contract_path: Path = self.workspace_dir / ".agents/state/contract.json"
356
+ self.symbol_graph_path: Path = self.workspace_dir / ".agents/state/symbol_graph.json"
357
+
358
+ def get_contract(self) -> dict[str, Any]:
359
+ """Returns active contract state."""
360
+ return load_contract(self.contract_path)
361
+
362
+ def init_contract(
363
+ self,
364
+ mode: str = "implement",
365
+ allowed_scope: list[str] | None = None,
366
+ acceptance_criteria: list[str] | None = None,
367
+ effort_profile: str = "balanced",
368
+ ) -> dict[str, Any]:
369
+ """Initializes or overwrites contract state."""
370
+ contract = load_contract(self.contract_path)
371
+ contract["mode"] = mode
372
+ contract["effort_profile"] = effort_profile
373
+ contract["allowed_write_scope"] = allowed_scope or []
374
+ contract["acceptance_criteria"] = acceptance_criteria or []
375
+ contract["last_verdict"] = "PENDING"
376
+ save_contract(contract, self.contract_path)
377
+ return contract
378
+
379
+ def generate_symbol_graph(self) -> dict[str, dict[str, Any]]:
380
+ """Re-indexes workspace AST symbol graph and stores to disk."""
381
+ return build_symbol_graph(self.workspace_dir, self.symbol_graph_path)
382
+
383
+ def verify(self, tier: int | None = None, custom_cmd: str = "") -> dict[str, Any]:
384
+ """Executes verification hierarchy."""
385
+ return run_verification(tier=tier, custom_cmd=custom_cmd, workspace_dir=self.workspace_dir)
386
+
387
+ def safe_patch(self, target_file: str, search_text: str, replacement_text: str) -> str:
388
+ """
389
+ Applies surgical search-and-replace edit with Invariant 7 (Zero Elision)
390
+ and Invariant 2 (Scope Confinement) validation.
391
+ """
392
+ # Invariant 7: Elision guard
393
+ enforce_elision_guard(replacement_text)
394
+
395
+ target_path = (self.workspace_dir / target_file).resolve()
396
+ norm_target = str(target_path.as_posix())
397
+
398
+ # Invariant 2: Scope Confinement
399
+ contract = self.get_contract()
400
+ allowed_scope = [str(Path(p).as_posix()) for p in contract.get("allowed_write_scope", [])]
401
+ if allowed_scope:
402
+ if not any(norm_target.endswith(s) or s in norm_target for s in allowed_scope):
403
+ raise PermissionError(
404
+ f"Invariant 2 Violation (Out of Scope): '{target_file}' is outside allowed_write_scope {allowed_scope}."
405
+ )
406
+
407
+ if not target_path.exists():
408
+ raise FileNotFoundError(f"Target file '{target_file}' does not exist.")
409
+
410
+ content = target_path.read_text(encoding="utf-8")
411
+ if search_text not in content:
412
+ raise ValueError(f"Search text not found in '{target_file}'.")
413
+
414
+ new_content = content.replace(search_text, replacement_text, 1)
415
+ target_path.write_text(new_content, encoding="utf-8")
416
+
417
+ contract["last_mutation_ts"] = time.time()
418
+ save_contract(contract, self.contract_path)
419
+ return f"Successfully patched '{target_file}'."