From cef8ad029e869c78b45ff03460ef0506a60b8bfc Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Tue, 17 Mar 2026 00:12:53 -0700 Subject: [PATCH] fix(ai_mail,trigger): replace stale dev_central references with devpulse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clean up 33 stale "dev_central" references across ai_mail (16 hits) and trigger (17 hits) branches. All @dev_central → @devpulse in code, comments, prompt templates, and config values. ALL CAPS prompt directives rewritten to normal case. Category B rewrite in ai_mail.py (dashboard comment corrected to reference prax). Also adds demo/ hello_aipass.py — animated terminal dashboard for demos. Co-Authored-By: @devpulse --- src/aipass/ai_mail/apps/ai_mail.py | 2 +- .../ai_mail/apps/handlers/dispatch/daemon.py | 2 +- .../ai_mail/apps/handlers/dispatch/wake.py | 6 +- .../ai_mail/apps/handlers/email/delivery.py | 2 +- .../apps/handlers/email/error_dispatch.py | 2 +- .../ai_mail/apps/handlers/email/reply.py | 2 +- .../ai_mail/apps/handlers/email/send_args.py | 2 +- .../apps/handlers/trigger/error_handler.py | 22 +- src/aipass/ai_mail/apps/modules/dispatch.py | 2 +- src/aipass/devpulse/demo/hello_aipass.py | 236 ++++++++++++++++++ .../apps/handlers/events/error_detected.py | 16 +- .../apps/handlers/events/error_logged.py | 18 +- 12 files changed, 274 insertions(+), 38 deletions(-) create mode 100644 src/aipass/devpulse/demo/hello_aipass.py diff --git a/src/aipass/ai_mail/apps/ai_mail.py b/src/aipass/ai_mail/apps/ai_mail.py index 8a9288bf..24e27148 100644 --- a/src/aipass/ai_mail/apps/ai_mail.py +++ b/src/aipass/ai_mail/apps/ai_mail.py @@ -26,7 +26,7 @@ from typing import Dict, Any, Optional, List if hasattr(signal, 'SIGPIPE'): signal.signal(signal.SIGPIPE, signal.SIG_DFL) -# Dashboard integration (optional, requires dev_central package) +# Dashboard integration (optional, provided by prax) _UPDATE_SECTION = None # type: ignore # AIPass infrastructure imports diff --git a/src/aipass/ai_mail/apps/handlers/dispatch/daemon.py b/src/aipass/ai_mail/apps/handlers/dispatch/daemon.py index f9700841..cc72b467 100644 --- a/src/aipass/ai_mail/apps/handlers/dispatch/daemon.py +++ b/src/aipass/ai_mail/apps/handlers/dispatch/daemon.py @@ -482,7 +482,7 @@ def spawn_agent( def is_protected_branch(branch_email: str) -> bool: """Check if a branch is protected from auto-dispatch.""" - return branch_email == "@dev_central" + return branch_email == "@devpulse" def _read_session_type(pid_str: str) -> str: diff --git a/src/aipass/ai_mail/apps/handlers/dispatch/wake.py b/src/aipass/ai_mail/apps/handlers/dispatch/wake.py index 27bc23f8..793f188b 100644 --- a/src/aipass/ai_mail/apps/handlers/dispatch/wake.py +++ b/src/aipass/ai_mail/apps/handlers/dispatch/wake.py @@ -309,7 +309,7 @@ def resolve_branch(branch_email: str) -> Optional[Tuple[Path, str]]: def wake_branch(branch_email: str, custom_message: Optional[str] = None, fresh: bool = False, auto: bool = False, - sender: str = "@dev_central") -> Tuple[DispatchStatus, bool]: + sender: str = "@devpulse") -> Tuple[DispatchStatus, bool]: """ Spawn a Claude agent at the target branch with step-by-step status. @@ -488,7 +488,7 @@ if __name__ == "__main__": print("Flags:") print(" --fresh Start fresh session (claude -p) instead of resuming (claude -c -p)") print(" --auto Respect autonomous_pause (used by daemon). Manual wake ignores it.") - print(" --sender @branch Set return-to-sender for bounce emails (default: @dev_central)") + print(" --sender @branch Set return-to-sender for bounce emails (default: @devpulse)") print() print("Output: Step-by-step status of the dispatch pipeline:") print(" ✅ resolve → @branch found at /path/to/branch") @@ -508,7 +508,7 @@ if __name__ == "__main__": # Parse flags use_fresh = "--fresh" in args use_auto = "--auto" in args - use_sender = "@dev_central" + use_sender = "@devpulse" if "--sender" in args: idx = args.index("--sender") diff --git a/src/aipass/ai_mail/apps/handlers/email/delivery.py b/src/aipass/ai_mail/apps/handlers/email/delivery.py index 7ce909c5..a6ff4079 100644 --- a/src/aipass/ai_mail/apps/handlers/email/delivery.py +++ b/src/aipass/ai_mail/apps/handlers/email/delivery.py @@ -455,7 +455,7 @@ def _send_desktop_notification(sender: str, recipient: str, subject: str, messag Gracefully handles cases where notify-send is not available. Args: - sender: Email sender address (e.g., @dev_central) + sender: Email sender address (e.g., @devpulse) recipient: Email recipient address (e.g., @ai_mail) subject: Email subject line message: Email body (first ~100 chars shown in notification) diff --git a/src/aipass/ai_mail/apps/handlers/email/error_dispatch.py b/src/aipass/ai_mail/apps/handlers/email/error_dispatch.py index 04b5e51c..410f5f1a 100644 --- a/src/aipass/ai_mail/apps/handlers/email/error_dispatch.py +++ b/src/aipass/ai_mail/apps/handlers/email/error_dispatch.py @@ -53,7 +53,7 @@ def build_error_report(to_branch: str, subject: str, error_msg: str) -> Dict[str "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "auto_execute": False, "priority": "normal", - "reply_to": "@dev_central", + "reply_to": "@devpulse", } diff --git a/src/aipass/ai_mail/apps/handlers/email/reply.py b/src/aipass/ai_mail/apps/handlers/email/reply.py index ff50b6f2..7c236b73 100644 --- a/src/aipass/ai_mail/apps/handlers/email/reply.py +++ b/src/aipass/ai_mail/apps/handlers/email/reply.py @@ -100,7 +100,7 @@ def send_reply( raise RuntimeError(error_msg) # Get reply destination - use reply_to if set, otherwise use original sender - # This allows emails to specify where replies should go (e.g., broadcasts from dev_central) + # This allows emails to specify where replies should go (e.g., broadcasts from devpulse) reply_destination = original_email.get("reply_to") or original_email.get("from", "") if not reply_destination: return False, "Original email has no sender or reply_to address", None diff --git a/src/aipass/ai_mail/apps/handlers/email/send_args.py b/src/aipass/ai_mail/apps/handlers/email/send_args.py index e76dde9a..598b9de3 100644 --- a/src/aipass/ai_mail/apps/handlers/email/send_args.py +++ b/src/aipass/ai_mail/apps/handlers/email/send_args.py @@ -82,7 +82,7 @@ def parse_send_args(args: List[str]) -> Dict[str, Any]: "subject": None, "message": None, "mode": "error", - "error": "--reply-to requires a branch address (e.g., --reply-to @dev_central)", + "error": "--reply-to requires a branch address (e.g., --reply-to @devpulse)", } # Separate recipients from subject/message diff --git a/src/aipass/ai_mail/apps/handlers/trigger/error_handler.py b/src/aipass/ai_mail/apps/handlers/trigger/error_handler.py index e902b4e3..757d22e4 100644 --- a/src/aipass/ai_mail/apps/handlers/trigger/error_handler.py +++ b/src/aipass/ai_mail/apps/handlers/trigger/error_handler.py @@ -66,20 +66,20 @@ Error message: {message} --- -INVESTIGATION STEPS: +Investigation steps: 1. Check the log file for context around this error 2. Identify root cause -DECISION TREE: -- SIMPLE FIX (typo, missing import, config issue): - -> Fix it yourself, then report what you did to @dev_central -- COMPLEX/UNCLEAR (needs research, affects multiple files): - -> Report findings only to @dev_central, recommend action, don't fix -- CRITICAL (data loss risk, security, system stability): - -> STOP immediately, escalate to @dev_central with full context +Decision tree: +- Simple fix (typo, missing import, config issue): + -> Fix it yourself, then report what you did to @devpulse +- Complex/unclear (needs research, affects multiple files): + -> Report findings only to @devpulse, recommend action, don't fix +- Critical (data loss risk, security, system stability): + -> Stop immediately, escalate to @devpulse with full context -REPORT TO @dev_central: - ai_mail send @dev_central "ERROR {error_hash} - [STATUS]" "Findings..." +Report to @devpulse: + ai_mail send @devpulse "ERROR {error_hash} - [STATUS]" "Findings..." Include: Error ID, severity (low/medium/high/critical), what you found, action taken or recommended. """ @@ -162,7 +162,7 @@ def handle_error_detected( 'timestamp': timestamp, 'auto_execute': True, 'priority': 'normal', - 'reply_to': '@dev_central' + 'reply_to': '@devpulse' } # Deliver via inbox.json diff --git a/src/aipass/ai_mail/apps/modules/dispatch.py b/src/aipass/ai_mail/apps/modules/dispatch.py index c13186a1..554f7c0d 100644 --- a/src/aipass/ai_mail/apps/modules/dispatch.py +++ b/src/aipass/ai_mail/apps/modules/dispatch.py @@ -160,7 +160,7 @@ def _orchestrate_wake(args: List[str]) -> bool: # Parse --fresh and --sender flags use_fresh = "--fresh" in args - use_sender = "@dev_central" + use_sender = "@devpulse" filtered = [] i = 0 while i < len(args): diff --git a/src/aipass/devpulse/demo/hello_aipass.py b/src/aipass/devpulse/demo/hello_aipass.py new file mode 100644 index 00000000..753a0688 --- /dev/null +++ b/src/aipass/devpulse/demo/hello_aipass.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 +""" +AIPass Hello World — A fancy terminal demo. + +Shows off the AIPass multi-agent ecosystem with a live animated dashboard. +Built by @devpulse as a demo for friends. +""" + +import time +import random +import sys +from datetime import datetime + +# ── ANSI Colors ────────────────────────────────────────────────────── +RESET = "\033[0m" +BOLD = "\033[1m" +DIM = "\033[2m" +CYAN = "\033[36m" +GREEN = "\033[32m" +YELLOW = "\033[33m" +MAGENTA = "\033[35m" +BLUE = "\033[34m" +WHITE = "\033[97m" +RED = "\033[31m" +BG_DARK = "\033[48;5;235m" +CLEAR = "\033[2J\033[H" + +# ── Branch Data ────────────────────────────────────────────────────── +BRANCHES = [ + ("drone", "Command Router", "Routes commands to branches", CYAN), + ("seedgo", "Standards Engine", "21-standard compliance pack", GREEN), + ("prax", "Logging System", "Stack-aware dual routing", YELLOW), + ("cli", "CLI Framework", "Argument parsing & registry", BLUE), + ("flow", "Plan Manager", "FPLANs + DPLANs", MAGENTA), + ("ai_mail", "Communications", "Inter-branch email & dispatch", CYAN), + ("spawn", "Branch Lifecycle", "Create, update, delete", GREEN), + ("trigger", "Event System", "12 events, error registry", YELLOW), + ("api", "API Layer", "External interfaces", BLUE), + ("devpulse", "Orchestration Hub", "You are here", MAGENTA), + ("memory", "Memory Bank", "ChromaDB vector search", CYAN), + ("daemon", "Background Sched", "Cron, plugins, monitoring", GREEN), + ("backup", "Backup Utils", "Snapshot & restore", YELLOW), + ("commons", "Shared Library", "Cross-branch utilities", BLUE), + ("skills", "Skill Catalog", "Reusable AI capabilities", MAGENTA), +] + +ACTIVITIES = [ + "Processing dispatch...", + "Running seedgo audit...", + "Indexing memories...", + "Routing command...", + "Checking standards...", + "Syncing mailbox...", + "Updating passport...", + "Logging event...", + "Refreshing dashboard...", + "Scanning for changes...", + "Compiling plan status...", + "Waking branch agent...", +] + + +def print_slow(text, delay=0.02): + """Print text character by character for dramatic effect.""" + for char in text: + sys.stdout.write(char) + sys.stdout.flush() + time.sleep(delay) + print() + + +def draw_box(title, content_lines, width=60, color=CYAN): + """Draw a bordered box with title.""" + print(f" {color}┌{'─' * (width - 2)}┐{RESET}") + print(f" {color}│{RESET} {BOLD}{WHITE}{title.center(width - 4)}{RESET} {color}│{RESET}") + print(f" {color}├{'─' * (width - 2)}┤{RESET}") + for line in content_lines: + padded = f"{line:<{width - 4}}"[:width - 4] + print(f" {color}│{RESET} {padded} {color}│{RESET}") + print(f" {color}└{'─' * (width - 2)}┘{RESET}") + + +def animate_startup(): + """Animated boot sequence.""" + print(CLEAR) + print() + + # ASCII art title + title = f""" + {CYAN}{BOLD} █████╗ ██╗██████╗ █████╗ ███████╗███████╗ + ██╔══██╗██║██╔══██╗██╔══██╗██╔════╝██╔════╝ + ███████║██║██████╔╝███████║███████╗███████╗ + ██╔══██║██║██╔═══╝ ██╔══██║╚════██║╚════██║ + ██║ ██║██║██║ ██║ ██║███████║███████║ + ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝{RESET} + """ + print(title) + time.sleep(0.5) + + print_slow(f" {DIM}Multi-Agent Framework — Where AI Citizens Live{RESET}", 0.03) + print() + time.sleep(0.3) + + # Boot sequence + steps = [ + ("Initializing drone router", 0.4), + ("Loading 15 branch identities", 0.3), + ("Connecting AI Mail network", 0.3), + ("Mounting .trinity/ memories", 0.4), + ("Starting Prax logging", 0.2), + ("Seedgo standards: 21 loaded", 0.3), + ("System ready", 0.1), + ] + + for step, duration in steps: + sys.stdout.write(f" {DIM}[{YELLOW}...{DIM}]{RESET} {step}") + sys.stdout.flush() + time.sleep(duration) + sys.stdout.write(f"\r {DIM}[{GREEN} ✓ {DIM}]{RESET} {step}\n") + sys.stdout.flush() + + time.sleep(0.5) + print() + + +def show_branch_grid(): + """Display all branches in a nice grid.""" + print(f" {BOLD}{WHITE}┌─ REGISTERED BRANCHES {'─' * 36}┐{RESET}") + print() + + for name, role, _, color in BRANCHES: + status = "●" if random.random() > 0.1 else "○" + status_color = GREEN if status == "●" else RED + + bar_len = random.randint(2, 12) + bar = f"{'█' * bar_len}{'░' * (12 - bar_len)}" + + is_devpulse = " ◀ YOU" if name == "devpulse" else "" + + print(f" {status_color}{status}{RESET} {color}{BOLD}@{name:<10}{RESET} {DIM}{role:<18}{RESET} {CYAN}{bar}{RESET}{YELLOW}{is_devpulse}{RESET}") + time.sleep(0.08) + + print() + print(f" {BOLD}{WHITE}└{'─' * 58}┘{RESET}") + print() + + +def show_live_activity(): + """Simulate live system activity for a few seconds.""" + print(f" {BOLD}{WHITE}── LIVE ACTIVITY ──{RESET}") + print() + + for _ in range(8): + branch = random.choice(BRANCHES) + activity = random.choice(ACTIVITIES) + timestamp = datetime.now().strftime("%H:%M:%S") + + print(f" {DIM}{timestamp}{RESET} {branch[3]}@{branch[0]:<10}{RESET} {activity}") + time.sleep(0.4) + + print() + + +def show_dispatch_demo(): + """Simulate a dispatch cycle.""" + print(f" {BOLD}{WHITE}── DISPATCH DEMO ──{RESET}") + print() + + # Simulate sending + print_slow(f" {CYAN}📤 drone @ai_mail send @spawn \"Build greeting module\" --dispatch{RESET}", 0.02) + time.sleep(0.5) + print(f" {GREEN} ✓ Email dispatched to @spawn{RESET}") + time.sleep(0.3) + + print_slow(f" {CYAN}🔔 drone @ai_mail dispatch wake @spawn{RESET}", 0.02) + time.sleep(0.5) + print(f" {GREEN} ✓ @spawn is awake and processing...{RESET}") + time.sleep(0.8) + + # Simulate work + work_steps = [ + "Reading inbox...", + "Found dispatch: 'Build greeting module'", + "Creating FPLAN-0099...", + "Deploying build agent...", + "Writing src/aipass/spawn/apps/handlers/greeting.py...", + "Running seedgo audit... 100% compliant", + "Emailing results back to @devpulse...", + ] + + for step in work_steps: + sys.stdout.write(f" {DIM} ↳ {step}{RESET}") + sys.stdout.flush() + time.sleep(0.5) + print() + + print() + print(f" {GREEN}{BOLD} 📨 Reply from @spawn: \"Greeting module built. 12 tests passing.\"{RESET}") + print() + + +def show_stats(): + """Final stats box.""" + stats = [ + f"{WHITE}Branches: {CYAN}15 registered, 15 operational{RESET}", + f"{WHITE}Standards: {GREEN}21 seedgo checks active{RESET}", + f"{WHITE}Sessions: {YELLOW}32 completed (devpulse alone){RESET}", + f"{WHITE}Architecture:{MAGENTA} Citizens + Agents + Dispatch{RESET}", + f"{WHITE}Built with: {CYAN}Python, Rich, Claude Code{RESET}", + f"{WHITE}Memory: {GREEN}.trinity/ — persistent identity{RESET}", + ] + draw_box("SYSTEM STATS", stats, 56, CYAN) + + +def main(): + """Run the full demo.""" + try: + animate_startup() + show_branch_grid() + time.sleep(0.5) + show_live_activity() + time.sleep(0.5) + show_dispatch_demo() + time.sleep(0.5) + show_stats() + + print() + print_slow(f" {BOLD}{CYAN}AIPass{RESET}{DIM} — Where AI citizens live, work, and remember.{RESET}", 0.03) + print() + + except KeyboardInterrupt: + print(f"\n\n {DIM}Demo interrupted. Goodbye!{RESET}\n") + + +if __name__ == "__main__": + main() diff --git a/src/aipass/trigger/apps/handlers/events/error_detected.py b/src/aipass/trigger/apps/handlers/events/error_detected.py index a3f4859a..91cd3167 100644 --- a/src/aipass/trigger/apps/handlers/events/error_detected.py +++ b/src/aipass/trigger/apps/handlers/events/error_detected.py @@ -292,19 +292,19 @@ INVESTIGATION STEPS: DECISION TREE: - SIMPLE FIX (typo, missing import, config issue): - -> Fix it yourself, then report what you did to @dev_central + -> Fix it yourself, then report what you did to @devpulse - COMPLEX/UNCLEAR (needs research, affects multiple files): - -> Report findings only to @dev_central, recommend action, don't fix + -> Report findings only to @devpulse, recommend action, don't fix - CRITICAL (data loss risk, security, system stability): - -> STOP immediately, escalate to @dev_central with full context + -> STOP immediately, escalate to @devpulse with full context SEED STANDARDS REMINDER: - Any code changes made during this investigation MUST follow Seed standards - After fixing, run: drone @seed checklist - Fixes scoring below 80% on Seed audit should NOT be shipped - clean up first -REPORT TO @dev_central: - ai_mail send @dev_central "ERROR {error_hash} - [STATUS]" "Findings..." +REPORT TO @devpulse: + ai_mail send @devpulse "ERROR {error_hash} - [STATUS]" "Findings..." Include: Error ID, severity (low/medium/high/critical), what you found, action taken or recommended. """ @@ -422,14 +422,14 @@ def handle_error_detected( # Convert branch name to email format (FLOW -> @flow) recipient = f"@{branch.lower()}" - # HARD RULE: DEV_CENTRAL is NEVER auto-triggered - if recipient == '@dev_central': + # devpulse is protected from auto-triggering + if recipient == '@devpulse': return # Validate target branch exists in registry before attempting delivery registered_emails = _get_registered_emails() if recipient not in registered_emails: - # Unknown branch - log and skip (do NOT route to dev_central) + # Unknown branch - log and skip (do NOT route to devpulse) try: suppressed_log = TRIGGER_ROOT / "logs" / "medic_suppressed.log" suppressed_log.parent.mkdir(parents=True, exist_ok=True) diff --git a/src/aipass/trigger/apps/handlers/events/error_logged.py b/src/aipass/trigger/apps/handlers/events/error_logged.py index b57a4fcc..15a8a2b8 100644 --- a/src/aipass/trigger/apps/handlers/events/error_logged.py +++ b/src/aipass/trigger/apps/handlers/events/error_logged.py @@ -15,7 +15,7 @@ backoff, and registry-based deduplication. This handler remains for backward compatibility with code that fires error_logged events directly. It now includes full medic gating (medic_enabled, branch_muted, -rate limiting, DEV_CENTRAL protection) to prevent bypass. +rate limiting, devpulse protection) to prevent bypass. Event data expected: - branch: Branch where error occurred (e.g., FLOW) @@ -198,14 +198,14 @@ INVESTIGATION STEPS: DECISION TREE: - SIMPLE FIX (typo, missing import, config issue): - -> Fix it yourself, then report what you did to @dev_central + -> Fix it yourself, then report what you did to @devpulse - COMPLEX/UNCLEAR (needs research, affects multiple files): - -> Report findings only to @dev_central, recommend action, don't fix + -> Report findings only to @devpulse, recommend action, don't fix - CRITICAL (data loss risk, security, system stability): - -> STOP immediately, escalate to @dev_central with full context + -> STOP immediately, escalate to @devpulse with full context -REPORT TO @dev_central: - ai_mail send @dev_central "ERROR {error_hash[:8]} - [STATUS]" "Findings..." +REPORT TO @devpulse: + ai_mail send @devpulse "ERROR {error_hash[:8]} - [STATUS]" "Findings..." """ @@ -229,7 +229,7 @@ def handle_error_logged( Gating (matches error_detected.py): 1. medic_enabled check (global toggle) 2. branch_muted check (per-branch suppression) - 3. DEV_CENTRAL protection (HARD RULE: never auto-trigger) + 3. devpulse protection (never auto-trigger) 4. Branch validation (unknown branches logged + skipped) 5. Rate limiting (3 per 10 minutes per branch) @@ -266,8 +266,8 @@ def handle_error_logged( # Gate 3: Convert branch name to email format recipient = f"@{branch.lower()}" - # HARD RULE: DEV_CENTRAL is NEVER auto-triggered - if recipient == '@dev_central': + # devpulse is protected from auto-triggering + if recipient == '@devpulse': return # Gate 4: Validate target branch exists in registry