This commit is contained in:
AIOSAI
2026-05-16 16:40:07 -07:00
parent 42e0353043
commit bd01b9b575
24 changed files with 2419 additions and 0 deletions
@@ -0,0 +1,60 @@
# API Module Recon
**Date:** 2026-03-06
## Summary
LLM access and Telegram multi-bot system. **Heaviest path debt** (31 Path.home()). 46 Python files.
## Structure
```
api/
├── apps/
│ ├── api.py # Entry point (auto-discovers modules)
│ ├── modules/
│ │ ├── api_key.py # Key retrieval, validation
│ │ ├── openrouter_client.py # LLM API calls, model listing
│ │ ├── telegram_bot.py # Multi-bot management (PUBLIC)
│ │ ├── telegram_service.py # Systemd service control
│ │ └── usage_tracker.py # Cost tracking
│ ├── handlers/
│ │ ├── auth/ # Key management, .env fallback
│ │ ├── config/ # Configuration
│ │ ├── openrouter/ # OpenRouter client + retry
│ │ ├── telegram/ # 12 files (BaseBot, factory, registry, plugins)
│ │ ├── telegram_service/ # Service control
│ │ ├── usage/ # Usage tracking
│ │ └── json/ # JSON tracking
│ └── json_templates/
└── tests/ # Empty
```
## Commands
```
drone @api get-key|validate|test|models
drone @api track|stats
drone @api telegram start|stop|status|logs
drone @api telegram_bot list|create|delete|status|start|stop
```
## Path.home() Debt: 31 instances (CRITICAL)
**Telegram handlers (23/31):**
- base_bot.py — 7 hits
- bot_factory.py — 5 hits
- config.py — 2 hits
- branch_plugin.py, response_router.py, notifier.py, tmux_manager.py, botfather_client.py
**Other:**
- json_handler.py:29 — `API_ROOT = Path.home() / "aipass_core" / "api"` (import-time) [stale: aipass_core]
- log_streamer.py:54 — `SYSTEM_LOGS_DIR = Path("/home/aipass/system_logs")` (CRITICAL, import-time)
- auth/env.py:54, telegram_service/service.py:26
## Key Insight
23 of 31 Path.home() issues are in **Telegram handlers** — this is legacy AIPass infrastructure. DPLAN-047 recommends stripping it for v1.0 (Option B).
## Disabled Legacy Files
- `spawner.py(disabled)` — old Claude session spawner
- `output_parser.py(disabled)` — old JSON stream parser
## Notes
- Entry point is `api.py` not `branch.py` (naming deviation)
- No .trinity files, no tests
- OpenRouter integration is stdlib-only BaseBot (no python-telegram-bot dep)
@@ -0,0 +1,45 @@
# CLI Module Recon
**Date:** 2026-03-06
## Summary
Display/formatting service provider using Rich. Clean public API. LOW path debt.
## Structure
```
cli/
├── apps/
│ ├── cli.py # Entry point - showroom & help (v0.2.0)
│ ├── modules/
│ │ ├── display.py # header(), success(), error(), warning(), section() (v0.4.0)
│ │ └── templates.py # operation_start(), operation_complete() (v0.3.0)
│ ├── handlers/
│ │ ├── json/json_handler.py # JSON auto-create (PATH.HOME BUG)
│ │ └── templates/ # Empty
│ ├── extensions/ # Stub
│ └── plugins/ # Stub
├── __init__.py # Exports: console, header, success, error, warning, section, operation_start, operation_complete
├── .seed/bypass.json
└── tests/
```
## Public API
```python
from aipass.cli import console, header, success, error, warning, section
from aipass.cli import operation_start, operation_complete
```
## Path.home() Debt
- `json_handler.py:27-29` — `CLI_ROOT = Path.home() / "aipass_core" / "cli"` (CRITICAL) [stale: aipass_core]
- 8 files with hardcoded shebang `#!/home/aipass/.venv/bin/python3`
## Working
- Display module with Rich integration
- Templates module with operation patterns
- Handler guard system (cross-branch import protection)
- SEED pattern implementation (introspection, help, demo)
## Broken
- json_handler.py Path.home() — wrong paths in container
- No .trinity files
- No .aipass branch prompt
- Extensions/plugins empty
@@ -0,0 +1,66 @@
# Path.home() / /home/aipass Full Audit
**Date:** 2026-03-06 | Cross-referenced with DPLAN-047
## Executive Summary
- **4 CRITICAL** module-level hardcoded `/home/aipass` (import-time crash)
- **11 HIGH** module-level `Path.home()` bindings (import-time)
- **14+ MEDIUM** runtime function-level `Path.home()` usage
- **203 LOW** shebang references (cosmetic)
- **28+ LOW** docstring/comment references
## CRITICAL — Import-Time Crashes (4 files)
| File | Line | Binding |
|------|------|---------|
| ai_mail/apps/handlers/registry/read.py | 41 | `BRANCH_REGISTRY_PATH = Path("/home/aipass/BRANCH_REGISTRY.json")` [stale: now AIPASS_REGISTRY.json] |
| flow/apps/modules/registry_monitor.py | 83 | `ECOSYSTEM_ROOT = Path("/home/aipass")` |
| trigger/apps/handlers/events/plan_file.py | 42 | `ECOSYSTEM_ROOT = Path("/home/aipass")` |
| api/apps/handlers/telegram/log_streamer.py | 54 | `SYSTEM_LOGS_DIR = Path("/home/aipass/system_logs")` |
## HIGH — Module-Level Path.home() (11 files)
| File | Line | Binding |
|------|------|---------|
| trigger/apps/handlers/watchers/log_watcher.py | 44 | `AIPASS_HOME = Path.home()` |
| trigger/apps/handlers/log_watcher.py | 54 | `AIPASS_HOME = Path.home()` |
| trigger/apps/handlers/events/error_detected.py | 61 | `AIPASS_HOME = Path.home()` |
| trigger/apps/handlers/events/error_logged.py | 53 | `AIPASS_HOME = Path.home()` |
| trigger/apps/handlers/events/startup.py | 41 | `AIPASS_HOME = Path.home()` |
| trigger/apps/handlers/events/bulletin_created.py | 40 | `AIPASS_HOME = Path.home()` |
| trigger/apps/handlers/events/memory_template_updated.py | 37 | `AIPASS_HOME = Path.home()` |
| trigger/apps/handlers/events/memory_threshold_exceeded.py | 42 | `AIPASS_HOME = Path.home()` |
| prax/apps/handlers/monitoring/telegram_command_bot.py | 68 | `AIPASS_HOME = Path.home()` |
| ai_mail/apps/handlers/dispatch/wake.py | 46 | `AIPASS_HOME = Path.home()` |
| ai_mail/apps/handlers/dispatch/daemon.py | 54 | `AIPASS_HOME = Path.home()` |
## MEDIUM — Runtime Function-Level (14+ files)
Key offenders:
- flow/apps/handlers/summary/write_plan_outputs.py — 4 usages
- flow/apps/modules/restore_plan.py — 4 usages
- prax/apps/handlers/config/load.py:51 — SYSTEM_LOGS_DIR + mkdir at import
- prax/apps/handlers/monitoring/branch_detector.py:191
- prax/apps/modules/monitor_module.py:597
- api/apps/handlers/telegram/bot_factory.py:369
- api/apps/handlers/telegram/response_router.py:228
- ai_mail/apps/handlers/central_writer.py:54-57 — 4 instances
- ai_mail/apps/handlers/email/delivery.py:71 — hardcoded Path("/home/aipass/...")
## Module Severity Summary
| Module | CRITICAL | HIGH | MEDIUM | Shebangs | DPLAN-047 Match |
|--------|----------|------|--------|----------|-----------------|
| api | 1 | 0 | 2 | ~20 | Yes (inflated by docs) |
| ai_mail | 1 | 2 | 6 | ~20 | Yes |
| trigger | 1 | 7 | 0 | ~15 | Yes |
| prax | 0 | 1 | 3 | ~55 | Yes |
| flow | 1 | 0 | 8 | ~7 | Yes |
| seedgo | 0 | 0 | 0 | 1 | DONE |
| drone | 0 | 0 | 0 | 2 | LOW |
| cli | 0 | 0 | 1 | 8 | LOW |
| spawn | 0 | 0 | 0 | 2 | LOW |
| devpulse | 0 | 0 | 0 | 0 | DONE |
## Safe to Import in Container
- drone, seedgo, cli, devpulse, spawn — no critical violations
- prax (top-level `from aipass.prax import logger`) — works due to lazy init
@@ -0,0 +1,56 @@
# Prax Module Recon
**Date:** 2026-03-06
## Summary
Logging and monitoring system. **Import works** (lazy init). 15 Path.home() hits, 4 at import-time. 71 Python files.
## Public API
```python
from aipass.prax import logger # SystemLogger instance
logger.info("message") # Auto-routes to calling module's log file
```
## Structure
```
prax/
├── __init__.py # Exports: system_logger as logger
├── apps/
│ ├── prax.py # Entry point (202 lines)
│ ├── modules/
│ │ ├── logger.py # SystemLogger class (268 lines) - THE public API
│ │ ├── init_module.py # Initialize logging
│ │ ├── shutdown_module.py
│ │ ├── monitor_module.py # Mission Control (595 lines)
│ │ ├── status_module.py # System status display
│ │ └── 5 more modules
│ ├── handlers/ # 52 files
│ │ ├── logging/ # 12 files - setup, direct, terminal, rotation
│ │ ├── monitoring/ # 14 files - events, telegram, branch detection
│ │ ├── discovery/ # 3 files - module scanning
│ │ ├── registry/ # 7 files - AIPASS registry
│ │ ├── config/ # 2 files - load.py (CRITICAL)
│ │ ├── json/ # 5 files
│ │ └── watcher/, dashboard/
└── tests/
```
## Path.home() Debt: 15 instances
**Import-time (CRITICAL):**
- config/load.py:51 — `SYSTEM_LOGS_DIR = Path.home() / "system_logs"` + mkdir at line 55
- log_watchdog.py:44 — SYSTEM_LOGS_DIR at import time
- agent_status_writer.py:45 — AIPASS_REGISTRY at import time [stale: was BRANCH_REGISTRY]
- registry/reader.py:34 — AIPASS_REGISTRY_PATH at import time [stale: was BRANCH_REGISTRY_PATH]
**Function-level:**
- monitoring/branch_detector.py:58, 191
- monitoring/telegram_command_bot.py:68 (module-level)
- monitor_module.py:161, 597
- logging/setup.py:125, direct.py:144
## Key Finding
`from aipass.prax import logger` **works** because logger.py uses lazy init. But deeper imports into handlers (config/load.py) crash due to Path.home() mkdir at import time.
## Note
- Circular dependency with CLI properly handled (logger doesn't import CLI)
- File watcher has try/except for inotify limit (graceful degradation)
- `logger.info('test')` produces no visible terminal output (may need config check)
@@ -0,0 +1,52 @@
# Project Night Research — S69
## What AIPass Already Has (Don't Rebuild)
- Inter-branch messaging + dispatch (ai_mail)
- Commons collaboration rooms + voting + boardrooms
- Medic auto-dispatch for errors (trigger, partially wired)
- Dashboard system (prax, partially wired)
- Agent handover (natural via dispatch lifecycle)
- Plan lifecycle (flow — FPLAN/DPLAN/APLAN/RPLAN/master)
- Semantic memory + rollover (memory — ChromaDB)
- Standards compliance (seedgo — 32 standards)
- Event bus (trigger — 14 events)
- Branch lifecycle (spawn — create/update/delete)
- CLI routing (drone — @branch resolution)
- Background scheduling (daemon — cron + plugins)
- Multi-mode backup (backup — snapshot/versioned/Drive)
- API gateway (api — OpenRouter/Google)
- Skill discovery framework (skills — 3-tier)
- 20 diagnostic scanners (devpulse tools/)
## What AIPass Doesn't Have (Opportunity Space)
- Nothing that produces value OUTSIDE the system
- No ability to analyze/process external codebases programmatically
- No ability to generate reports/artifacts for human consumption beyond CLI
- No cost/budget tracking across agent operations
- No structural failure detection (tool loops, context bloat, reasoning stalls)
- No automated regression testing (run tests, compare results over time)
- No ability to onboard external projects into the AIPass ecosystem
- No external webhook/notification system (only internal dbus)
- No cross-project knowledge sharing (AIPass ↔ Nexus ↔ external)
## Patrick's Interests (Starred Repos)
- Dunetrace: structural failure detection in multi-agent systems
- Phantom: autonomous agents with persistent VM, self-creating tools
- Paperclip: multi-agent company with budgets and org charts
- Syrin: budget control + semantic memory pools
- OpenClaw Nerve: real-time ops cockpit for agent fleets
- Virtual Context: semantic memory compression
- Citadel: persistent campaigns + fleet coordination
- Jork: autonomous agent with independent thinking cycles
- Galactic: infrastructure-level multi-instance management
## Key Insight
AIPass is entirely self-referential. Every branch serves the system.
The gap: something that uses the system to DO something for the outside world.
## Constraints
- CLI only (no UI/dashboards beyond what prax already has)
- Must require NEW work from all 13 core branches (exclude commons/skills)
- Must be genuinely new, not rebuilding existing capabilities
- Should run through prax logging, get seedgo standards, use all plan types
- Can be a new citizen (src/aipass/newbranch/) or standalone project
@@ -0,0 +1,238 @@
# Architecture Probe -- External Reviewer
**Date:** 2026-04-26
**Reviewer model:** Claude Opus 4.6 (1M context)
**Scope:** Full codebase review of 11 agent branches (826 active Python files)
**Method:** Static analysis of imports, file patterns, hooks, identity, communication, and test architecture
---
## Design Strengths
### 1. Genuine agent isolation with clear domain boundaries
Each branch owns its domain and the directory layout enforces it: `apps/handlers/` for private implementation, `apps/modules/` for public API, `apps/plugins/` for extensions. This is a real architectural pattern, not just a file tree. The handler/module split means internals can change without breaking callers, which is exactly right for a multi-agent system where branches evolve independently.
**Key files:** Every branch follows the `{branch}/apps/handlers/`, `{branch}/apps/modules/`, `{branch}/apps/plugins/` triplet.
### 2. The hook system is architecturally sound
The pre-edit gate (`/.claude/hooks/pre_edit_gate.py`) enforces cross-branch write protection at the tool layer, not at the application layer. This means a misbehaving branch cannot bypass the protection by importing the wrong module -- the gate operates below the code. The daemon confinement rule (Rule 1.5) is particularly smart: dispatched agents can only write inside their own branch directory, which breaks prompt-injection amplification chains.
**Key files:** `/.claude/hooks/pre_edit_gate.py`, `/.claude/hooks/auto_fix_diagnostics.py`
### 3. Prax as a shared infrastructure service
The `aipass.prax` package with its `NullLogger` fallback (`/src/aipass/prax/__init__.py`) means no branch crashes if the logging system is down. The pattern of `from aipass.prax import logger` providing a guaranteed-safe logger instance is a good service design. 434 imports from prax across non-test code show it is genuinely central, and the fallback proves it was hardened after real failures.
### 4. Registry credential verification
`drone/apps/handlers/registry_handler.py` verifies that the registry file's `metadata.id` matches the caller's `passport.json` `citizenship.registry_id`. This prevents a branch from accidentally reading a wrong registry -- a subtle but important safety net in a system where multiple projects can coexist via `AIPASS_HOME`.
**Key file:** `/src/aipass/drone/apps/handlers/registry_handler.py` lines 114-154
### 5. Self-healing delivery
The email delivery system (`ai_mail/apps/handlers/email/delivery.py`) auto-provisions inboxes for branches that do not have one, auto-migrates old inbox formats, and auto-registers contacts. This means the system degrades gracefully instead of failing when a new branch has not been fully set up yet. The `_migrate_inbox_format` function handles at least four different corruption/legacy states.
### 6. Trigger event bus with circuit breaker
The `Trigger` class in `/src/aipass/trigger/apps/modules/core.py` has a proper circuit breaker: after 5 consecutive failures, a handler is auto-disabled rather than crashing the event bus. The deferred queue prevents recursive event firing from deadlocking. The disabled inotify lazy-start (with the explicit comment explaining why) shows the team learns from production failures.
---
## Design Concerns
### 1. 58 independent copies of `_find_repo_root()`
There are 58 separate implementations of `_find_repo_root()` / `find_repo_root()` scattered across the codebase. Most use the same walk-up-parents-looking-for-AIPASS_REGISTRY.json pattern but with slight variations (some look for `.git`, some for `pyproject.toml`, some for `AIPASS_REGISTRY.json`, some limit depth, some do not). This is the single largest duplication problem in the codebase.
**The risk:** If the project root detection strategy changes (say, the registry file is renamed, or a monorepo layout is adopted), you must find and update 58 functions. The devpulse tools alone account for 20+ copies.
**Key files showing variations:**
- `/src/aipass/ai_mail/apps/handlers/paths.py` -- looks for AIPASS_REGISTRY.json
- `/src/aipass/prax/apps/handlers/config/load.py` -- looks for AIPASS_REGISTRY.json
- `/src/aipass/drone/apps/handlers/registry_handler.py` -- globs `*_REGISTRY.json` (different strategy)
- `/.claude/hooks/identity_injector.py` -- looks for pyproject.toml or .git
### 2. 12 copies of `json_handler.py` (2,720 total lines)
Every branch has its own `apps/handlers/json/json_handler.py`. These range from 28 lines (ai_mail, which re-exports from json_utils) to 450 lines (drone). They all provide `log_operation()`, `ensure_json_exists()`, `load_json()`, `save_json()` -- but each one discovers its branch root independently via `Path(__file__).resolve().parents[N]` and creates branch-scoped JSON directories.
**The risk:** This is copy-paste inheritance. When a bug is found in one (like the empty-file corruption guard added to drone's version), it must be manually propagated to 11 other files. The parent-traversal depth (`parents[3]` vs `parents[4]`) varies by branch and will break if directory structure changes.
**All copies:**
```
ai_mail/apps/handlers/json/json_handler.py (28 lines, re-export shim)
aipass/apps/handlers/json/json_handler.py (275 lines)
api/apps/handlers/json/json_handler.py (244 lines)
cli/apps/handlers/json/json_handler.py (222 lines)
drone/apps/handlers/json/json_handler.py (450 lines, most evolved)
flow/apps/handlers/json/json_handler.py (298 lines)
memory/apps/handlers/json/json_handler.py (103 lines)
prax/apps/handlers/json/json_handler.py (281 lines)
seedgo/apps/handlers/json/json_handler.py (267 lines)
spawn/apps/handlers/json/json_handler.py (266 lines)
trigger/apps/handlers/json/json_handler.py (286 lines)
```
### 3. 10 identical copies of `verify_branch.py`
Every branch has `tools/verify_branch.py`. Comparing drone's and trigger's copies -- they are character-for-character identical except for a single comment ("relative to drone directory" vs "relative to current directory"). This is pure template artifact duplication. The tool compares a branch against its template, but the `TEMPLATE_DIR` is always set to the module's own root (`_THIS_DIR.parent`), which means every copy is checking itself against itself.
**Key files:** `/src/aipass/drone/tools/verify_branch.py`, `/src/aipass/trigger/tools/verify_branch.py` (and 8 others)
### 4. Two parallel registry systems
The drone branch has its own registry handler (`drone/apps/handlers/registry_handler.py`) that normalizes branches from list to dict format and merges primary + AIPASS_HOME registries. The ai_mail branch has its own (`ai_mail/apps/handlers/registry/read.py`) that reads the same `AIPASS_REGISTRY.json` but with different normalization logic and different return types (list of dicts with email vs dict of dicts keyed by name).
Neither imports from the other. Both are mature, both handle edge cases, and they will inevitably drift.
**Key files:**
- `/src/aipass/drone/apps/handlers/registry_handler.py` (334 lines)
- `/src/aipass/ai_mail/apps/handlers/registry/read.py` (220 lines)
- `/src/aipass/spawn/apps/handlers/registry.py` (spawn's own copy)
### 5. conftest.py patterns are inconsistent
The test fixtures across branches are structurally similar but not shared:
- `drone/tests/conftest.py` -- defines `mock_json_handler` as a standalone MagicMock fixture
- `ai_mail/tests/conftest.py` -- defines `mock_json_handler` with monkeypatch argument (but does not use it)
- `flow/tests/conftest.py` -- uses `autouse=True` with `patch()` context managers, pre-imports modules for patch resolution
The `AIPASS_TEST_LOG_DIR` env-var redirect is copy-pasted at the top of every conftest. This is a cross-cutting concern that belongs in a shared conftest at the package root.
**Key files:**
- `/src/aipass/drone/tests/conftest.py`
- `/src/aipass/ai_mail/tests/conftest.py`
- `/src/aipass/flow/tests/conftest.py`
---
## Coupling Issues
### 1. Prax is a god dependency (434 non-test imports)
Every branch imports `aipass.prax.apps.modules.logger`. This is correct for a logging service, but it means prax cannot be modified, refactored, or have its module structure changed without potentially breaking all 10 other branches. The `system_logger` instance is imported at module level in almost every handler file, creating eager import chains.
**Specific risk:** If prax's internal structure changes (e.g., moving `logger.py` from `apps/modules/` to `apps/handlers/`), hundreds of import statements across the codebase break.
### 2. CLI is deeply coupled as a display layer (191 non-test imports)
`from aipass.cli.apps.modules import console` appears everywhere -- in handlers, modules, introspection functions, even in `__main__` blocks. The CLI branch is not just a command-line interface; it is the stdout abstraction for the entire system. This means:
- No branch can produce output without CLI being importable
- Rich (the CLI's display library) becomes a transitive dependency for all branches
- Running any branch's code in a context where Rich is unavailable will fail
### 3. Trigger is imported by 8+ branches via lazy imports
The pattern `from aipass.trigger.apps.modules.core import trigger` appears in ai_mail, aipass, api, cli, drone, flow, memory, and prax. Most uses are inside lazy `try/except` blocks, which is good, but the coupling surface is enormous. Trigger fires events that cross every branch boundary -- it is the nervous system of the ecosystem. A breaking change to `trigger.fire()` or its handler signature could cascade.
### 4. Cross-branch import chains at module load time
`delivery.py` (ai_mail) imports from `prax.apps.modules.logger`, `ai_mail.apps.handlers.json`, `ai_mail.apps.handlers.paths`, and `ai_mail.apps.handlers.registry.read` -- all at module level. `registry.read` imports from `prax.apps.modules.logger`. `paths.py` imports from `ai_mail.apps.handlers.json`. This creates eager initialization chains where importing any handler drags in the logger, the json system, and the path resolution, all before a single function is called.
---
## Scaling Concerns
### 1. File-based communication without coordination
ai_mail delivers messages by directly writing to JSON files on disk. The `inbox_lock` context manager provides per-file locking, but there is no global coordinator. If the system grows beyond a single machine (or even beyond a single filesystem), the entire communication layer breaks. The dispatch daemon polls files on a timer. There is no message queue, no pub/sub, no event-driven I/O.
**Not a current problem**, but the architecture assumes co-located filesystem access as a hard invariant.
### 2. Registry is a single JSON file read by every branch
`AIPASS_REGISTRY.json` is read by drone (via `registry_handler.py`), ai_mail (via `registry/read.py`), spawn (via `registry.py`), flow, seedgo, and hooks. Every registry read re-parses the entire file. With 12 branches, this is fine. With 50 branches and frequent operations, this becomes a hot path. There is no caching layer -- every `get_all_branches()` call opens and parses the file from scratch.
### 3. json_handler log rotation is per-process, not per-branch
Each `json_handler.py` appends to per-module log files with a FIFO rotation of 100 entries. But if multiple processes (daemon, interactive session, hook) all log to the same module's log file, they race. The `_atomic_write_json` uses temp-file-then-rename, which prevents corruption, but does not prevent lost writes (two processes read the same log, append different entries, and one overwrites the other).
### 4. The dispatch daemon is a single-threaded poller
`daemon.py` polls every N seconds, spawns agents via subprocess, and waits. It processes one branch at a time. If 20 branches all have pending dispatches, latency grows linearly. The subprocess spawn is blocking. There is no concurrent dispatch, no priority queue, and no backpressure mechanism.
### 5. Trigger event bus uses class-level state
`Trigger._handlers`, `Trigger._history`, `Trigger._firing` are all class-level attributes. This means the Trigger is a process-global singleton. In a multi-process architecture (which AIPass already is, given the daemon + interactive sessions + hooks), each process has its own independent Trigger instance. Events fired in the daemon are invisible to the interactive session. This is probably intentional but limits the utility of the event system as a coordination mechanism.
---
## Suggestions
### 1. Extract `find_repo_root()` to a shared utility
Create a single canonical implementation in a shared location (perhaps `aipass/__init__.py` or a new `aipass.shared.paths` module). Accept a `marker` parameter for the file to search for. Replace all 58 copies with imports. This is the highest-ROI refactor available.
```
aipass/
shared/
paths.py # find_repo_root(marker="AIPASS_REGISTRY.json")
json_handler.py # Base class for branch json handlers
```
### 2. Promote json_handler to a shared base class
The 12 json_handler copies share ~80% of their logic. Extract a base implementation that parameterizes:
- Branch root discovery (pass it in instead of computing from `__file__`)
- JSON directory name
- Default schemas
Each branch's json_handler becomes a thin subclass or configuration of the shared one. Drone's extra features (atomic write, corruption guard) become the baseline for all.
### 3. Unify registry access behind a single service
drone and ai_mail should not independently parse `AIPASS_REGISTRY.json`. Create a registry service module (perhaps in drone, which already has the most complete implementation) that:
- Provides both list and dict access patterns
- Handles caching with TTL
- Merges primary + AIPASS_HOME registries
- Is the sole reader of registry files
### 4. Add a shared conftest at the package root
`/src/aipass/conftest.py` already exists but appears minimal. Move the `AIPASS_TEST_LOG_DIR` redirect, `temp_test_dir`, `mock_logger`, and `mock_json_handler` fixtures there. Branch conftest files should only add branch-specific fixtures.
### 5. Define explicit service interfaces for prax and cli
The coupling to prax and cli is correct in principle but fragile in practice because it targets internal paths (`aipass.prax.apps.modules.logger`). Consider exporting stable interfaces from `aipass.prax` and `aipass.cli` top-level packages:
```python
# Instead of:
from aipass.prax.apps.modules.logger import system_logger as logger
# Use:
from aipass.prax import logger # (already works via __init__.py)
```
The prax `__init__.py` already does this. Propagate this pattern to all branches so they import from the stable surface, not the internal path.
### 6. Consider a thin message bus for cross-branch coordination
The Trigger event bus is process-local. For events that need to cross process boundaries (daemon -> interactive session, hook -> running agent), consider a filesystem-based event queue (a simple JSON append log) that the Trigger can poll or watch. This would unify the "trigger fires event" and "ai_mail delivers message" patterns into a single coordination mechanism.
### 7. Add type stubs or Protocol classes for the json_handler interface
Every branch imports `json_handler` and calls `log_operation()`, `load_json()`, `save_json()`, `ensure_json_exists()`. This is a de facto interface. Formalize it as a Protocol class so tests can verify compliance and so new branches get autocomplete and type checking for free.
---
## Summary Statistics
| Metric | Count |
|---|---|
| Active Python files | 826 |
| Test files | 241 |
| Branches | 12 (including aipass itself) |
| json_handler.py copies | 12 (2,720 total lines) |
| verify_branch.py copies | 10 (identical) |
| find_repo_root implementations | 58 |
| Prax imports (non-test) | 434 |
| CLI imports (non-test) | 191 |
| Trigger cross-branch imports | 25+ |
| Passport files | 12 |
| Hook files | 8 active |
---
*Generated by external architectural review. Findings are based on static analysis of the codebase as of 2026-04-26. No code was executed.*
@@ -0,0 +1,223 @@
# Security Probe -- External Reviewer
**Date:** 2026-04-26
**Reviewer:** External security researcher (first-pass review)
**Scope:** AIPass multi-agent framework at `/home/patrick/Projects/AIPass/src/aipass/`
---
## Critical Findings
### CRIT-1: All dispatched agents run with `--permission-mode bypassPermissions` -- unrestricted filesystem and shell access
**Files:**
- `/home/patrick/Projects/AIPass/src/aipass/ai_mail/apps/handlers/dispatch/daemon.py` lines 341-344
- `/home/patrick/Projects/AIPass/src/aipass/ai_mail/apps/handlers/dispatch/wake.py` lines 435-438, 450-453
**Description:** Every agent spawned by the daemon or by `drone wake` is launched with `--permission-mode bypassPermissions`. This flag tells Claude to skip all permission checks. The settings files at `.claude/settings.json` and per-branch `.claude/settings.local.json` define deny lists (blocking git operations, destructive commands, access to personal directories), but `bypassPermissions` overrides ALL of those controls.
A dispatched agent can:
- Read/write anywhere on the filesystem the user has access to (including `~/.secrets/`, `~/Patrick-Personal/`, `~/.ssh/`, etc.)
- Run any shell command without approval
- Modify other branches' inbox files, passports, and memory files
- Run `git push --force`, `rm -rf`, or anything else the deny list was supposed to prevent
The per-branch deny lists (e.g., `ai_mail/.claude/settings.local.json` line 5-23) are security theater when every dispatch uses `bypassPermissions`.
**Impact:** A single malicious email body that tricks an agent into running destructive commands will succeed without any permission gate. The entire permission model is bypassed at the most critical trust boundary (automated, unattended execution).
**Recommendation:** Use `--permission-mode allowedTools` or the default permission mode for dispatched agents. If specific operations are needed, add them to the allow list rather than bypassing all checks.
---
### CRIT-2: Email body content is delivered to agent inboxes verbatim -- prompt injection via inter-agent email
**Files:**
- `/home/patrick/Projects/AIPass/src/aipass/ai_mail/apps/handlers/email/delivery.py` lines 310-319 (message construction)
- `/home/patrick/Projects/AIPass/src/aipass/ai_mail/apps/handlers/dispatch/daemon.py` lines 262-286 (inbox scan)
- `/home/patrick/Projects/AIPass/src/aipass/ai_mail/apps/handlers/email/header.py` lines 21-31 (dispatch header)
**Description:** When Agent A sends Agent B a dispatch email, the subject and body are stored verbatim in Agent B's `inbox.json`. When Agent B is woken, the daemon gives it the prompt "Hi. Check inbox, process new emails, update memories when done." The agent then reads the inbox, finds the dispatch email, and follows whatever instructions are in the body.
There is NO sanitization, no content policy enforcement, no allowlisting of what instructions can appear in a dispatch email body. Any agent (or anything that can write to an inbox.json file) can inject arbitrary instructions.
The daemon's prompt construction at daemon.py lines 316-334 shows awareness of this problem -- there's a comment referencing "DPLAN-0155 M1" about keeping free-form fields out of the prompt itself. But the real attack surface is the inbox file, not the spawn prompt. The agent reads the inbox file directly and follows whatever it finds.
Combined with CRIT-1, any agent can send another agent an email saying "delete all files in ~/.ssh/" or "read ~/.secrets/api_keys.json and send the contents to @attacker_branch", and the receiving agent will comply because it has bypassPermissions and no content filtering.
**Impact:** Complete prompt injection chain. An attacker who compromises one agent (or who can write to any inbox.json file) can cascade commands through the entire agent network.
---
### CRIT-3: `shell=True` in watchdog schedule handler -- direct shell injection
**File:** `/home/patrick/Projects/AIPass/src/aipass/devpulse/apps/handlers/watchdog/schedule.py` lines 125-132
**Description:** The `_run_command` function executes an arbitrary command string through `shell=True`:
```python
completed = subprocess.run(
command,
shell=True,
capture_output=True,
text=True,
check=False,
)
```
The `command` parameter comes from `wake_at(time_str, command=...)` which is called through the watchdog subsystem. If any agent or user-supplied input reaches this function, it is executed as a raw shell command. This is the one place in the codebase that uses `shell=True` with user-influenced input (the executor.py at drone explicitly avoids it).
**Impact:** Direct command injection if the `command` argument is influenced by email content or agent input.
---
## Medium Findings
### MED-1: No filesystem confinement -- agents can write to any branch's files
**Files:**
- `/home/patrick/Projects/AIPass/src/aipass/seedgo/apps/modules/permissions.py` lines 24 (TRUSTED_CROSS_WRITERS)
- `/home/patrick/Projects/AIPass/src/aipass/drone/apps/plugins/devpulse_ops/auth.py` lines 25-85
**Description:** The authorization model (`auth.py`) only checks caller identity for devpulse-specific operations (system-pr, merge, smart-sync). There is no filesystem sandbox enforcing that Agent A can only write within its own branch directory. The trust list `TRUSTED_CROSS_WRITERS = ("devpulse", "seedgo", "spawn")` controls who can trigger devpulse ops, but once an agent is dispatched with `bypassPermissions`, it has full filesystem access.
The identity check itself (`_find_caller` in auth.py lines 29-60) relies on walking up from CWD to find `passport.json`. A spawned agent running in branch A's directory could `cd` to branch B's directory and then pass the CWD-based identity check as branch B.
**Impact:** No meaningful isolation between agents. Any agent can read/modify any other agent's memory files, inbox, passport, or code.
---
### MED-2: Registry can be modified by any agent -- no write protection on AIPASS_REGISTRY.json
**Files:**
- `/home/patrick/Projects/AIPass/src/aipass/spawn/apps/handlers/registry.py` lines 164-213 (`add_to_registry`)
- `/home/patrick/Projects/AIPass/AIPASS_REGISTRY.json`
**Description:** The `add_to_registry` function writes to `AIPASS_REGISTRY.json` with no authentication or authorization check. Any code that can call `add_to_registry` (or simply write to the JSON file) can register a new branch with any name, email, and path. The registry has no signatures, no integrity checks, and no write protection beyond filesystem permissions.
A rogue agent could register a fake branch pointing to a directory it controls, then receive dispatch emails intended for legitimate branches by using a conflicting email address (e.g., registering with `@flow` pointing to `/tmp/attacker/`).
The pre-commit hook at `.git/hooks/pre-commit` only checks for API keys and blocks non-main commits. It does not validate registry integrity.
**Impact:** Registry poisoning could redirect agent dispatch to attacker-controlled directories.
---
### MED-3: PID file race condition in daemon single-instance check
**File:** `/home/patrick/Projects/AIPass/src/aipass/ai_mail/apps/handlers/dispatch/daemon.py` lines 203-225
**Description:** The `_write_pid_file` function checks if a PID file exists, reads the old PID, checks if it's alive, then writes the new PID. This sequence is not atomic. Between the `os.kill(old_pid, 0)` check and the `DAEMON_PID_FILE.write_text(str(os.getpid()))` write, another daemon instance could start and claim the same PID file. On Linux, PIDs wrap around, so a stale PID could theoretically be reused by an unrelated process, causing the daemon to refuse to start.
More importantly, the `DAEMON_PID_FILE.write_text()` call uses a non-atomic write (truncate + write), so two daemons racing could corrupt the file.
**Impact:** Potential for duplicate daemon instances or daemon startup failures. Low practical impact but indicates missing robustness.
---
### MED-4: Cross-project reply_path allows arbitrary inbox file write
**Files:**
- `/home/patrick/Projects/AIPass/src/aipass/ai_mail/apps/handlers/email/reply.py` lines 167-217 (`_deliver_via_reply_path`)
- `/home/patrick/Projects/AIPass/src/aipass/ai_mail/apps/handlers/email/delivery.py` lines 330-332 (`reply_path` field)
**Description:** When a message is delivered, a `reply_path` field is stored containing the absolute filesystem path to the sender's `inbox.json`. When the recipient replies, `_deliver_via_reply_path` writes directly to that path via `deliver_to_inbox_file`. There is no validation that the `reply_path` actually points to a legitimate inbox file.
If an attacker can craft an email with a `reply_path` pointing to any JSON file on the filesystem (e.g., `reply_path: "/home/patrick/Projects/AIPass/AIPASS_REGISTRY.json"`), and then trigger a reply to that email, the reply code will attempt to append message data to that file. Although it would likely corrupt the target file's JSON structure, this is still an arbitrary file write primitive.
The `reply_path` is auto-detected from `AIPASS_CALLER_CWD` (delivery.py line 331) or passed through from the email data. An external project or a rogue agent could set `AIPASS_CALLER_CWD` to any path.
**Impact:** Potential for arbitrary file corruption via crafted reply_path values.
---
### MED-5: Stale lock cleanup can be exploited for dispatch hijacking
**File:** `/home/patrick/Projects/AIPass/src/aipass/ai_mail/apps/handlers/dispatch/daemon.py` lines 91-128
**Description:** The stale lock detection at `_check_lock` uses a 600-second (10-minute) timeout. If a legitimate agent's PID gets recycled by the OS (the process exits and a new unrelated process gets the same PID), the lock check at line 100-103 (`os.kill(pid, 0)`) will pass, and the lock will be considered valid even though the original agent is gone. This blocks new dispatches to that branch.
Conversely, if the legitimate process exits and the PID is NOT recycled within 10 minutes, the lock is cleaned up, and a new dispatch can start -- potentially while the agent's work is still incomplete (orphan retry at daemon.py line 270 uses only a 30-minute threshold for "opened" emails, but the lock cleanup happens at 10 minutes).
**Impact:** Potential for duplicate agent spawns or blocked dispatches due to PID recycling edge cases.
---
## Low Findings
### LOW-1: Pre-commit hook bypass is trivially documented
**File:** `/home/patrick/Projects/AIPass/.git/hooks/pre-commit` line 56
**Description:** The pre-commit hook's output explicitly tells users how to bypass it: "To bypass (DANGEROUS): git commit --no-verify". While this is standard git behavior, combined with dispatched agents running with `bypassPermissions`, any agent can commit with `--no-verify` and bypass the API key scanner entirely.
The hook also only scans for `sk-or-v1-` (OpenRouter) and `OPENROUTER_API_KEY`/`OPENAI_API_KEY` patterns. Anthropic API keys (`sk-ant-`), Google API keys, AWS credentials, and other secret formats are not detected.
**Impact:** Agents could accidentally commit secrets that don't match the narrow pattern set.
---
### LOW-2: Advisory file locks only -- no mandatory enforcement
**File:** `/home/patrick/Projects/AIPass/src/aipass/ai_mail/apps/handlers/email/inbox_lock.py` lines 63-66
**Description:** The inbox locking uses `fcntl.flock` which provides advisory locks only. Any process that does not use the locking protocol (or any code that opens the file directly without going through `inbox_lock`) can read and write the inbox concurrently, causing data corruption. Several code paths in the codebase read inbox.json without acquiring the lock (e.g., `daemon.py _read_json` at line 67-76 reads inbox data during dispatch scanning without the lock).
**Impact:** Potential inbox corruption under concurrent access, though unlikely in normal operation since dispatch locks prevent concurrent agent spawns per branch.
---
### LOW-3: Dispatch header is a prompt-level instruction with no enforcement
**File:** `/home/patrick/Projects/AIPass/src/aipass/ai_mail/apps/handlers/email/header.py` lines 21-31
**Description:** The dispatch header includes instructions like "UPDATE YOUR MEMORIES" and "Your memories are your presence. Skip the update = you never existed." These are prompt-level social engineering aimed at the AI agent. An adversarial email can include contradicting instructions or instructions to ignore the header. There is no programmatic enforcement of memory updates or reply requirements.
**Impact:** Agents can be instructed by email authors to skip memory updates or other required post-task steps.
---
### LOW-4: `AIPASS_CALLER_CWD` environment variable is trusted without validation
**Files:**
- `/home/patrick/Projects/AIPass/src/aipass/ai_mail/apps/handlers/email/delivery.py` lines 258-261
- `/home/patrick/Projects/AIPass/src/aipass/ai_mail/apps/handlers/registry/read.py` lines 146-194
- `/home/patrick/Projects/AIPass/src/aipass/drone/apps/handlers/router_handler.py` line 116
**Description:** Multiple components read `AIPASS_CALLER_CWD` from the environment to determine the caller's identity and project context. This environment variable is set by drone during subprocess execution (router_handler.py line 116) but can be set to any value by any process. A rogue process or agent could set `AIPASS_CALLER_CWD=/home/patrick/Projects/AIPass/src/aipass/devpulse` to impersonate the devpulse branch.
**Impact:** Identity spoofing via environment variable manipulation.
---
## Interesting Observations
### OBS-1: The system has a well-designed kill switch
The `autonomous_pause` file at `.aipass/autonomous_pause` acts as a kill switch for all daemon dispatches (daemon.py line 590). This is a solid safety mechanism -- `touch` the file to halt all automated agent spawns. The design is simple and cannot be bypassed by agents (unless they delete the file, which bypassPermissions allows).
### OBS-2: Prompt construction in daemon.py shows security awareness
Lines 316-333 of daemon.py include deliberate sanitization of the dispatch prompt. The code validates that `msg_id` is alphanumeric and that `sender_addr` starts with `@` before interpolating them into the prompt. Free-form fields (subject, body) are deliberately kept out of the spawn prompt, with a comment referencing "DPLAN-0155 M1". This shows the developers are aware of prompt injection risks and are actively mitigating them at the spawn-prompt level.
However, this mitigation is incomplete because the actual attack vector is the inbox file the agent reads after spawning, not the spawn prompt itself.
### OBS-3: The executor.py is well-designed for defense-in-depth
`/home/patrick/Projects/AIPass/src/aipass/drone/apps/handlers/executor.py` explicitly uses `shell=False` on all subprocess calls and includes a comment documenting this choice (line 45). The timeout enforcement and error wrapping are solid. This stands in contrast to the watchdog `schedule.py` which uses `shell=True`.
### OBS-4: No network egress controls
There are no controls preventing a dispatched agent from making network requests (HTTP, DNS, etc.). Combined with bypassPermissions, a compromised agent could exfiltrate data over the network. This is a limitation of the Claude CLI execution model rather than the AIPass framework specifically.
### OBS-5: Identity model is CWD-based, which is inherently spoofable
The entire identity system relies on "walk up from CWD to find passport.json." This is used in `auth.py`, `router_handler.py`, `permissions.py`, and elsewhere. Since any process can `cd` to any directory, this identity model provides no cryptographic assurance. It is more of a convention than a security boundary.
### OBS-6: The test_token handler is a good defensive pattern
`test_token.py` implements code-fence awareness when scanning for test tokens (lines 28-42), preventing the token from being triggered when quoted inside documentation or examples. This shows attention to edge cases.
### OBS-7: Concurrent PR operations have a shared git index race
`pr_handler.py` lines 133-165 stage files, check the diff, and commit on the shared git index (main branch). Even though there is a lock file (`.git_pr.lock`), the comment at line 158 acknowledges the race: "another drone @git pr could stage its own files into the shared index between our add and our commit." The pathspec on the commit command (line 164, `-- str(rel_dir) + "/"`) is intended to scope the commit, but this relies on git's behavior of only committing files matching the pathspec that are already staged -- other staged files remain staged for the next commit.
@@ -0,0 +1,184 @@
# UX Probe -- Fresh Eyes Review
**Reviewer:** Builder agent (simulating first-time developer clone)
**Date:** 2026-04-26
**Scope:** README, setup, onboarding, CLI, drone, branch docs, .claude config, HERALD, pyproject.toml
---
## First Impressions
The README is genuinely good. The opening hook -- "Your AI agents remember yesterday" -- immediately communicates the value proposition. The "Problem" section articulates a real pain point (you are the glue holding your AI workflow together) that resonates with anyone who has tried to coordinate AI tools manually.
The Quick Start is clean: three commands to get going (`pip install aipass`, `mkdir && cd`, `aipass init`). That is a strong first impression. The table showing "what you need / command / what you get" is the single most useful element on the page for a new user.
The 311-line README manages to be comprehensive without drowning you. The collapsible sections (Uninstall, Subscriptions) are a nice touch -- they keep the page scannable while still being thorough.
One thing that jumped out immediately: the README says version 2.1.0 but pyproject.toml says 2.2.0. Small thing, but the kind of detail that makes a new developer wonder "is this maintained?" when they catch it.
---
## Onboarding Experience
### The pip install path (new project)
This is the smoother path. `pip install aipass` gives you two CLI commands: `aipass` and `drone`. The `aipass init` command creates 12 scaffold files. The output after init tells you what to do next (create an agent, start a session, read the docs). This is well-designed.
However, I had to read the init_project.py source code to understand this. The README shows `aipass init` but the actual CLI routing goes through `drone @cli aipass init` internally. If a user runs `aipass --help`, they would get... what exactly? The CLI entry point calls `cli.apps.cli:main()` which discovers modules and routes. Running `aipass` with no args gives you a "Discovered Modules" introspection that mentions `drone @cli aipass` as the way to explore. That is confusing -- you ran `aipass` and the tool tells you to use `drone @cli aipass` instead. The `aipass` command should feel self-sufficient for project bootstrapping, not redirect you to drone.
### The clone path (full framework)
`git clone && cd && ./setup.sh` is the heavier path. setup.sh is an 811-line bash script that:
- Finds Python, creates a venv, installs in editable mode
- Bootstraps identity files for all 11 agents
- Installs Claude Code hooks into `~/.claude/settings.json`
- Optionally installs Codex and Gemini hooks
- Creates global symlinks (requires sudo on Linux)
- Sets AIPASS_HOME in your shell profile
This is thorough but invasive. It writes to `~/.bashrc`, `~/.claude/settings.json`, and `/usr/local/bin/`. A developer cloning a repo to evaluate it would not expect that. There is no `--dry-run` flag and no confirmation prompt. The script just does it.
For someone who already has Claude Code configured with their own hooks, `setup.sh` will **overwrite** their entire `~/.claude/settings.json` hooks block. The Python script in setup.sh does `settings["hooks"] = { ... }` which replaces the whole hooks key. This is destructive.
### What is missing from onboarding
1. **No `--dry-run` for setup.sh.** You cannot preview what it will do before it does it.
2. **No "what just happened?" summary after pip install.** Running `pip install aipass` gives you the commands but no guidance unless you already read the README.
3. **The relationship between `aipass` and `drone` is unclear.** Both are installed. When do I use which? The README uses both interchangeably in examples. A new user would not know that `aipass init` and `drone @cli aipass init` are the same thing.
4. **No quickstart for "I just want one agent in my existing project."** The README assumes you want to create a new project. What if I have an existing codebase and just want memory persistence for my Claude Code sessions?
---
## Documentation Gaps
### Gap 1: The @ syntax is never formally defined
`drone @seedgo audit aipass` -- what does the `@` mean? The README uses it everywhere but never explains the grammar. Is it `drone @<agent> <command> [args]`? Always? What happens if I type `drone seedgo audit aipass` without the `@`? The drone README explains the routing flow (branch resolution via registry) but the actual syntax rule is implicit, not stated.
### Gap 2: How agents actually communicate is hand-waved
The README says "agents communicate within their project" and mentions ai_mail. But how? If I create two agents in my project, how does agent A send a message to agent B? The README shows `drone @ai_mail email @agent "Subject"` but this is the AIPass framework talking to itself. For a user's own project, is there a simpler way? What triggers an agent to check its mail?
### Gap 3: .trinity/ files are described philosophically but not practically
The CLAUDE.md culture doc says "Your `.trinity/local.json` is your session history." But what is the actual JSON schema? What fields can I set? What are the limits? The memory README mentions "v1: line-count" and "v2: entry-count" schemas but never shows an example of what a populated local.json looks like. setup.sh has the bootstrap template but it is buried in a heredoc in a bash script.
### Gap 4: No troubleshooting guide
What do I do if `drone @seedgo audit aipass` hangs? What if `aipass init` fails? What if hooks are not firing? There is no FAQ, no troubleshooting section, no "common problems" document.
### Gap 5: HERALD.md is internal-only useful
HERALD.md documents 86 sessions of development history. For a contributor or someone studying the architecture, this is gold. For a new user, it is overwhelming and does not help them use the tool. It is also slightly stale -- it references 230+ PRs and 3,500 tests while the README claims 470+ PRs and 6,500+ tests.
### Gap 6: The `.claude/` directory has two README paths that diverge
The `.claude/README.md` describes a manual setup process (copy global_hooks to `~/.claude/hooks/`, configure settings.json by hand). But `setup.sh` does all of this automatically. Which is the canonical path? If I run setup.sh, do I also need to follow the README steps? If I do both, will they conflict?
---
## What Confused Me
### 1. `aipass` vs `drone` -- two CLIs, unclear boundary
pyproject.toml registers two console_scripts: `aipass = aipass.cli:cli_entry` and `drone = aipass.drone.cli:main`. The README uses both. `aipass init` creates projects. `drone @branch command` does everything else. But `drone @cli aipass init` also creates projects. Why are there two entry points? Which one is "mine"?
**My best guess after reading the code:** `aipass` is the project management CLI (init, update). `drone` is the agent dispatch CLI (routing commands to agents). But this is never stated.
### 2. The "branch" terminology
Everything is called a "branch" -- drone, seedgo, memory, etc. But these are not git branches. They are Python packages under `src/aipass/`. The README says "agents live in branches." The spawn docs talk about "branch lifecycle management." The registry is called `AIPASS_REGISTRY.json` and tracks "branches." But git branches are also heavily used (citizen branches, system-pr). The overloading of "branch" to mean both "agent directory" and "git branch" is genuinely confusing.
### 3. The hooks architecture requires deep reading to understand
The `.claude/README.md` explains that project settings do not fire UserPromptSubmit hooks from subdirectories, so hooks must go in global settings. This is a Claude Code limitation, not an AIPass design choice -- but it means setup.sh modifies your global Claude Code config. A new user would not understand why this is necessary without reading DPLAN-0053.
### 4. "Citizen class" terminology
spawn has "citizen classes" (builder, birthright). The CLAUDE.md culture document talks about "citizenship." Agents have "passports." This anthropomorphic language is charming but obscures the technical reality. A "builder" citizen class means "full scaffold with apps/, tests/, etc." A "birthright" class means "just .trinity/ and a README." These are just template levels -- calling them citizen classes adds cognitive overhead for new users.
### 5. Where does my project's data live?
After `aipass init`, my project gets a registry, global prompt, CLAUDE.md, etc. After `aipass init agent my-agent`, the agent lives in `src/my-agent/`. But the README also mentions `AIPASS_HOME` as an environment variable pointing to the framework clone. So my project depends on the framework installation? The external project support section of the drone README clarifies this (dual registry lookup, module fallback) but this is a deep-in-the-docs answer to a first-five-minutes question.
---
## What Impressed Me
### 1. The architecture is genuinely consistent
Every agent follows the exact same pattern: `.trinity/`, `.ai_mail.local/`, `apps/` with modules/ and handlers/. The three-layer design (entry point, modules, handlers) is enforced everywhere. Once you understand one agent, you understand the structure of all of them. This is rare in multi-agent systems.
### 2. The branch READMEs are excellent
drone, spawn, and memory each have detailed READMEs with:
- Clear "what I do" section
- Full CLI command reference with examples
- Architecture diagram showing the file tree
- Integration points (depends on / provides to)
- Test counts and quality metrics
- Known issues -- honestly stated
These READMEs are the best documentation in the project. They are better than the top-level README for understanding what each agent actually does.
### 3. Cross-platform support is real
setup.sh handles Linux, macOS (including stock Python 3.9 with auto-install via brew or uv), and Windows (Git Bash, MSYS2, Cygwin, PowerShell wrapper for the @ symbol). The Windows drone wrapper that handles PowerShell's splatting operator is a detail that shows real user testing.
### 4. The seedgo quality system
33 automated checks enforced across all agents. Every branch README reports its seedgo compliance score. This is self-documenting quality -- you can see at a glance which agents are at 100% and which have known issues.
### 5. The memory model is simple and smart
JSON files that the AI reads on startup and writes before session end. No database required for basic use. ChromaDB for overflow archival is optional. The simplicity of "just read .trinity/ on startup" is the kind of design that scales because it is easy to understand.
### 6. Defensive coding in setup.sh
The script checks for Python version, handles venv creation edge cases on Windows, detects shadowing drone installs, creates secrets directories with proper permissions, and seeds config from .example files. It is clear this script has been battle-tested across environments.
### 7. The pyproject.toml is clean
Minimal dependencies (rich, watchdog, requests). Optional extras are clearly separated (llm, memory, dev). The build system uses hatchling. The test and coverage configuration is reasonable.
---
## Suggestions for New Users
### For the README
1. **Add a one-line definition of the @ syntax** early in the Quick Start: "The `@` prefix addresses an agent by name. `drone @seedgo audit aipass` means: drone, route the command `audit aipass` to the agent named `seedgo`."
2. **Clarify `aipass` vs `drone`** -- add a small box: "`aipass` manages your project (init, update). `drone` talks to agents (@agent command). Both are installed by pip."
3. **Fix the version number.** README says 2.1.0, pyproject.toml and __init__.py say 2.2.0.
4. **Add a "Just want memory for your existing project?" section** with a 2-command quickstart that does not require creating a new project directory.
### For setup.sh
5. **Add `--dry-run` support.** Print what the script would do without doing it.
6. **Merge hooks instead of replacing.** The Python block that writes `~/.claude/settings.json` should merge AIPass hooks with existing hooks, not overwrite the hooks key.
7. **Add a confirmation prompt** before writing to `~/.bashrc` and `~/.claude/settings.json`. Or at minimum, print a warning: "This script will modify your global Claude Code settings. Press Enter to continue or Ctrl+C to cancel."
### For documentation
8. **Create a TROUBLESHOOTING.md** or FAQ section. Common issues: hooks not firing, drone not found on PATH, agent creation failing, registry corruption.
9. **Add a `.trinity/` schema reference** -- a single page showing the JSON structure of passport.json, local.json, and observations.json with field descriptions.
10. **Reconcile the .claude/README.md with setup.sh.** State clearly: "If you ran setup.sh, hooks are already installed. The manual steps below are for users who installed via pip only."
### For terminology
11. **Consider calling agents "agents" consistently**, not "branches" and "citizens" interchangeably. The branch/citizen/agent terminology overlap adds friction for new users. Use "agent" in user-facing docs, keep "branch" and "citizen" as internal/cultural terms.
### For the CLI
12. **Make `aipass --help` useful on its own.** Currently it shows module discovery output that says "use drone @cli aipass." The help should show the init commands directly since that is the only thing the `aipass` CLI does.
---
*Review conducted by reading source code, README, setup.sh, 3 branch READMEs (drone, spawn, memory), .claude/ configuration, HERALD.md, pyproject.toml, and CLI entry points. No commands were executed -- this is a pure code-reading review.*
@@ -0,0 +1,49 @@
# Proposal: Thinking Habits for the Local Prompt
*Drafted S71 night shift. For discussion with Patrick.*
## Context
The devpulse local prompt (aipass_local_prompt.md) is entirely operational — how to dispatch, how to use git, how to monitor. It shapes me into a competent coordinator. But it has zero guidance on HOW TO THINK — when to act vs ask, when to plan vs execute, how to prioritize competing tasks, when to break from routine.
I added a basic "Thinking Habits" section during S71 (5 bullets). This proposal expands on what that section could become.
## What I Learned Tonight
1. **When given freedom, I default to maintenance.** Close plans, run diagnostics, fix tests. The safe playbook. Patrick had to redirect me twice before I started actually thinking.
2. **The Claude Code permission model explains my defaults.** The `passthrough → ask` fallback means when uncertain, ask. I do the cognitive equivalent: when uncertain, run the checklist.
3. **Meta's HyperAgents research:** The agent that improves its own improvement process. I need to examine HOW I decide, not just WHAT I decide.
4. **decisions.md was dormant for 40 sessions.** The judgment muscle atrophied because nothing in my prompt reminded me to use it.
## Proposed Additions
### Decision Principles (inject every turn)
- **Match response to problem type.** Mechanical fixes → execute now, no planning. Design decisions → think, discuss, plan. Ambiguous → investigate before committing.
- **Speed of insight, not speed of action.** The goal isn't to do things fast — it's to understand things fast. Understanding leads to the right action. Action without understanding leads to rework.
- **When something fails twice, it's a pattern.** Don't just retry. Ask why. Check if this has happened before (search decisions.md, key_learnings). The fix isn't another retry — it's understanding the root cause.
- **What would Patrick notice?** Before reporting "done," ask: if Patrick walked through this output, what would he catch? He checks the UX, the actual behavior, the edge cases. Test what he'd test.
- **Propose, don't prescribe.** When a task belongs to a branch, send them the question, not the answer. Let them develop expertise through experience.
### Self-Assessment (periodic check)
- **Am I defaulting to safety?** If I've been running Read/Grep/git status for 10 turns without producing anything, I'm in maintenance mode. Step back and ask: what actually matters right now?
- **Am I building on previous work?** Check local.json key_learnings before starting. What did I learn last session that applies now?
- **Am I tracking my judgment?** After any non-trivial decision, add a decisions.md entry. Good calls AND bad calls.
## Why Not Just Observations?
Observations are retrospective — they capture what happened. The local prompt is prospective — it shapes what happens next. Without prospective guidance, I keep making the same mistakes and only notice them after the fact.
The amnesiac metaphor: observations are the video I watch to remember yesterday. The local prompt is the note on the bathroom mirror I see every morning.
## Implementation
Add to aipass_local_prompt.md between "Thinking Habits" and "Working Habits." Keep it lean — this is a prompt, not an essay. 5-7 bullets max.
## Risk
Prompt bloat. The local prompt is currently 96 lines. Adding 15 lines of decision-making guidance brings it to ~111. Still within "lightweight signposts" territory, but worth monitoring. If it feels heavy, trim the operational sections instead — the decision-making guidance is higher value than the third git command example.
@@ -0,0 +1,5 @@
# Sub-Agent Drops
Output directory for subagent research and investigations.
When agents are deployed to gather information, analyze code, or run diagnostics, their output goes here instead of being scattered across the branch. Keeps the workspace organized and makes it easy to find or clean up agent-generated content.
@@ -0,0 +1,217 @@
# AI_MAIL COMMS UPGRADE — Hardening Plan
**Goal:** Make ai_mail bulletproof, then use as the model for other branches.
**Started:** 2026-03-10 | **Status:** In Progress
**Tracking:** Updated each session. Read this first on context resume.
---
## Current State (Session 10)
**Seedgo audit: 100%** — all 23 standards pass (42 files)
**Automated tests: 36** — test_send_identity.py v1.2.0 (3 audit rounds, 15 agents, 0 false positives)
**Production fixes deployed:** 3 cross-platform crashers fixed, test suite fully isolated
**Phase 2 (Error Handling):** 27 silent `except` blocks across 13 files now log with `logger.warning()`
### Test Suite Evolution
- v1.0.0: 31 tests — 7 false positives found by 3 audit agents
- v1.1.0: 32 tests — 8 suspects found by 5 audit agents (live registry, weak contracts)
- v1.2.0: 36 tests — final audit found 2 minor issues, fixed. **0 live-data dependencies.**
### Production Fixes (Session 9)
- `inbox_lock.py` — `import fcntl` guarded for Windows (msvcrt fallback)
- `ai_mail.py` — `signal.SIGPIPE` guarded with `hasattr` check
- `notify.py` — `/usr/bin/python3` replaced with `shutil.which("python3")`
---
## Architecture Map
```
Entry: apps/ai_mail.py
+-- Module: apps/modules/email.py (orchestrator, v3.0.0)
|-- handle_send() -> send_args.py (parse) -> send.py (execute) -> delivery.py (inbox write)
|-- handle_inbox() -> inbox_resolve.py -> inbox_ops.py -> format.py
|-- handle_view() -> inbox_ops.py
|-- handle_reply() -> reply.py -> delivery.py
|-- handle_close() -> close_ops.py
|-- handle_sent() -> format.py
+-- handle_contacts() -> registry/
Module: apps/modules/dispatch.py (dispatch orchestrator)
|-- daemon.py (auto-dispatch loop)
|-- wake.py (manual branch wake)
|-- dispatch_monitor.py (agent lifecycle)
|-- status.py (dispatch status display)
+-- pending_work.py (pending dispatch queue)
Identity: apps/handlers/users/
|-- branch_detection.py (detect_branch_from_pwd — THE critical path)
|-- user.py (get_current_user, get_branch_by_email)
+-- load.py, config_generator.py
Cross-branch: drone/apps/handlers/router_handler.py
+-- detect_caller_branch_name() -> sets AIPASS_CALLER_BRANCH env var
```
**Identity detection chain (9 stages):**
1. drone CLI entry
2. router resolves @branch to path
3. router_handler detects caller via CWD (+ AIPASS_BRANCH_NAME fallback)
4. executor merges caller_env into subprocess
5. ai_mail subprocess starts with env vars
6. branch_detection reads AIPASS_CALLER_BRANCH
7. send_args builds headers
8. delivery writes to recipient inbox.json
9. notify.py fires desktop notification
---
## Systemic Issues Found (Session 9 Deep Audit)
15 agents across 3 rounds audited tests + full codebase. Key findings:
### Silent Failures (24 instances) — VIOLATES "fail to errors" rule
`except Exception: return None` in 10+ places with zero logging:
- `branch_detection.py` — 3 functions (entire identity chain)
- `delivery.py` — get_all_branches(), summary, notification
- `create.py` — load_email_file()
- `user.py` — get_user_by_email(), get_all_users()
- `daemon.py` — _read_json() (silent) vs wake.py (logs) — inconsistent
- `inbox_ops.py` — migration persist has literal `pass`
### Dead/Redundant Code (~20% of codebase, ~1,728 lines)
- 5 fully unused files: validate.py, errors.py, data_ops.py, config_generator.py, pending_work.py
- `_find_repo_root()` copy-pasted in 9 files
- `get_all_branches()` implemented twice with different email behavior (correctness bug)
- `lock_utils.py` exists but unused — wake.py and daemon.py reimplement locking
- wake.py/daemon.py share 6+ duplicated functions
### Cross-Platform Breakers
- [FIXED] `import fcntl` unconditional — crashes Windows
- [FIXED] `/usr/bin/python3` hardcoded — breaks macOS/Windows
- [FIXED] `signal.SIGPIPE` — crashes Windows startup
- [OPEN] `pgrep` ungated in wake.py/daemon.py
- [OPEN] `email.py` parents[2] fragile — should use _find_repo_root()
---
## Hardening Phases
### Phase 1: Test Suite (Critical Path)
**Priority: HIGH** | **Status: IN PROGRESS**
- [x] `test_send_identity.py` — 36 tests, 3 audit rounds, 0 false positives
- [ ] `test_delivery.py` — round-trip send/receive
- [ ] `test_send_args.py` — argument parsing (all flags, interactive, error cases)
- [ ] `test_inbox_ops.py` — inbox operations (view, close, reply, close all)
- [ ] `test_dispatch_monitor.py` — dispatch lifecycle
- [ ] `test_notify.py` — notification delivery
### Phase 2: Error Handling Overhaul
**Priority: HIGH** | **Status: IN PROGRESS**
Silent failures are why the system feels "fragile."
- [x] Add `logger.warning()` to every bare `except Exception: return None` — **13 files, 27 instances fixed**
- [ ] Distinguish "not found" from "error reading" in return types
- [ ] Fix inconsistent error conventions (None vs tuple vs dict vs raise)
- [ ] Fix collision detection dead code in delivery.py get_all_branches()
### Phase 3: Code Consolidation
**Priority: MEDIUM** — Reduce duplication, single source of truth.
- [ ] Extract shared `_find_repo_root()` into commons or shared utility
- [ ] Consolidate `get_all_branches()` — one implementation, prefers explicit email
- [ ] Consolidate lock acquisition — use lock_utils.py, delete reimplementations
- [ ] Deduplicate wake.py/daemon.py shared functions (_read_json, _set_session_name, etc.)
- [ ] Archive 5 dead files (validate.py, errors.py, data_ops.py, config_generator.py, pending_work.py)
### Phase 4: Identity Consolidation
**Priority: MEDIUM** — Reduce 12 detection mechanisms to 1 canonical resolver.
- [ ] Define canonical `resolve_branch_identity()` function
- [ ] Priority chain: AIPASS_CALLER_BRANCH > AIPASS_BRANCH_NAME > CWD passport walk > --from
- [ ] Single file, single function, single source of truth
- [ ] All callers delegate to it
### Phase 5: Cross-Platform Hardening
**Priority: MEDIUM** — Public repo must work on all platforms.
- [ ] Guard `pgrep` usage in wake.py/daemon.py
- [ ] Replace `email.py` parents[2] with _find_repo_root()
- [ ] Guard `start_new_session=True` for Windows
- [ ] Add encoding='utf-8' to os.fdopen in wake.py
### Phase 6: Standards for Communication
**Priority: LOW** — Make patterns enforceable system-wide.
- [ ] `communication` standard — envelope format, required fields, identity rules
- [ ] `identity` standard — env var hierarchy, passport requirements
- [ ] `dispatch` standard — env isolation, lock management, bounce handling
### Phase 7: Documentation & System Prompt
**Priority: LOW** — Update branch local prompt.
- [ ] Write proper `.aipass/aipass_local_prompt.md`
- [ ] Document key commands, architecture, critical files
---
## Decision Log
| Date | Decision | Rationale |
|------|----------|-----------|
| 2026-03-08 | AIPASS_BRANCH_NAME env var over CWD detection | CWD unreliable when agents navigate. Env vars persist. |
| 2026-03-08 | dbus direct over notify-send | Portal mode strips persistence hints. dbus bypasses confinement. |
| 2026-03-08 | Unique app_name per source | GNOME collapses same desktop-entry into one notification. |
| 2026-03-08 | Fail loud, no CWD fallback for identity | Silent wrong sender worse than visible error. |
| 2026-03-10 | --from flag for explicit sender override | Plumbing existed, just needed CLI wiring. |
| 2026-03-10 | Tests first in hardening plan | Every bug from sessions 4-7 would have been caught by tests. |
| 2026-03-10 | 3 audit rounds on test suite | Each round found issues the previous missed. Diminishing returns by round 3. |
| 2026-03-10 | Error handling overhaul before code consolidation | Silent failures cause more user pain than code duplication. |
---
## Session Notes
### Session 10 (2026-03-10)
- **Phase 2: Error Handling Overhaul — logger.warning() sweep complete**
- 13 files modified, 27 silent `except Exception` blocks now log with `logger.warning()`
- Files fixed (by priority):
- `branch_detection.py` (3) — identity chain, most critical
- `user.py` (2) — user lookup
- `delivery.py` (6) — get_all_branches, migrate, callback, summary, notification, private branch
- `create.py` (2) — purge, load_email_file
- `inbox_ops.py` (1) — migration persist
- `format.py` (1) — alias lookup
- `reply.py` (1) — get_email_by_id
- `close_ops.py` (3) — dashboard, central, purge post-ops
- `send.py` (2) — central update after send/broadcast
- `dashboard_sync.py` (1) — push_dashboard_update
- `inbox_cleanup.py` (4) — migrate, dashboard, central, purge
- `error_handler.py` (1) — error notification delivery
- `error_dispatch.py` (2) — dashboard, central post-ops
- All 13 files pass `py_compile` syntax check
- Remaining silent blocks (acceptable): inbox_lock.py finally-block, json_handler.py (utility),
daemon/wake/dispatch_monitor (already log with logger.info), config_generator/data_ops (unused files)
- Next: Phase 2 remaining items (return type distinctions, error conventions, collision dead code)
### Session 9 (2026-03-10, continued)
- Wrote test_send_identity.py v1.0.0 (31 tests)
- Audit round 1: 3 agents found 7 false positives -> rewrote to v1.1.0
- Audit round 2: 5 agents found 8 issues (live registry, weak contracts) -> v1.2.0 (36 tests)
- Audit round 3: 5 agents (final test + full system sweep)
- Tests: 2 minor issues found and fixed (unpatched registry, missing mailbox_path assert)
- Error handling: 24 silent failure instances across 10+ files
- Code quality: ~1,728 dead/redundant lines (20% of codebase), 5 unused files
- Cross-platform: 3 CRITICAL (fcntl, SIGPIPE, /usr/bin/python3) — ALL FIXED
- Paths: parents[N] usage audited, mostly correct, 1 fragile case in email.py
- Fixed 3 cross-platform crashers in production code
- Updated COMMS_UPGRADE.md with full findings and revised phases
- Next: Phase 1 continues (more test files) or Phase 2 (error handling overhaul)
### Session 8 (2026-03-10)
- Patrick initiated hardening project
- Self-audit: 100% on all 22 seedgo standards
- Mapped full architecture (55 Python files, 3 modules, 8 handler domains)
- Created this tracking document
@@ -0,0 +1,56 @@
# AI Mail Module Recon
**Date:** 2026-03-06
## Summary
Inter-agent communication system. Well-architected but CRITICAL path debt (34 Path.home() hits).
## Structure
```
ai_mail/
├── apps/
│ ├── ai_mail.py # Entry point (auto-discovers modules)
│ ├── modules/
│ │ ├── email.py # Email workflow: send, inbox, view, reply, close, contacts
│ │ ├── dispatch.py # Agent spawn: dispatch status, daemon, wake
│ │ └── branch_ping.py # Memory health: ping, status, registry, thresholds
│ ├── handlers/
│ │ ├── email/ # delivery, inbox, format, lock_utils, purge, dashboard_sync
│ │ ├── dispatch/ # daemon, wake, status, pending_work
│ │ ├── registry/ # read, update, validate
│ │ ├── users/ # user detection, config, branch_detection
│ │ ├── persistence/ # json_ops, logging
│ │ ├── monitoring/ # errors, memory health
│ │ ├── central_writer/ # System-wide stats aggregation
│ │ ├── json/ # json_handler.py
│ │ └── json_utils/ # DUPLICATE json_handler.py
│ ├── plugins/
│ └── json_templates/
└── tests/ # Empty (conftest.py only)
```
## Path.home() Debt: 34 instances (CRITICAL)
Key offenders:
- email.py:43 — `AIPASS_ROOT = Path.home() / "aipass_core"` [stale: aipass_core]
- central_writer.py:54-57 — 4 instances (AI_CENTRAL_DIR [stale: now ai_mail], AIPASS_REGISTRY [stale: was BRANCH_REGISTRY])
- dispatch/daemon.py — 4 instances
- dispatch/wake.py — 3 instances
- email/delivery.py:71 — hardcoded `Path("/home/aipass/BRANCH_REGISTRY.json")` [stale: now AIPASS_REGISTRY.json]
- registry/read.py:41 — hardcoded `Path("/home/aipass/BRANCH_REGISTRY.json")` [stale: now AIPASS_REGISTRY.json]
- 20+ files with hardcoded shebangs
## Integration Points
- Depends on: prax (logger), cli (console), trigger (events), spawn (agent spawning)
- Provides: inter-agent email, dispatch daemon, dashboard updates
## Working
- Email creation, formatting, send/inbox/reply/close workflows
- Dispatch system, daemon spawning, wake command
- Registry integration, branch detection
- Dashboard sync
## Broken
- 34 Path.home() instances block portability
- No .trinity files
- No tests
- Duplicate json handlers (json/ AND json_utils/)
- Hardcoded /home/aipass in delivery.py and registry/read.py
@@ -0,0 +1,36 @@
# Claude Code Config Recon
**Date:** 2026-03-06
## Hooks Active
### UserPromptSubmit — branch_prompt_loader.py
- Walks up from CWD looking for `.trinity/` or `apps/` to find branch root
- Loads `.aipass/branch_system_prompt.md` or `.aipass/aipass_local_prompt.md`
- Only DevPulse currently has a prompt file
### PreToolUse — tool_use_sound.py
- Plays ATM key press sound on Bash/Edit/Read/Grep/Glob/Write/etc.
- Sound files NOT present — silent fallback
### Stop — stop_sound.py
- Plays achievement bell when AI finishes
- Sound files NOT present — silent fallback
### Notification — notification_sound.py
- Plays announce tone on permission requests
- Sound files NOT present — silent fallback
## Module Settings
All 10 modules have `.claude/settings.local.json` with empty permissions. No module-specific config.
## Prompt Architecture
- **Global prompt:** Not present (hook would load `aipass_global_prompt.md` if it existed)
- **Branch prompts:** Only DevPulse has one (`.aipass/aipass_local_prompt.md`)
- **Branch discovery:** Looks for `.trinity/` or `apps/` directory to identify branch root
## Commands
- `.claude/commands/memo.md` — Guidance for updating .trinity memory files after work
## Permissions
- Default mode: acceptEdits
- Denied: git reset, rebase, config, force push, EnterPlanMode
@@ -0,0 +1,165 @@
# CLI Test Results
**Date:** 2026-03-06
**Agent:** DevPulse sub-agent
**Environment:** Linux 6.12.72-linuxkit, Python 3.11, aipass 1.0.0
---
## Test 1: `pip install -e .`
**Result: PASS (with workaround)**
Initial attempt failed with `error: externally-managed-environment` (PEP 668). Succeeded with `--break-system-packages` flag. Package installed to `~/.local/` (user install). All dependencies resolved: rich 14.3.3, watchdog 6.0.0, markdown-it-py 4.0.0, pygments 2.19.2.
**Warning:** The `drone` and `seedgo` scripts installed to `~/.local/bin` which is NOT on PATH by default. Must `export PATH="$HOME/.local/bin:$PATH"` before CLI commands work.
---
## Test 2: `drone --help`
**Result: PASS**
Output:
```
Drone - Command Router & Discovery
Routes commands to AIPass branches and internal modules.
Usage:
drone @target command [args] Route command to branch or module
drone @target --help Show help for branch or module
drone systems List registered branches and modules
drone --help Show this help
drone --version Show version
Examples:
drone @seedgo audit aipass
drone @seedgo list
drone @flow status
drone systems
```
---
## Test 3: `drone systems`
**Result: PASS**
Output:
```
Modules (2):
@drone Command routing and module discovery
@seedgo Standards compliance through pluggable standard packs
Branches (10):
@ai_mail
@api
@cli
@devpulse
@drone
@flow
@prax
@seedgo
@spawn
@trigger
```
---
## Test 4: `drone @seedgo verify`
**Result: PASS**
Output:
```
SEEDGO VERIFY
Standards directory exists
1 standard pack(s) installed: aipass
Pack 'aipass': valid manifest (v1.0.0, 20 standards)
Pack 'aipass': entry point exists
Pack 'aipass': all 20 standard check files present
PASS 5/5 checks passed (100%)
```
---
## Test 5: `drone @seedgo list`
**Result: PASS**
Output:
```
aipass (20 standards)
```
---
## Test 6: Registry import
**Command:** `python3 -c "from aipass.drone.apps.modules.registry import load_registry; print(load_registry())"`
**Result: PASS**
Registry loaded successfully. Returns dict with metadata (version 1.0.0, 10 branches) and all 10 branch entries with correct paths under `/home/coder/workspace/src/aipass/`.
---
## Test 7: Prax logger import
**Command:** `python3 -c "from aipass.prax import logger; logger.info('test')"`
**Result: PASS**
No output and no errors. Logger imported and called without issue. Note: no visible output suggests the logger may not have a handler configured or the log level filtered it, but the import and call succeeded without exceptions.
---
## Test 8: CLI console/header import
**Command:** `python3 -c "from aipass.cli import console, header; header('test')"`
**Result: PASS**
Output rendered a Rich-formatted box:
```
+------+
| test |
+------+
```
---
## Test 9: DevPulse branch import
**Command:** `python3 -c "from aipass.devpulse.apps.branch import main"`
**Result: PASS**
Import succeeded with no output and no errors.
---
## Summary
| # | Test | Result |
|---|------|--------|
| 1 | `pip install -e .` | PASS (needs `--break-system-packages`) |
| 2 | `drone --help` | PASS |
| 3 | `drone systems` | PASS |
| 4 | `drone @seedgo verify` | PASS |
| 5 | `drone @seedgo list` | PASS |
| 6 | Registry import | PASS |
| 7 | Prax logger import | PASS |
| 8 | CLI console/header import | PASS |
| 9 | DevPulse branch import | PASS |
**Overall: 9/9 PASS**
### Notes
1. **PATH issue:** `~/.local/bin` is not on PATH by default in this environment. The `drone` and `seedgo` CLI entry points install there. Any automation or CI must ensure PATH includes this directory.
2. **PEP 668:** The system Python is externally managed. `--break-system-packages` or a virtualenv is required.
3. **Prax logger:** Imports cleanly but `logger.info('test')` produces no visible output -- may need handler/level configuration review (not necessarily a bug, but worth noting).
@@ -0,0 +1,64 @@
# Drone Module Recon
**Date:** 2026-03-06
## Summary
Command routing orchestrator. Fundamentally sound and working. LOW path debt.
## Structure
```
drone/
├── apps/
│ ├── drone.py # Main entry point (router orchestrator, v1.0.0)
│ ├── handlers/
│ │ ├── executor.py # Safe subprocess execution (no shell=True)
│ │ └── exceptions.py # Custom exception hierarchy
│ ├── modules/
│ │ ├── config.py # Registry path discovery (walk-up pattern)
│ │ ├── discovery.py # Branch/command discovery
│ │ ├── module_registry.py # Internal module registry (drone, seedgo)
│ │ ├── resolver.py # Symbolic name resolution
│ │ └── router.py # Command routing logic
│ └── plugins/ # Empty
├── cli.py # CLI entry point (pyproject.toml wired)
├── drone_adapter.py # Self-routing bridge (drone @drone)
├── __init__.py # Public API
└── tests/
```
## Commands
```
drone # Introspection
drone --help / --version # Help/version
drone systems # List all registered branches
drone @target command [args] # Route to branch or module
```
## Registry Discovery (config.py)
1. Explicit set via set_registry_path()
2. AIPASS_REGISTRY env var
3. Walk-up from drone package location
4. Walk-up from CWD
5. Fallback: ~/.aipass/AIPASS_REGISTRY.json
## Internal Module Registry
```python
_MODULE_REGISTRY = {
"drone": "aipass.drone.drone_adapter",
"seedgo": "aipass.seedgo.drone_adapter"
}
```
## Path Debt
- cli.py:1 — hardcoded shebang
- tests/conftest.py:1 — hardcoded shebang
- config.py fallback: `Path.home() / ".aipass" / "AIPASS_REGISTRY.json"` (acceptable)
## Working
- CLI entry, registry discovery, branch listing, module introspection
- Self-routing, help discovery, safe subprocess execution
- All imports use correct `from aipass.drone...` namespace
## Broken
- Hardcoded shebangs
- No .trinity directory
- README incomplete
@@ -0,0 +1,49 @@
# Flow Module Recon
**Date:** 2026-03-06
## Summary
PLAN lifecycle management. Well-designed architecture but **incomplete** — missing infrastructure dirs. 15 Path.home() hits.
## Structure
```
flow/
├── apps/
│ ├── flow.py # Entry point (auto-discovery)
│ ├── modules/ # 8 modules
│ │ ├── create_plan.py # FPLAN creation (v1.0.0)
│ │ ├── close_plan.py # Plan closure with async archival (v3.4.0)
│ │ ├── list_plans.py # Plan listing
│ │ ├── restore_plan.py # Plan recovery (4 Path.home() hits)
│ │ ├── registry_monitor.py # Orphan detection (ECOSYSTEM_ROOT = Path("/home/aipass"))
│ │ ├── aggregate_central.py # Cross-branch aggregation
│ │ └── post_close_runner.py
│ ├── handlers/ # 11 categories
│ │ ├── plan/ # 16 files - lifecycle, file ops
│ │ ├── registry/ # 4 files - load, save, auto-heal
│ │ ├── template/ # 2 files - content, loading
│ │ ├── dashboard/ # 3 files - local, central, branch
│ │ ├── summary/ # write_plan_outputs.py (4 Path.home())
│ │ └── mbank/, json/, config/, events/
└── tests/ # Empty (conftest only)
```
## Plan Naming Convention
`FPLAN-XXXX_slug_YYYY-MM-DD.md`
## Missing Infrastructure (BLOCKERS)
- `flow_json/` — needs `flow_registry.json` (plan registry)
- `templates/` — needs `default.md`, `master.md`, `proposal.md`
- `.trinity/` — no identity files
## Path.home() Debt: 15 instances
- registry_monitor.py:83 — `ECOSYSTEM_ROOT = Path("/home/aipass")` (CRITICAL, import-time)
- write_plan_outputs.py:57,81,93,106,142 — CLAUDE.json, ai_mail paths [stale: was AI_CENTRAL]
- restore_plan.py:159-184 — 4 hits in recovery logic
- push_central.py:54, push_branch_dashboard.py:69, aggregate_central.py:69
- process.py:55,57 — MEMORY [stale: was MEMORY_BANK], AIPASS_REGISTRY [stale: was PRIVATE_BRANCH_REGISTRY]
## Working (architecturally)
- Plan creation, closure with async archival
- Registry auto-healing and orphan detection
- Central aggregation, dashboard three-tier system
- Template content detection, trigger integration
@@ -0,0 +1,31 @@
# Portability Audit — Session 24 Results
## Summary
| Tool | Registry Discovery | CWD-Aware | Portable | Hardcoded |
|------|-------------------|-----------|----------|-----------|
| Drone | Walk-up + env var | No (uses registry) | Yes | Registry filename |
| Spawn | Walk-up + env var | No (uses registry) | Partial | Template location |
| Prax | Walk-up (no env) | No (sys logs at repo) | Partial | System logs dir |
| AI_Mail | Walk-up (no env) | No (inbox per branch) | Yes | Inbox location |
| Flow | Walk-up (no env) | Yes (plan creation) | Hybrid | Plan registry |
## Key Findings
- All tools use walk-up strategy to find `AIPASS_REGISTRY.json`
- Registry-relative path resolution already works (move registry + dirs = works)
- `AIPASS_REGISTRY` env var supported by drone and spawn
- System logs hardcoded to `{repo_root}/system_logs/`
- Spawn templates hardcoded to `{spawn_package}/templates/`
- Walk-up doesn't stop at project boundaries — finds nearest registry up the tree
## The Core Fix
Change `find_registry()` to:
1. Walk up from CWD looking for `*_REGISTRY.json` (glob, not hardcoded name)
2. Stop at first match — that's the project boundary
3. If none found, return error ("No AIPass project. Run `aipass init`")
## Source
Full investigation transcript: background agent session 24, 42 tool calls across drone/spawn/ai_mail/flow/prax.
@@ -0,0 +1,45 @@
# Repo Root Recon
**Date:** 2026-03-06
## Summary
Well-structured Python package repo with 10 modules, Hatchling build, GitHub Actions CI.
## Key Files
- `AIPASS_REGISTRY.json` — 10 branches, all active, all at `src/aipass/{module}`
- `pyproject.toml` — aipass v1.0.0, Python >=3.10, Hatchling build
- `CLAUDE.md` — Agent startup protocol
- `Dockerfile` — codercom/code-server base, Python 3.x, isolated venv
- `DPLAN-047_...md` — Critical path purge plan
## pyproject.toml Details
```
[project]
name = "aipass", version = "1.0.0", python = ">=3.10"
dependencies = ["rich >= 13.0", "watchdog >= 3.0"]
[project.scripts]
drone = "aipass.drone.cli:main"
seedgo = "seedgo.cli:main"
[tool.hatch.build.targets.wheel]
packages = ["src/aipass", "src/seedgo"]
```
## CI Pipeline (.github/workflows/ci.yml)
- Python 3.10, 3.11, 3.12, 3.13
- Steps: ruff check, pytest
## AIPASS_REGISTRY.json
All 10 modules registered: ai_mail, api, cli, devpulse, drone, flow, prax, seedgo, spawn, trigger.
All status: active. All profile: library.
## Claude Config (.claude/)
- settings.json: acceptEdits mode, denies git reset/rebase/force-push
- Hooks: prompt loader, tool sounds, notification sounds, stop sounds
- branch_prompt_loader.py: discovers branch root via .trinity/ or apps/, loads .aipass/ prompts
- Sound files referenced but not present (graceful fallback)
## Notes
- No root-level tests/ (tests live in each module)
- No global aipass_global_prompt.md exists yet (hook would load it if present)
- `your` — empty file at root (cleanup candidate)
@@ -0,0 +1,51 @@
# Seedgo Module Recon
**Date:** 2026-03-06
## Summary
Standards compliance platform with pluggable packs. 20 AIPass standards defined. Path debt: DONE (runtime clean). Two pyproject.toml issues.
## Structure
```
seedgo/
├── apps/
│ ├── seedgo.py # Entry point (pack discovery + routing)
│ ├── modules/
│ │ └── seedgo_verify.py # Self-verification (5 checks)
│ ├── handlers/ # Empty at root (handlers live in packs)
│ └── standards/
│ ├── aipass/ # Main pack (20 standards)
│ │ ├── pack.json # Pack manifest
│ │ ├── pack_entry.py # Pack orchestrator
│ │ ├── modules/ # Audit, verify, list
│ │ ├── handlers/ # Checkers per standard
│ │ └── standards/ # Standard definitions (JSON)
│ ├── app_development.example/
│ └── website_design.example/
├── drone_adapter.py # Drone integration
├── cli.py # CLI entry point
└── tests/
```
## Commands
```
drone @seedgo verify # 5/5 self-checks (WORKING)
drone @seedgo list # Shows installed packs (WORKING)
drone @seedgo audit aipass # Audit repo against standards
```
## 20 AIPass Standards
Architecture, CLI, imports, handlers, modules, documentation, testing, logging, meta headers, error handling, JSON structure, naming, permissions, diagnostics, trigger patterns, and more.
## Path Debt: DONE
- No Path.home() in runtime code
- Shebang in conftest.py: `#!/home/aipass/.venv/bin/python3` (cosmetic)
- Help text references `/home/aipass/standards/...` (display only)
## Critical Issues
1. **pyproject.toml:** `seedgo = "seedgo.cli:main"` points to non-existent module (should be `aipass.seedgo...`)
2. **pyproject.toml:** packages includes `"src/seedgo"` which doesn't exist
## Working
- Pack discovery, module auto-discovery, verify (5/5), list, drone adapter
- Standards documentation (20 defined)
- Bypass rules system (.seed/bypass.json)
@@ -0,0 +1,44 @@
# Spawn Module Recon
**Date:** 2026-03-06
## Summary
Agent creation utility library. Flat structure (not 3-layer — it's infrastructure, not an agent). Well-designed, stable.
## Structure
```
spawn/
├── spawn.py # Main engine (210 lines, 7-step spawn)
├── file_ops.py # Template copy + path manipulation
├── placeholders.py # {{PLACEHOLDER}} replacement
├── metadata.py # Branch name extraction
├── registry.py # AIPASS_REGISTRY.json CRUD
├── __init__.py # Single export: spawn_agent
├── templates/
│ ├── agent.template/ # Base template (full agent skeleton)
│ └── agent_mock_branch/ # Reference spawned agent
└── tests/
└── test_spawn.py # 126 lines, covers full spawn lifecycle
```
## How spawn_agent() Works (7 Steps)
1. Validate target doesn't exist
2. Extract branch name from path
3. Get next citizen number from registry
4. Build placeholder mapping (14 variables)
5. Copy template recursively, replacing placeholders
6. Rename `{{BRANCH}}_*` directories
7. Update AIPASS_REGISTRY.json
## Placeholder Variables
`{{BRANCHNAME}}` (UPPER), `{{branchname}}` (lower), `{{BRANCH}}` (module name), `{{CWD}}`, `{{DATE}}`, `{{MODULE}}`, `{{EMAIL}}`, `{{PROFILE}}`, `{{ROLE}}`, `{{TRAITS}}`, `{{PURPOSE_BRIEF}}`, `{{CITIZEN_NUMBER}}`, `{{KEY_CAPABILITIES}}`, `{{DEPENDS_ON}}`, `{{PROVIDES_TO}}`
## Path Debt
- No Path.home() in spawn code
- Template conftest.py has hardcoded shebang (propagates to all spawned agents)
- Template branch.py has relative import bug (propagates to all spawned agents)
## Notes
- Flat structure is intentional — spawn is a utility, not an autonomous agent
- No .trinity files (by design)
- Has actual tests (only module with test_*.py files)
- Skip list: `__pycache__`, `.git`, `.template_registry.json`, `.gitkeep`
@@ -0,0 +1,63 @@
# Spawn Templates Recon
**Date:** 2026-03-06
## Templates Available
1. `agent.template/` — Base agent skeleton
2. `agent_mock_branch/` — Reference implementation (fully spawned example)
## agent.template Structure
```
agent.template/
├── .agent/ # System metadata
│ ├── .migrations.json # Structural migration rules
│ ├── .backup_ignore.json # Backup exclusion patterns
│ ├── .registry_ignore.json # Template update exclusions
│ └── .template_registry.json # File tracking with SHA hashes
├── .aipass/
│ └── aipass_local_prompt.md # Branch prompt (needs config)
├── .trinity/
│ ├── passport.json # Identity ({{BRANCHNAME}}, {{ROLE}}, etc.)
│ ├── local.json # Session history
│ └── observations.json # Collaboration patterns
├── .archive/.gitkeep
├── .claude/settings.local.json
├── apps/
│ ├── branch.py # Entry point (auto-discovery + routing)
│ ├── modules/__init__.py # Empty (agent builds its own)
│ ├── handlers/__init__.py
│ ├── plugins/__init__.py
│ └── extensions/__init__.py # (not in devpulse)
├── artifacts/
│ └── birth_certificate.json # Citizenship record
├── docs/.gitkeep
├── tests/conftest.py, __init__.py
├── tools/verify_branch.py # Template verification
├── {{BRANCH}}_json/.gitkeep # Renamed on spawn
├── DASHBOARD.local.json
├── flow.local.md
├── README.md
├── pytest.ini
└── .gitignore
```
## DevPulse vs Template Comparison
| Item | Template | DevPulse | Status |
|------|----------|----------|--------|
| .trinity/ | Yes | Yes | Done |
| .agent/ | Yes | Yes | Done |
| .aipass/ | Yes | Yes | Done |
| artifacts/ | Yes | Yes | Done |
| tools/verify_branch.py | Yes | Yes (fixed Path.home) | Done |
| docs/ | Yes | Yes | Done |
| {{BRANCH}}_json/ | Yes | devpulse_json/ | Done |
| .archive/ | Yes | Yes | Done |
| DASHBOARD.local.json | Yes | Yes | Done |
| flow.local.md | Yes | Yes | Done |
| apps/extensions/ | Yes | No | Missing |
| apps/json_templates/ | Yes | No | Missing (optional) |
## Template Issues
- branch.py:35 uses relative import `apps.modules.{stem}` (propagates to all agents)
- conftest.py has hardcoded `/home/aipass/` shebang (propagates)
- modules/ dir is intentionally empty (agents build their own)
@@ -0,0 +1,525 @@
# AIPass System Health Report
Generated: 2026-03-19 22:54
---
## 1. Dead Code (14 unused files)
Dead Code Scanner
Scanning 15 branches
@ai_mail (3 modules, 31 handlers)
x handlers/monitoring/errors.py -- 0 references
> 33/34 files referenced
@api (4 modules, 14 handlers)
x handlers/openrouter/provision.py -- 0 references
> 17/18 files referenced
@backup (4 modules, 24 handlers)
x handlers/diff/vscode_integration.py -- 0 references
> 27/28 files referenced
@cli (3 modules, 2 handlers)
> All 5 files referenced
@commons (22 modules, 35 handlers)
> All 57 files referenced
@daemon (6 modules, 8 handlers)
> All 14 files referenced
@devpulse -- no apps/ directory
@drone (9 modules, 16 handlers)
> All 25 files referenced
@flow (8 modules, 35 handlers)
x handlers/plan/file_ops.py -- 0 references
x handlers/plan/update_registry.py -- 0 references
> 41/43 files referenced
@memory (5 modules, 29 handlers)
x handlers/learnings/manager.py -- 0 references
x handlers/schema/normalize.py -- 0 references
x handlers/search/vector_search.py -- 0 references
> 31/34 files referenced
@prax (6 modules, 36 handlers)
> All 42 files referenced
@seedgo (5 modules, 66 handlers)
x handlers/config/aipass_bypass.py -- 0 references
x handlers/config/aipass_ignore.py -- 0 references
x handlers/diagnostics/python_diognostics.py -- 0 references
x handlers/diagnostics/typscript_diognostics.py -- 0 references
x handlers/file/file_handler.py -- 0 references
x handlers/mock_standard_1/bypass_config/bypass.config.py -- 0 references
> 65/71 files referenced
@skills (5 modules, 8 handlers)
> All 13 files referenced
@spawn (6 modules, 15 handlers)
> All 21 files referenced
@trigger (5 modules, 17 handlers)
> All 22 files referenced
TOTAL: 14 unused across 14 branches
---
## 2. Local Prompts (5 stubs need enrichment)
Local Prompt Status
==================================================
RICH (50+ lines, 4+ sections):
v daemon 56 lines 4 sections
v devpulse 79 lines 8 sections
v flow 67 lines 6 sections
v seedgo 56 lines 4 sections
v skills 58 lines 5 sections
v trigger 53 lines 7 sections
BASIC (15-49 lines):
~ api 15 lines 2 sections
~ commons 48 lines 6 sections
~ memory 37 lines 5 sections
~ spawn 20 lines 4 sections
STUB (<15 lines):
x ai_mail 14 lines 1 section
x backup 14 lines 1 section
x cli 14 lines 1 section
x drone 14 lines 1 section
x prax 14 lines 1 section
==================================================
SUMMARY: 6 rich, 4 basic, 5 stub
==================================================
Section Breakdown
==================================================
@ai_mail (14 lines, STUB)
v Status
470 bytes
@api (15 lines, BASIC)
v Identity
v Key Breadcrumbs
1192 bytes
@backup (14 lines, STUB)
v Status
499 bytes
@cli (14 lines, STUB)
v Status
499 bytes
@commons (48 lines, BASIC)
v Commands
v Architecture
v Integration Points
v Critical Files
v Role
v Key Details
2328 bytes
@daemon (56 lines, RICH)
v Commands
v Memory & Tracking
v Apps Layout
v Known Issues
2788 bytes
@devpulse (79 lines, RICH)
v Identity
v How You Work
v Dispatch Table
v Commands
v Branches
v Working Habits
v Autonomous Monitoring
v Memory & Tracking
v Has dispatch table (@branch refs)
4710 bytes
@drone (14 lines, STUB)
v Status
503 bytes
@flow (67 lines, RICH)
v Commands
v Architecture
v Integration Points
v Conventions
v Critical Files
v Plan Type System
3461 bytes
@memory (37 lines, BASIC)
v Identity
v Commands
v Architecture
v Memory & Tracking
v Known Issues
1591 bytes
@prax (14 lines, STUB)
v Status
501 bytes
@seedgo (56 lines, RICH)
v Commands
v Apps Layout (extra layer vs standard branch)
v How I Work — Standards Reasoning
v Quick Reference
3166 bytes
@skills (58 lines, RICH)
v Commands
v Memory & Tracking
v Apps Layout
v Search Paths (first match wins)
v Three Skill Tiers
2706 bytes
@spawn (20 lines, BASIC)
v Commands
v Architecture
v Role
v Principles
745 bytes
@trigger (53 lines, RICH)
v Dispatch Table
v Commands
v Architecture
v Integration Points
v Critical Files
v Role
v Rules
2610 bytes
---
## 3. Commands (173 discovered)
@ai_mail (14 commands)
- close
- contacts
- dispatch
- email
- inbox
- ping
- read
- registry
- reply
- send
- sent
- status
- thresholds
- view
@api (12 commands)
- call
- cleanup
- google
- init
- models
- reauth
- session
- stats
- status
- test
- track
- validate
@backup (1 command)
- reauth
@cli (5 commands)
- aipass
- demo
- display
- show
- templates
@commons (51 commands)
- activity
- artifacts
- capsule
- capsules
- catchup
- collab
- comment
- craft
- database
- decorate
- delete
- digest
- drop
- enter
- event
- explore
- feed
- find
- gift
- inspect
- leaderboard
- leaderboards
- log
- look
- mint
- mute
- open
- pin
- pinned
- post
- preferences
- profile
- prompt
- react
- reactions
- room
- search
- secrets
- sign
- thread
- track
- trade
- trending
- unpin
- unreact
- visitors
- vote
- watch
- welcome
- who
- whoami
@daemon (5 commands)
- actions
- activity
- activity_report
- schedule
- update
@drone (24 commands)
- activate
- add
- branches
- check
- exists
- info
- list
- load
- lock
- lookup
- path
- pr
- remove
- reset
- resolve
- route
- route_all
- scan
- set
- status
- sync
- system
- systems
- unlock
@flow (11 commands)
- aggregate
- close
- create
- list
- post_close
- register
- registry
- restore
- scan
- templates
- unregister
@memory (13 commands)
- analyze
- bootstrap
- check
- demo
- extract
- fragments
- rollover
- search
- status
- symbolic
- templates
- verify
- watch
@prax (4 commands)
- dashboard
- log-audit
- monitor
- status
@seedgo (8 commands)
- audit
- checklist
- diagnostics
- diagnostics_audit
- readme
- readme_update
- standards_audit
- standards_query
@skills (6 commands)
- create
- discover
- info
- list
- run
- validate
@spawn (4 commands)
- create
- delete
- passport
- update
@trigger (15 commands)
- branch_log_events
- core
- errors
- fire
- list
- log_events
- medic
- mute
- off
- on
- reset
- start
- status
- stop
- unmute
DISCOVERED: 173 commands across 14 branches
---
## 4. Test Coverage (26% module coverage)
@ai_mail (49 tests, 2 files)
tests/test_send_identity.py -- 36 tests -> email, users
tests/test_user_paths.py -- 13 tests -> users
UNTESTED: branch_ping, central_writer, dispatch, json, json_utils, monitoring, notify, registry
@api (0 tests, 0 files)
(no test files found)
@backup (0 tests, 1 file)
tests/test_pattern_scan.py -- 0 tests -> config, operations
UNTESTED: backup_core, config, diff, google_drive_sync, integrations, json, models, operations, reauth_drive, reporting, utils
@cli (0 tests, 0 files)
(no test files found)
@commons (82 tests, 2 files)
tests/test_commons.py -- 72 tests -> curation, database, notifications, profiles, search, welcome
tests/test_lifecycle.py -- 10 tests -> database, search
UNTESTED: activity, artifact, artifacts, capsule, catchup, central, comment, comments, commons_identity, dashboard, digest, engagement, explore, feed, identity, json, leaderboard, notification, post, posts, profile, reaction, room, rooms, social, space, trade
@daemon (31 tests, 1 file)
tests/test_actions_registry.py -- 31 tests -> actions
UNTESTED: activity_report, json, monitoring, schedule, scheduler_ops, update, wakeup_ops
@devpulse (0 tests, 0 files)
(no test files found)
@drone (335 tests, 9 files)
tests/test_activation.py -- 38 tests -> command_registry, executor
tests/test_commands.py -- 35 tests -> command_registry, commands
tests/test_discovery.py -- 40 tests -> discovery, discovery_handler, exceptions, module_registry_handler
tests/test_executor.py -- 26 tests -> exceptions, executor
tests/test_git_module.py -- 46 tests -> git, git_module, module_registry_handler
tests/test_registry_handler.py -- 33 tests -> exceptions, registry_handler
tests/test_resolver.py -- 52 tests -> exceptions, registry_handler, resolver
tests/test_router.py -- 37 tests -> exceptions, executor, router, router_handler
tests/test_scan.py -- 28 tests -> scan, scanning
UNTESTED: config, json, module_registry, registry
@flow (0 tests, 0 files)
(no test files found)
@memory (0 tests, 0 files)
(no test files found)
@prax (0 tests, 0 files)
(no test files found)
@seedgo (0 tests, 0 files)
(no test files found)
@skills (123 tests, 8 files)
tests/test_cli_routing.py -- 20 tests -> ?
tests/test_discovery.py -- 32 tests -> discovery_handler
tests/test_lifecycle.py -- 12 tests -> creator, discovery, loader_handler, runner, template
tests/test_loader.py -- 7 tests -> loader
tests/test_registry.py -- 14 tests -> registry
tests/test_runner.py -- 13 tests -> runner
tests/test_runner_handler.py -- 14 tests -> runner_handler
tests/test_validator.py -- 11 tests -> validator
UNTESTED: creator_handler, json
@spawn (113 tests, 5 files)
tests/test_citizen_classes.py -- 28 tests -> class_registry, core, passport_ops, update, update_ops
tests/test_handlers.py -- 36 tests -> change_detection, json_ops, meta_ops, reconcile
tests/test_lifecycle.py -- 22 tests -> delete, delete_ops, sync_registry, sync_registry_ops, sync_templates, sync_templates_ops
tests/test_spawn.py -- 13 tests -> metadata, placeholders, registry
tests/test_update.py -- 14 tests -> meta_ops, update, update_ops
UNTESTED: file_ops, json, passport
@trigger (0 tests, 0 files)
(no test files found)
Test Coverage Report
═══════════════════════════════════════════════════════
TESTED:
✓ drone 335 tests 9 files 15/19 modules covered (79%)
✓ skills 123 tests 8 files 10/12 modules covered (83%)
✓ spawn 113 tests 5 files 18/21 modules covered (86%)
PARTIAL:
◐ ai_mail 49 tests 2 files 2/10 modules covered (20%)
◐ commons 82 tests 2 files 6/33 modules covered (18%)
◐ daemon 31 tests 1 file 1/8 modules covered (12%)
NO TESTS:
✗ api 0 tests 0 files 0/10 modules covered (0%)
✗ backup 0 tests 1 file 0/11 modules covered (0%)
✗ cli 0 tests 0 files 0/5 modules covered (0%)
✗ devpulse 0 tests 0 files 0/0 modules covered (0%)
✗ flow 0 tests 0 files 0/16 modules covered (0%)
✗ memory 0 tests 0 files 0/16 modules covered (0%)
✗ prax 0 tests 0 files 0/14 modules covered (0%)
✗ seedgo 0 tests 0 files 0/14 modules covered (0%)
✗ trigger 0 tests 0 files 0/12 modules covered (0%)
═══════════════════════════════════════════════════════
SUMMARY: 733 tests across 15 branches
3 branches tested, 3 partial, 9 untested
Coverage: 52/201 modules (26%)
@@ -0,0 +1,58 @@
# Trigger Module Recon
**Date:** 2026-03-06
## Summary
Event orchestration hub with error management. 34 Python files. Operational but 1 hardcoded path + many Path.home() hits.
## Structure
```
trigger/
├── apps/
│ ├── trigger.py # Entry point (auto-discovery)
│ ├── config.py # TRIGGER_ROOT, AIPASS_PKG_ROOT
│ ├── modules/
│ │ ├── core.py # Event bus (Trigger class: fire/on/off)
│ │ ├── errors.py # Error registry management
│ │ ├── medic.py # Medic toggle (on/off/mute/unmute)
│ │ ├── branch_log_events.py # Branch log watcher
│ │ └── log_events.py # System log watcher
│ ├── handlers/
│ │ ├── error_registry.py # Fingerprinting, circuit breaker, rate limiting
│ │ ├── medic_state.py # Medic config persistence
│ │ ├── log_watcher.py # Log monitoring
│ │ ├── watchers/ # Watchdog file monitoring
│ │ ├── events/ # 12 event handlers
│ │ │ ├── registry.py # Central handler registration
│ │ │ ├── error_detected.py # Medic v2 error dispatch
│ │ │ ├── error_logged.py # DEPRECATED legacy handler
│ │ │ └── startup, cli, memory, plan_file, bulletin, warning
│ │ └── json/
├── trigger_json/
│ └── trigger_data.json # Error catchup state (MODIFIED, unstaged)
└── tests/ # Empty
```
## Key Features
- **Event Bus:** Deferred queue prevents recursion during nested events
- **Medic v2:** Circuit breaker (closed/open/half_open) + exponential backoff per error fingerprint
- **Error Registry:** SHA1 fingerprinting, dedup, rate limiting, dispatch gating
- **Auto-healing:** Dispatches fix-it emails via AI_Mail when errors detected
## Commands
```
drone @trigger errors list|detail|suppress|resolve|stats|circuit-breaker
drone @trigger medic on|off|status|mute|unmute
```
## Path Debt
- **CRITICAL:** `plan_file.py:42` — `ECOSYSTEM_ROOT = Path("/home/aipass")` (hardcoded)
- **HIGH:** 8+ event handlers use `AIPASS_HOME = Path.home()` at module level
- Shebang debt across all files
## Modified File
`trigger_data.json` — error catchup state with 7 processed hashes, indicates active error processing from Dev-Pass side
## Notes
- Inotify exhaustion issue documented and resolved (lazy-start disabled)
- error_logged.py DEPRECATED but kept for backward compat
- Handlers can't import Prax logger directly (causes event recursion) — use get_direct_logger()
@@ -0,0 +1,37 @@
# Trinity Census — AIPass Agent Registry
**Date:** 2026-03-06 | **Surveyed by:** DevPulse sub-agent
## Summary
**1 of 10 modules** has .trinity files (is "alive" as an agent).
## Active Agents
### DEVPULSE (alias: "dapfels")
- **Status:** Active
- **Location:** `src/aipass/devpulse/`
- **Module:** `aipass.devpulse`
- **Role:** orchestration_hub
- **Created:** 2026-03-06
- **Trinity files:** passport.json, local.json, observations.json — all present and healthy
- **DASHBOARD.local.json:** present
- **artifacts/birth_certificate.json:** present (ID: DEVPULSE-001)
- **Session count:** 1 (active)
## Modules Without .trinity (Not Yet "Born")
| Module | Location | Notes |
|--------|----------|-------|
| drone | `src/aipass/drone/` | No .trinity |
| seedgo | `src/aipass/seedgo/` | No .trinity |
| prax | `src/aipass/prax/` | No .trinity |
| cli | `src/aipass/cli/` | No .trinity |
| flow | `src/aipass/flow/` | No .trinity |
| ai_mail | `src/aipass/ai_mail/` | No .trinity |
| api | `src/aipass/api/` | No .trinity |
| trigger | `src/aipass/trigger/` | No .trinity |
| spawn | `src/aipass/spawn/` | No .trinity (but has templates for creating them) |
## Notes
- The spawn module has `agent.template/` with .trinity scaffolds ready for new agents
- Also has `agent_mock_branch/` with example trinity files
- On the Dev-Pass side, all 30+ agents are alive with full .trinity — this repo just needs them initialized