diff --git a/src/aipass/seedgo/apps/handlers/aipass_standards/json_structure_check.py b/src/aipass/seedgo/apps/handlers/aipass_standards/json_structure_check.py index 602c11fb..62685a07 100644 --- a/src/aipass/seedgo/apps/handlers/aipass_standards/json_structure_check.py +++ b/src/aipass/seedgo/apps/handlers/aipass_standards/json_structure_check.py @@ -1,26 +1,34 @@ # =================== AIPass ==================== # Name: json_structure_check.py # Description: JSON Structure Standards Checker Handler -# Version: 2.0.0 +# Version: 3.0.0 # Created: 2026-03-05 -# Modified: 2026-03-05 +# Modified: 2026-03-17 # ============================================= """ JSON Structure Standards Checker Handler -Validates JSON handling patterns for pip packages: -- json_handler.py uses relative paths (not hardcoded absolute) -- Modules use json_handler for JSON operations (not direct json.load/dump) -- Branch detection via AIPASS_REGISTRY.json and BRANCH_REGISTRY.json +Validates three-JSON code wiring in modules and handlers. + +For every .py file in apps/modules/ and apps/handlers/: + 1. Must import json_handler from the branch's handlers/json package + 2. Must call json_handler.log_operation() at least once + +For json_handler.py itself (in a json/ directory): + - Validates handler config (relative paths, no hardcoded absolutes) + +Entry points and other files outside modules/handlers are skipped. """ import re -import ast import json from pathlib import Path from typing import Dict, List, Optional +# Audit scope: scan every .py file, not just entry point +AUDIT_SCOPE = "all_files" + def is_bypassed(file_path: str, standard: str, line: int | None = None, bypass_rules: list | None = None) -> bool: """Check if a violation should be bypassed""" @@ -45,14 +53,29 @@ def check_module(module_path: str, bypass_rules: list | None = None) -> Dict: """ Check if module follows JSON structure standards. - For pip packages, validates: - 1. json_handler.py uses relative paths (Path(__file__).resolve().parent) - 2. json_handler.py does NOT use hardcoded absolute paths (BRANCH_ROOT, Path.home()) - 3. Modules use json_handler instead of direct json.load/dump + Routing: + a. json_handler.py in a json/ dir -> validate handler config + b. File in apps/modules/ -> check code wiring + c. File in apps/handlers/ -> check code wiring + d. Everything else (entry points) -> skip (not applicable) + + Args: + module_path: Path to Python module to check + bypass_rules: Optional bypass rules + + Returns: + dict with passed, checks, score, standard keys """ - checks = [] path = Path(module_path) + if is_bypassed(module_path, 'json_structure', bypass_rules=bypass_rules): + return { + 'passed': True, + 'checks': [{'name': 'Bypassed', 'passed': True, 'message': 'Standard bypassed via .seedgo/bypass.json'}], + 'score': 100, + 'standard': 'JSON STRUCTURE' + } + if not path.exists(): return { 'passed': False, @@ -61,9 +84,17 @@ def check_module(module_path: str, bypass_rules: list | None = None) -> Dict: 'standard': 'JSON STRUCTURE' } + # Skip __init__.py files + if path.name == '__init__.py': + return { + 'passed': True, + 'checks': [{'name': 'JSON structure check', 'passed': True, 'message': '__init__.py file (skipped)'}], + 'score': 100, + 'standard': 'JSON STRUCTURE' + } + try: - with open(path, 'r', encoding='utf-8') as f: - content = f.read() + content = path.read_text(encoding='utf-8') except Exception as e: return { 'passed': False, @@ -72,36 +103,166 @@ def check_module(module_path: str, bypass_rules: list | None = None) -> Dict: 'standard': 'JSON STRUCTURE' } - if 'json_handler' in path.name and path.parent.name == 'json': - checks = check_json_handler_config(path, content, bypass_rules) - elif 'apps/modules' in str(path): - checks = check_module_json_files(path, content, bypass_rules) + path_str = str(path) - if not checks: + # --- Case (a): json_handler.py in a json/ directory --- + if 'json_handler' in path.name and path.parent.name == 'json': + checks = _check_json_handler_config(path, content, bypass_rules) + passed_count = sum(1 for c in checks if c['passed']) + total = len(checks) + score = int((passed_count / total * 100)) if total > 0 else 0 + return { + 'passed': score >= 75, + 'checks': checks, + 'score': score, + 'standard': 'JSON STRUCTURE' + } + + # --- Determine if the file is in modules/ or handlers/ --- + in_modules = 'apps/modules' in path_str or 'apps\\modules' in path_str + in_handlers = 'apps/handlers' in path_str or 'apps\\handlers' in path_str + + # Exclude files inside the json/ handler directory itself (they ARE the + # json infrastructure, not consumers of it) + if in_handlers and path.parent.name == 'json': return { 'passed': True, - 'checks': [{'name': 'JSON structure check', 'passed': True, 'message': 'No JSON operations detected (skipped)'}], + 'checks': [{'name': 'JSON structure check', 'passed': True, + 'message': 'JSON handler infrastructure file (not applicable)'}], 'score': 100, 'standard': 'JSON STRUCTURE' } - passed_checks = sum(1 for check in checks if check['passed']) - total_checks = len(checks) - score = int((passed_checks / total_checks * 100)) if total_checks > 0 else 0 - overall_passed = score >= 75 + # --- Cases (b) and (c): code wiring check --- + if in_modules or in_handlers: + checks = _check_code_wiring(path, content) + passed_count = sum(1 for c in checks if c['passed']) + total = len(checks) + score = int((passed_count / total * 100)) if total > 0 else 0 + return { + 'passed': passed_count == total, + 'checks': checks, + 'score': score, + 'standard': 'JSON STRUCTURE' + } + # --- Case (d): file outside modules/ and handlers/ (entry point, etc.) --- return { - 'passed': overall_passed, - 'checks': checks, - 'score': score, + 'passed': True, + 'checks': [{'name': 'JSON structure check', 'passed': True, + 'message': 'Not in modules/ or handlers/ (not applicable)'}], + 'score': 100, 'standard': 'JSON STRUCTURE' } +# ------------------------------------------------------------------ +# Internal helpers +# ------------------------------------------------------------------ + +def _check_code_wiring(_path: Path, content: str) -> List[Dict]: + """ + Check that a module/handler file has the three-JSON wiring: + 1. Imports json_handler + 2. Calls json_handler.log_operation() + + Returns a list of two check dicts. + """ + checks: List[Dict] = [] + + # Check 1: imports json_handler + # Matches patterns like: + # from aipass.seedgo.apps.handlers.json import json_handler + # from aipass.flow.apps.handlers.json import json_handler + # from ...handlers.json import json_handler + has_import = bool( + re.search(r'from\s+\S*\.json\s+import\s+json_handler', content) + or re.search(r'from\s+\S*json\s+import\s+json_handler', content) + or re.search(r'import\s+json_handler', content) + ) + checks.append({ + 'name': 'json_handler import', + 'passed': has_import, + 'message': 'Imports json_handler' if has_import + else 'Missing json_handler import — add: from aipass..apps.handlers.json import json_handler' + }) + + # Check 2: calls json_handler.log_operation() + has_log_operation = 'json_handler.log_operation' in content + checks.append({ + 'name': 'log_operation call', + 'passed': has_log_operation, + 'message': 'Calls json_handler.log_operation()' if has_log_operation + else 'Missing json_handler.log_operation() call — every module/handler must log operations' + }) + + return checks + + +def _check_json_handler_config(_handler_path: Path, content: str, _bypass_rules: list | None = None) -> List[Dict]: + """ + Check json_handler.py for library profile. + + For pip packages: + - PASS if using relative paths (Path(__file__).resolve().parent) + - FAIL if using hardcoded absolute paths (Path.home(), BRANCH_ROOT, etc.) + """ + checks: List[Dict] = [] + + # Check 1: No hardcoded absolute paths + has_path_home = bool(re.search(r'Path\.home\(\)', content)) + # Only flag _ROOT constants that use Path.home() (legacy pattern) + # Allow _ROOT = Path(__file__).resolve()... (relative, pip-safe) + has_branch_root = False + for line in content.split('\n'): + stripped = line.strip() + if stripped.startswith('#'): + continue + if re.search(r'\w+_ROOT\s*=', stripped) and 'Path.home()' in stripped: + has_branch_root = True + break + + if has_path_home or has_branch_root: + issues = [] + if has_path_home: + issues.append('Path.home()') + if has_branch_root: + issues.append('hardcoded _ROOT constant') + checks.append({ + 'name': 'No absolute paths', + 'passed': False, + 'message': f'Found {", ".join(issues)} — pip packages should use relative paths' + }) + else: + checks.append({ + 'name': 'No absolute paths', + 'passed': True, + 'message': 'No hardcoded absolute paths (correct for pip packages)' + }) + + # Check 2: Uses relative path resolution + has_relative = bool(re.search(r'Path\(__file__\)', content) + or re.search(r'\.resolve\(\)', content) + or re.search(r'\.parent', content)) + + checks.append({ + 'name': 'Relative path resolution', + 'passed': has_relative, + 'message': 'Uses relative path resolution (Path(__file__).parent)' if has_relative + else 'Missing relative path resolution — should use Path(__file__).resolve().parent' + }) + + return checks + + +# ------------------------------------------------------------------ +# Utility functions (used by other code in the audit system) +# ------------------------------------------------------------------ + def _find_registry() -> Path: """Find AIPASS_REGISTRY.json by walking up from this file's location.""" current = Path(__file__).resolve().parent - for parent in [current] + list(current.parents): + for parent in [current, *list(current.parents)]: candidate = parent / "AIPASS_REGISTRY.json" if candidate.exists(): return candidate @@ -169,123 +330,3 @@ def get_branch_path(branch_name: str) -> Optional[str]: pass return None - - -def check_json_handler_config(handler_path: Path, content: str, bypass_rules: list | None = None) -> List[Dict]: # noqa: ARG001 - """ - Check json_handler.py for library profile. - - For pip packages: - - PASS if using relative paths (Path(__file__).resolve().parent, etc.) - - FAIL if using hardcoded absolute paths (Path.home(), BRANCH_ROOT, etc.) - """ - checks = [] - - # Check 1: No hardcoded absolute paths - has_path_home = bool(re.search(r'Path\.home\(\)', content)) - # Only flag _ROOT constants that use Path.home() (legacy pattern) - # Allow _ROOT = Path(__file__).resolve()... (relative, pip-safe) - has_branch_root = False - for line in content.split('\n'): - stripped = line.strip() - if stripped.startswith('#'): - continue - if re.search(r'\w+_ROOT\s*=', stripped) and 'Path.home()' in stripped: - has_branch_root = True - break - - if has_path_home or has_branch_root: - issues = [] - if has_path_home: - issues.append('Path.home()') - if has_branch_root: - issues.append('hardcoded _ROOT constant') - checks.append({ - 'name': 'No absolute paths', - 'passed': False, - 'message': f'Found {", ".join(issues)} — pip packages should use relative paths' - }) - else: - checks.append({ - 'name': 'No absolute paths', - 'passed': True, - 'message': 'No hardcoded absolute paths (correct for pip packages)' - }) - - # Check 2: Uses relative path resolution - has_relative = bool(re.search(r'Path\(__file__\)', content) or - re.search(r'\.resolve\(\)', content) or - re.search(r'\.parent', content)) - - checks.append({ - 'name': 'Relative path resolution', - 'passed': has_relative, - 'message': 'Uses relative path resolution (Path(__file__).parent)' if has_relative - else 'Missing relative path resolution — should use Path(__file__).resolve().parent' - }) - - return checks - - -def check_module_json_files(module_path: Path, content: str, bypass_rules: list | None = None) -> List[Dict]: - """ - Check if module uses json_handler for JSON operations. - For library profile, just validates json_handler usage pattern. - """ - checks = [] - - uses_json_handler = 'json_handler' in content and ('from' in content or 'import' in content) - - if not uses_json_handler: - json_check = check_json_handler_usage(content, str(module_path), bypass_rules) - if json_check: - checks.append(json_check) - return checks - - # Module uses json_handler — that's correct - checks.append({ - 'name': 'json_handler usage', - 'passed': True, - 'message': 'Module uses json_handler for JSON operations' - }) - - return checks - - -def check_json_handler_usage(content: str, file_path: str, bypass_rules: list | None = None) -> Optional[Dict]: - """ - Check that modules use json_handler for JSON operations (not direct json.load/dump). - """ - try: - tree = ast.parse(content) - except SyntaxError: - return None - - has_json_operations = False - has_json_handler_import = False - - for node in ast.walk(tree): - if isinstance(node, ast.ImportFrom): - if node.module and 'json_handler' in node.module: - has_json_handler_import = True - - if isinstance(node, ast.Call): - if isinstance(node.func, ast.Attribute): - if (isinstance(node.func.value, ast.Name) and - node.func.value.id == 'json' and - node.func.attr in ['dump', 'load']): - has_json_operations = True - - if not has_json_operations: - return None - - if has_json_operations and not has_json_handler_import: - if is_bypassed(file_path, 'json_structure', bypass_rules=bypass_rules): - return None - return { - 'name': 'json_handler usage', - 'passed': False, - 'message': 'Direct JSON operations detected (use json_handler for structured JSON management)' - } - - return None diff --git a/src/aipass/seedgo/apps/handlers/aipass_standards/json_structure_content.py b/src/aipass/seedgo/apps/handlers/aipass_standards/json_structure_content.py index 4454acda..3e541e73 100644 --- a/src/aipass/seedgo/apps/handlers/aipass_standards/json_structure_content.py +++ b/src/aipass/seedgo/apps/handlers/aipass_standards/json_structure_content.py @@ -1,9 +1,9 @@ # =================== AIPass ==================== # Name: json_structure_content.py # Description: JSON Structure Standards Content Handler -# Version: 1.0.0 +# Version: 2.0.0 # Created: 2026-03-05 -# Modified: 2026-03-05 +# Modified: 2026-03-17 # ============================================= """ @@ -13,74 +13,118 @@ Provides formatted JSON structure standards content. Module orchestrates, handler implements. """ + def get_json_structure_standards() -> str: - """Return formatted JSON structure standards content with Rich markup + """Return formatted JSON structure standards content with Rich markup. Returns: str: Formatted standards text with Rich styling """ lines = [ + "[bold red]JSON STRUCTURE STANDARD[/bold red]", + "", + "[bold cyan]CORE PRINCIPLE:[/bold cyan] Operational JSON Output", + "", + "[yellow]RULE:[/yellow] Every module and handler produces operational JSON", + " output via json_handler. The CODE PATTERN is the standard --", + " not JSON file templates. Files auto-create on first use.", + "", + "─" * 70, + "", "[bold cyan]THREE-JSON PATTERN:[/bold cyan]", "", - "[bold]Every module gets 3 auto-created files:[/bold]", - " • [yellow]module_config.json[/yellow] - Settings, limits (small, stable)", - " • [yellow]module_data.json[/yellow] - Metrics, state (medium, periodic)", - " • [yellow]module_log.json[/yellow] - Operations history (auto-rotating)", + " Every module/handler gets 3 auto-created files:", + " [yellow]config.json[/yellow] - Settings, limits (small, stable)", + " [yellow]data.json[/yellow] - Metrics, state (medium, periodic)", + " [yellow]log.json[/yellow] - Operations history (auto-rotating)", "", - "[yellow]RULE:[/yellow] Auto-created on first use - NEVER create manually", + " [bold]Location:[/bold] All JSON goes to [green]{branch}_json/[/green] at branch root", + " [dim]src/aipass/{branch}/{branch}_json/[/dim]", + " One directory, no splits.", "", - "[bold cyan]USAGE (AUTO-DETECTION):[/bold cyan]", - " [dim]from aipass.seedgo.apps.handlers.json import json_handler[/dim]", + "─" * 70, "", - " [dim]# Handler auto-detects calling module name:[/dim]", - " [dim]json_handler.log_operation(\"operation_name\", {\"key\": \"value\"})[/dim]", + "[bold cyan]REQUIRED CODE WIRING:[/bold cyan]", "", - " [dim]# Creates module_config.json, module_data.json, module_log.json[/dim]", - " [dim]# Based on templates in apps/json_templates/default/[/dim]", + " This is the standard. This is what agents copy.", + "", + " [dim]from aipass.{branch}.apps.handlers.json import json_handler[/dim]", + "", + " [dim]# At end of main operations:[/dim]", + " [dim]json_handler.log_operation(\"operation_name\", {[/dim]", + " [dim] \"key\": \"value\",[/dim]", + " [dim] \"success\": True[/dim]", + " [dim]})[/dim]", + "", + " [bold]Example (flow branch):[/bold]", + " [dim]from aipass.flow.apps.handlers.json import json_handler[/dim]", + "", + " [dim]json_handler.log_operation(\"plan_created\", {[/dim]", + " [dim] \"command\": command,[/dim]", + " [dim] \"args\": args[/dim]", + " [dim]})[/dim]", + "", + " [yellow]Both the import AND the log_operation call are required.[/yellow]", + "", + "─" * 70, + "", + "[bold cyan]json_handler.py SETUP:[/bold cyan]", + "", + " Auto-detects branch via [dim]Path(__file__)[/dim]. Universal across branches:", + "", + " [dim]_BRANCH_ROOT = Path(__file__).resolve().parents[3][/dim]", + " [dim]_BRANCH_NAME = _BRANCH_ROOT.name[/dim]", + " [dim]JSON_DIR = _BRANCH_ROOT / f\"{_BRANCH_NAME}_json\"[/dim]", + "", + " [green]No per-branch customization needed.[/green]", + " Spawn ships the template, branches just copy it.", + "", + "─" * 70, + "", + "[bold cyan]SCORING:[/bold cyan]", + "", + " [bold]2 checks per module/handler:[/bold]", + " 1. [dim]json_handler[/dim] import present", + " 2. [dim]log_operation()[/dim] call present", + "", + " Both must pass. Score = compliant files / total files * 100", + "", + "─" * 70, + "", + "[bold cyan]WHAT'S CHECKED:[/bold cyan]", + "", + " [green]Checked:[/green]", + " [green]+[/green] Modules: [dim]apps/modules/*.py[/dim]", + " [green]+[/green] Handlers: [dim]apps/handlers/**/*.py[/dim]", + "", + " [red]Skipped:[/red]", + " [red]-[/red] [dim]__init__.py[/dim] files (structural, not functional)", + " [red]-[/red] Entry points (not required to wire JSON)", + " [red]-[/red] [dim]json_handler.py[/dim] itself (checked for path validation only)", + "", + "─" * 70, + "", + "[bold cyan]LOG ROTATION:[/bold cyan]", + "", + " Set [dim]max_log_entries[/dim] in config.json.", + " Handler auto-rotates when limit reached (FIFO).", + " Keeps most recent N entries.", "", - "[bold cyan]LOG ROTATION (CRITICAL):[/bold cyan]", - " Set in config.json:", " [dim]{[/dim]", - " [dim]\"config\": { \"max_log_entries\": 100 }[/dim]", + " [dim] \"config\": { \"max_log_entries\": 100 }[/dim]", " [dim]}[/dim]", "", - " • Handler auto-rotates when limit reached", - " • Keeps most recent N entries (FIFO)", - " • [yellow]⚠️ Prevents log explosion (drone logs hit 171KB/7001 entries)[/yellow]", + "─" * 70, "", - "[bold cyan]LOG STRUCTURE:[/bold cyan]", - " Array of entries (NOT object with \"entries\" key):", - " [dim][[/dim]", - " [dim]{ \"timestamp\": \"...\", \"operation\": \"...\", \"data\": {...} }[/dim]", - " [dim]][/dim]", - "", - "[bold cyan]LOCATIONS:[/bold cyan]", - " • Package JSON: [dim]src/aipass/{module}/aipass_json/[/dim]", - " • Per-module: [dim]src/aipass/{module}/{module}_json/[/dim]", - "", - "[bold red]SETUP json_handler.py (MANDATORY):[/bold red]", - " [red]✗ DO NOT copy seedgo's handler without changing paths![/red]", - "", - " [green]✓ Update BRANCH_ROOT:[/green]", - " [dim]{BRANCH}_ROOT = Path(__file__).resolve().parents[N][/dim]", - "", - " [green]✓ Update JSON_DIR:[/green]", - " [dim]{BRANCH}_JSON_DIR = {BRANCH}_ROOT / \"{branch}_json\"[/dim]", - "", - " [green]✓ Update TEMPLATES_DIR:[/green]", - " [dim]JSON_TEMPLATES_DIR = {BRANCH}_ROOT / \"apps\" / \"json_templates\"[/dim]", - "", - " [yellow]Validate:[/yellow] Run standards checker on json_handler.py", - " [dim]Expected: 100/100 on JSON STRUCTURE standard[/dim]", - "", - "[bold cyan]SPECIAL JSONS:[/bold cyan]", - " • [bold]Registries:[/bold] Collections (AIPASS_REGISTRY.json)", - " • [bold]Custom:[/bold] Module-specific needs (no limit)", - " • [dim]Not part of three-JSON pattern[/dim]", + "[bold cyan]KEY WARNINGS:[/bold cyan]", + " [yellow]![/yellow] The CODE PATTERN is the template -- no json_templates/ directory", + " [yellow]![/yellow] JSON files auto-create on first log_operation() call", + " [yellow]![/yellow] Never create JSON files manually", + " [yellow]![/yellow] Both import AND log_operation required for compliance", "", "[bold cyan]REFERENCE:[/bold cyan]", " [dim]See: seedgo standards pack (json_structure)[/dim]", - " [dim]See: src/aipass/seedgo/apps/standards/aipass/handlers/json/json_handler.py[/dim]", + " [dim]See: FPLAN-0056 (Three-JSON Pattern Redesign)[/dim]", ] return "\n".join(lines) diff --git a/src/aipass/seedgo/apps/handlers/aipass_standards/log_structure_check.py b/src/aipass/seedgo/apps/handlers/aipass_standards/log_structure_check.py index 41e65469..f8cf7eeb 100644 --- a/src/aipass/seedgo/apps/handlers/aipass_standards/log_structure_check.py +++ b/src/aipass/seedgo/apps/handlers/aipass_standards/log_structure_check.py @@ -1,16 +1,18 @@ # =================== AIPass ==================== # Name: log_structure_check.py # Description: Log Structure Standards Checker Handler -# Version: 1.1.0 +# Version: 1.2.0 # Created: 2026-03-06 -# Modified: 2026-03-08 +# Modified: 2026-03-17 # ============================================= """ Log Structure Standards Checker Handler -Validates that AIPass modules follow hierarchical log placement: -every directory containing .py code should have a sibling logs/ directory. +Validates the two-tier logging model: + - system_logs/ at repo root (system-wide) + - logs/ at branch root only (per-branch) +No hierarchical logs/ at every nested directory. No hardcoded absolute log paths. """ @@ -37,10 +39,10 @@ def is_bypassed(file_path: str, standard: str, bypass_rules: list | None = None) def check_module(module_path: str, bypass_rules: list | None = None) -> Dict: """ - Check module logging structure with hierarchical placement validation. + Check module logging structure against the two-tier model. Checks: - 1. Parent directory of file has a sibling logs/ directory (hierarchical placement) + 1. logs/ directory exists at branch root (entry file's parent) 2. No hardcoded absolute log paths in source 3. No /home/ references in logging configuration @@ -70,16 +72,16 @@ def check_module(module_path: str, bypass_rules: list | None = None) -> Dict: 'standard': 'LOG_STRUCTURE' } - # Check 1: Hierarchical log placement — logs/ directory at the same level as the file - parent_dir = path.parent - logs_dir = parent_dir / "logs" + # Check 1: Branch-root log placement — logs/ directory at the branch root + branch_root = path.parent + logs_dir = branch_root / "logs" has_logs_dir = logs_dir.is_dir() checks.append({ - 'name': 'Hierarchical logs/ directory', + 'name': 'Branch-root logs/ directory', 'passed': has_logs_dir, - 'message': f'logs/ directory exists at {parent_dir}/' + 'message': f'logs/ directory exists at branch root {branch_root}/' if has_logs_dir - else f'Missing logs/ directory at {parent_dir}/ — hierarchical placement requires logs/ at every code level' + else f'Missing logs/ directory at branch root {branch_root}/ — two-tier model requires logs/ at branch root' }) # Check 2-3: Scan file for hardcoded log paths diff --git a/src/aipass/seedgo/apps/handlers/aipass_standards/log_structure_content.py b/src/aipass/seedgo/apps/handlers/aipass_standards/log_structure_content.py index 7f72efc5..a3bc7d56 100644 --- a/src/aipass/seedgo/apps/handlers/aipass_standards/log_structure_content.py +++ b/src/aipass/seedgo/apps/handlers/aipass_standards/log_structure_content.py @@ -1,62 +1,57 @@ # =================== AIPass ==================== # Name: log_structure_content.py # Description: Log Structure Standards Content Handler -# Version: 1.1.0 +# Version: 1.2.0 # Created: 2026-03-06 -# Modified: 2026-03-08 +# Modified: 2026-03-17 # ============================================= """ Log Structure Standards Content Handler Provides formatted display of log structure standards for terminal output. -Describes the hierarchical log placement model where every directory -containing .py code has a sibling logs/ directory. +Describes the two-tier logging model: system_logs/ at repo root, +logs/ at branch root only. """ def get_log_structure_standards() -> str: """Return formatted log structure standards for display.""" lines = [ - "[bold red]HIERARCHICAL LOG STRUCTURE[/bold red]", + "[bold red]TWO-TIER LOG STRUCTURE[/bold red]", "", - "[yellow]CORE PRINCIPLE:[/yellow] Logs live where the code lives — every directory", - " containing .py files should have a sibling [bold]logs/[/bold] directory", + "[yellow]CORE PRINCIPLE:[/yellow] Two tiers, two locations. System-wide logs aggregate", + " at repo root; per-branch logs live at the branch root only.", "", - "[bold cyan]DUAL LOGGING MODEL:[/bold cyan]", - " [green]1.[/green] [bold]system_logs/[/bold] at repo root — central aggregation ({branch}_{module}.log)", - " [green]2.[/green] [bold]logs/[/bold] at every code level — hierarchical local placement", + "[bold cyan]TWO-TIER MODEL:[/bold cyan]", + " [green]1.[/green] [bold]system_logs/[/bold] at repo root — system-wide aggregation ({branch}_{module}.log)", + " [green]2.[/green] [bold]logs/[/bold] at branch root — per-branch local logs", "", "[bold cyan]REQUIREMENTS:[/bold cyan]", - " [green]1.[/green] Every directory with .py files has a sibling [bold]logs/[/bold] directory", + " [green]1.[/green] [bold]logs/[/bold] directory exists at the branch root", " [green]2.[/green] No hardcoded absolute paths to .log files", " [green]3.[/green] No /home/ references in logging configuration", "", - "[bold cyan]HIERARCHICAL DIRECTORY PATTERN:[/bold cyan]", - " [dim]src/aipass/{branch}/[/dim]", - " [dim] logs/ <-- branch root logs[/dim]", - " [dim] apps/[/dim]", - " [dim] logs/ <-- entry point level[/dim]", - " [dim] modules/[/dim]", - " [dim] logs/ <-- module level[/dim]", - " [dim] handlers/[/dim]", - " [dim] logs/ <-- handler root[/dim]", - " [dim] dispatch/[/dim]", - " [dim] logs/ <-- sub-handler level[/dim]", - " [dim] email/[/dim]", - " [dim] logs/ <-- sub-handler level[/dim]", + "[bold cyan]DIRECTORY LAYOUT:[/bold cyan]", + " [dim]repo_root/[/dim]", + " [dim] system_logs/ <-- system-wide logs[/dim]", + " [dim] src/aipass/{branch}/[/dim]", + " [dim] logs/ <-- branch-level logs (only here)[/dim]", + " [dim] apps/[/dim]", + " [dim] handlers/[/dim]", + " [dim] modules/[/dim]", "", "[bold cyan]LOG PATH PATTERN:[/bold cyan]", - " [green]CORRECT:[/green] Use prax's system_logger (auto-routes to correct location)", + " [green]CORRECT:[/green] Use prax's system_logger (auto-routes to correct tier)", " [dim] from aipass.prax.apps.modules.logger import system_logger[/dim]", - " [dim] system_logger.info('message') # prax handles hierarchical placement[/dim]", + " [dim] system_logger.info('message') # prax handles tier routing[/dim]", "", - " [green]ALSO OK:[/green] Manual relative path to sibling logs/", - " [dim] LOG_DIR = Path(__file__).resolve().parent / 'logs'[/dim]", + " [green]ALSO OK:[/green] Relative path to branch-root logs/", + " [dim] LOG_DIR = Path(__file__).resolve().parents[2] / 'logs'[/dim]", " [dim] log_path = LOG_DIR / 'my_handler.log'[/dim]", "", - " [red]WRONG:[/red] All logs dumped at branch root only", - " [dim] LOG_DIR = Path(__file__).resolve().parents[3] / 'logs'[/dim]", - " [dim] # All handlers write to branch/logs/ — no hierarchy[/dim]", + " [red]WRONG:[/red] logs/ at nested directories (old hierarchical pattern)", + " [dim] LOG_DIR = Path(__file__).resolve().parent / 'logs'[/dim]", + " [dim] # Creates logs/ inside handlers/ or modules/ — not two-tier[/dim]", "", " [red]WRONG:[/red] Hardcoded absolute paths", " [dim] path = '/absolute/path/to/system_logs/module.log'[/dim]", @@ -64,6 +59,5 @@ def get_log_structure_standards() -> str: "", "[bold cyan]REFERENCE:[/bold cyan]", " [dim]Prax logger: src/aipass/prax/apps/modules/logger.py[/dim]", - " [dim]Hierarchical resolver: src/aipass/prax/apps/handlers/config/load.py[/dim]", ] return "\n".join(lines) diff --git a/src/aipass/seedgo/apps/handlers/aipass_standards/meta_check.py b/src/aipass/seedgo/apps/handlers/aipass_standards/meta_check.py index 1d391a2f..6ac53663 100644 --- a/src/aipass/seedgo/apps/handlers/aipass_standards/meta_check.py +++ b/src/aipass/seedgo/apps/handlers/aipass_standards/meta_check.py @@ -1,9 +1,9 @@ # =================== AIPass ==================== # Name: meta_check.py # Description: META Block Standards Checker Handler -# Version: 1.1.0 +# Version: 1.2.0 # Created: 2026-03-05 -# Modified: 2026-03-08 +# Modified: 2026-03-16 # ============================================= """ @@ -30,6 +30,7 @@ import re from pathlib import Path from typing import Dict, List +AUDIT_SCOPE = "all_files" # Header/footer markers for library META # Accept both AIPass (canonical) and META (legacy) header markers diff --git a/src/aipass/seedgo/apps/handlers/json/json_handler.py b/src/aipass/seedgo/apps/handlers/json/json_handler.py index 1434e41a..cdacfa51 100755 --- a/src/aipass/seedgo/apps/handlers/json/json_handler.py +++ b/src/aipass/seedgo/apps/handlers/json/json_handler.py @@ -1,21 +1,21 @@ # =================== AIPass ==================== # Name: json_handler.py -# Description: Json Handler -# Version: 1.0.0 +# Description: Auto-Creating JSON Handler +# Version: 1.1.0 # Created: 2026-03-05 -# Modified: 2026-03-05 +# Modified: 2026-03-17 # ============================================= import json from pathlib import Path from datetime import datetime -from typing import Dict, List, Any, Optional -import sys +from typing import Dict, Any, Optional import inspect -PACK_ROOT = Path(__file__).resolve().parent.parent.parent # json/ -> handlers/ -> aipass/ -SEEDGO_JSON_DIR = PACK_ROOT / "aipass_json" -JSON_TEMPLATES_DIR = PACK_ROOT / "json_templates" +_BRANCH_ROOT = Path(__file__).resolve().parents[3] # json/ -> handlers/ -> apps/ -> {branch}/ +_BRANCH_NAME = _BRANCH_ROOT.name +JSON_DIR = _BRANCH_ROOT / f"{_BRANCH_NAME}_json" +JSON_TEMPLATES_DIR = _BRANCH_ROOT / "apps" / "json_templates" def _get_caller_module_name() -> str: @@ -82,12 +82,12 @@ def validate_json_structure(data: Any, json_type: str) -> bool: def get_json_path(module_name: str, json_type: str) -> Path: """Get path for module JSON file""" filename = f"{module_name}_{json_type}.json" - return SEEDGO_JSON_DIR / filename + return JSON_DIR / filename def ensure_json_exists(module_name: str, json_type: str) -> bool: """Ensure JSON file exists, create from template if missing""" - SEEDGO_JSON_DIR.mkdir(parents=True, exist_ok=True) + JSON_DIR.mkdir(parents=True, exist_ok=True) json_path = get_json_path(module_name, json_type) @@ -238,16 +238,17 @@ if __name__ == "__main__": border_style="bright_blue" )) console.print() - console.print("[yellow]TESTING:[/yellow] Creating seedgo JSONs...") + console.print(f"[yellow]TESTING:[/yellow] Creating {_BRANCH_NAME} JSONs...") + console.print(f"[dim]JSON_DIR: {JSON_DIR}[/dim]") # Test auto-creation - log_operation("test_operation", {"test": "data"}, "seedgo") - increment_counter("seedgo", "test_counter", 1) - update_data_metrics("seedgo", test_metric="working") + log_operation("test_operation", {"test": "data"}, _BRANCH_NAME) + increment_counter(_BRANCH_NAME, "test_counter", 1) + update_data_metrics(_BRANCH_NAME, test_metric="working") console.print() - console.print("[green]Check aipass_json/ directory for created files:[/green]") - console.print(" [dim]•[/dim] seedgo_config.json") - console.print(" [dim]•[/dim] seedgo_data.json") - console.print(" [dim]•[/dim] seedgo_log.json") + console.print(f"[green]Check {JSON_DIR.relative_to(_BRANCH_ROOT)}/ for created files:[/green]") + console.print(f" [dim]•[/dim] {_BRANCH_NAME}_config.json") + console.print(f" [dim]•[/dim] {_BRANCH_NAME}_data.json") + console.print(f" [dim]•[/dim] {_BRANCH_NAME}_log.json") console.print()