From edc43ace3e8aa9cd37cf6bc06fc8a2bb03b55bc2 Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Thu, 5 Mar 2026 19:46:05 -0800 Subject: [PATCH] Fix seedgo standard checkers for pip environment - Replace hardcoded Path(__file__).parents[8] with walk-up registry finder - Update prax import detection from Dev-Pass pattern to aipass.* namespace - Fix BRANCH_REGISTRY_PATH undefined error in encapsulation_check - Add .seed/ and tools/ to gitignore (runtime artifacts) Co-Authored-By: Claude Opus 4.6 --- .gitignore | 6 +- .../handlers/standards/encapsulation_check.py | 31 +++++-- .../standards/error_handling_check.py | 12 ++- .../standards/json_structure_check.py | 84 ++++++++----------- 4 files changed, 69 insertions(+), 64 deletions(-) diff --git a/.gitignore b/.gitignore index 09211824..777253e3 100644 --- a/.gitignore +++ b/.gitignore @@ -36,7 +36,11 @@ site/ DASHBOARD.local.json flow.local.md artifacts/ -tools/verify_branch.py +tools/ aipass_json/ + +# Seedgo runtime artifacts (created by seedgo audit) +.seed/ +.standards/ # AIPASS_REGISTRY.json — NOT ignored. Ships with core modules pre-registered. diff --git a/src/aipass/seedgo/apps/standards/aipass/handlers/standards/encapsulation_check.py b/src/aipass/seedgo/apps/standards/aipass/handlers/standards/encapsulation_check.py index 1f5bbd7b..3c304e91 100644 --- a/src/aipass/seedgo/apps/standards/aipass/handlers/standards/encapsulation_check.py +++ b/src/aipass/seedgo/apps/standards/aipass/handlers/standards/encapsulation_check.py @@ -21,7 +21,14 @@ import json from pathlib import Path from typing import Dict, List, Optional -# Branch registry for detecting branch context +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): + candidate = parent / "AIPASS_REGISTRY.json" + if candidate.exists(): + return candidate + return Path.cwd() / "AIPASS_REGISTRY.json" def is_bypassed(file_path: str, standard: str, line: int | None = None, bypass_rules: list | None = None) -> bool: @@ -47,18 +54,20 @@ def is_bypassed(file_path: str, standard: str, line: int | None = None, bypass_r def get_branch_from_path(file_path: str) -> Optional[Dict]: - """Detect which branch a file belongs to""" + """Detect which branch a file belongs to using AIPASS_REGISTRY.json.""" try: - if not BRANCH_REGISTRY_PATH.exists(): + registry_path = _find_registry() + if not registry_path.exists(): return None - with open(BRANCH_REGISTRY_PATH, 'r', encoding='utf-8') as f: + with open(registry_path, 'r', encoding='utf-8') as f: registry = json.load(f) if not registry: return None - file_path = str(Path(file_path).resolve()) + registry_dir = registry_path.parent + resolved_path = str(Path(file_path).resolve()) # Sort branches by path length (longest first) to match most specific branches = sorted(registry.get('branches', []), @@ -66,8 +75,12 @@ def get_branch_from_path(file_path: str) -> Optional[Dict]: reverse=True) for branch in branches: - branch_path = branch.get('path', '') - if file_path.startswith(branch_path + '/') or file_path == branch_path: + raw_path = branch.get('path', '') + branch_path = Path(raw_path) + if not branch_path.is_absolute(): + branch_path = (registry_dir / branch_path).resolve() + branch_path_str = str(branch_path) + if resolved_path.startswith(branch_path_str + '/') or resolved_path == branch_path_str: return branch return None @@ -89,8 +102,8 @@ def extract_branch_from_import(import_line: str) -> Optional[str]: if match: return match.group(1) - # Pattern 2: aipass_core.branch.apps.handlers... - match = re.search(r'from\s+aipass_core\.(\w+)\.apps\.handlers', import_line) + # Pattern 2: aipass.branch.apps.handlers... + match = re.search(r'from\s+aipass\.(\w+)\.apps\.handlers', import_line) if match: return match.group(1) diff --git a/src/aipass/seedgo/apps/standards/aipass/handlers/standards/error_handling_check.py b/src/aipass/seedgo/apps/standards/aipass/handlers/standards/error_handling_check.py index 7babd235..12b38caf 100644 --- a/src/aipass/seedgo/apps/standards/aipass/handlers/standards/error_handling_check.py +++ b/src/aipass/seedgo/apps/standards/aipass/handlers/standards/error_handling_check.py @@ -14,7 +14,6 @@ Checks Prax imports in modules/handlers, logger calls in handlers. # ============================================= -import sys import re from pathlib import Path from typing import Dict, List @@ -161,7 +160,10 @@ def check_module_has_prax(content: str, file_path: str, bypass_rules: list | Non Modules MUST import Prax for business logging """ - has_prax_import = 'from prax.apps.modules.logger import system_logger' in content + has_prax_import = ( + 'from aipass.prax import logger' in content + or 'from aipass.prax import' in content and 'logger' in content + ) if has_prax_import: return { @@ -180,7 +182,7 @@ def check_module_has_prax(content: str, file_path: str, bypass_rules: list | Non return { 'name': 'Module Prax import', 'passed': False, - 'message': 'Module MUST import Prax: from prax.apps.modules.logger import system_logger as logger' + 'message': 'Module MUST import Prax: from aipass.prax import logger' } @@ -234,7 +236,9 @@ def check_module_error_logging(content: str) -> Dict: Modules just need Prax import (checked separately). This check passes if Prax is imported - modules CAN log but aren't required to. """ - has_prax_import = 'from prax.apps.modules.logger import system_logger' in content + has_prax_import = 'from aipass.prax import logger' in content or ( + 'from aipass.prax import' in content and 'logger' in content + ) if has_prax_import: return { diff --git a/src/aipass/seedgo/apps/standards/aipass/handlers/standards/json_structure_check.py b/src/aipass/seedgo/apps/standards/aipass/handlers/standards/json_structure_check.py index 7a6d1950..35de8813 100644 --- a/src/aipass/seedgo/apps/standards/aipass/handlers/standards/json_structure_check.py +++ b/src/aipass/seedgo/apps/standards/aipass/handlers/standards/json_structure_check.py @@ -99,60 +99,52 @@ def check_module(module_path: str, bypass_rules: list | None = None) -> Dict: } +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): + candidate = parent / "AIPASS_REGISTRY.json" + if candidate.exists(): + return candidate + return Path.cwd() / "AIPASS_REGISTRY.json" + + def detect_branch(file_path: Path) -> Optional[str]: """ Detect which branch a file belongs to from its path. - Checks both AIPASS_REGISTRY.json (pip/library) and - BRANCH_REGISTRY.json (Dev-Pass) as sources of truth. - Falls back to path heuristics if neither available. + Checks AIPASS_REGISTRY.json as source of truth. + Falls back to path heuristics if not available. """ - file_path_str = str(file_path) + file_path_str = str(file_path.resolve()) - # Try AIPASS_REGISTRY.json first (library profile) - aipass_registry = Path(__file__).parents[8] / "AIPASS_REGISTRY.json" - if aipass_registry.exists(): + registry_path = _find_registry() + if registry_path.exists(): try: - with open(aipass_registry, 'r', encoding='utf-8') as f: + with open(registry_path, 'r', encoding='utf-8') as f: registry = json.load(f) + registry_dir = registry_path.parent branches = sorted( registry.get('branches', []), key=lambda b: len(b.get('path', '')), reverse=True ) for branch in branches: - branch_path = branch.get('path', '') - if branch_path and file_path_str.startswith(branch_path): - return branch.get('name', '').lower() - except (json.JSONDecodeError, IOError): - pass - - # Try BRANCH_REGISTRY.json (Dev-Pass) - branch_registry = Path.home() / "BRANCH_REGISTRY.json" - if branch_registry.exists(): - try: - with open(branch_registry, 'r', encoding='utf-8') as f: - registry = json.load(f) - branches = sorted( - registry.get('branches', []), - key=lambda b: len(b.get('path', '')), - reverse=True - ) - for branch in branches: - branch_path = branch.get('path', '') - if branch_path and file_path_str.startswith(branch_path): + raw_path = branch.get('path', '') + branch_path = Path(raw_path) + if not branch_path.is_absolute(): + branch_path = (registry_dir / branch_path).resolve() + if file_path_str.startswith(str(branch_path)): return branch.get('name', '').lower() except (json.JSONDecodeError, IOError): pass # Fallback: path heuristics path_parts = file_path.parts - if 'seed' in path_parts: - return 'seed' if 'seedgo' in path_parts: return 'seedgo' - if 'aipass_core' in path_parts: - idx = path_parts.index('aipass_core') + if 'aipass' in path_parts: + idx = path_parts.index('aipass') if idx + 1 < len(path_parts): return path_parts[idx + 1] @@ -160,28 +152,20 @@ def detect_branch(file_path: Path) -> Optional[str]: def get_branch_path(branch_name: str) -> Optional[str]: - """Get actual branch path from registries.""" - # Try AIPASS_REGISTRY.json first - aipass_registry = Path(__file__).parents[8] / "AIPASS_REGISTRY.json" - if aipass_registry.exists(): + """Get actual branch path from AIPASS_REGISTRY.json.""" + registry_path = _find_registry() + if registry_path.exists(): try: - with open(aipass_registry, 'r', encoding='utf-8') as f: + with open(registry_path, 'r', encoding='utf-8') as f: registry = json.load(f) + registry_dir = registry_path.parent for branch in registry.get('branches', []): if branch.get('name', '').lower() == branch_name.lower(): - return branch.get('path', '') - except (json.JSONDecodeError, IOError): - pass - - # Fall back to BRANCH_REGISTRY.json - branch_registry = Path.home() / "BRANCH_REGISTRY.json" - if branch_registry.exists(): - try: - with open(branch_registry, 'r', encoding='utf-8') as f: - registry = json.load(f) - for branch in registry.get('branches', []): - if branch.get('name', '').lower() == branch_name.lower(): - return branch.get('path', '') + raw_path = branch.get('path', '') + branch_path = Path(raw_path) + if not branch_path.is_absolute(): + branch_path = (registry_dir / branch_path).resolve() + return str(branch_path) except (json.JSONDecodeError, IOError): pass