Skip to content

feat(archon): add persona service and API routes for agent creation#2

Closed
POWERFULMOVES wants to merge 11 commits intoPMOVES.AI-Edition-Hardenedfrom
feat/personas-first-architecture
Closed

feat(archon): add persona service and API routes for agent creation#2
POWERFULMOVES wants to merge 11 commits intoPMOVES.AI-Edition-Hardenedfrom
feat/personas-first-architecture

Conversation

@POWERFULMOVES
Copy link
Copy Markdown
Owner

@POWERFULMOVES POWERFULMOVES commented Jan 15, 2026

Summary

Add persona management service and API routes for Archon, integrating with Agent Zero's persona-based agent creation.

Features

  • PersonaService with Supabase integration for persona fetching
  • build_system_prompt() for enhanced prompt generation with Archon templates
  • create_agent_with_persona() for Agent Zero integration

API Endpoints

  • GET /api/personas - List all personas (with active_only filter)
  • GET /api/personas/{id} - Get persona details
  • POST /api/personas/agent/create - Create agent from persona
  • GET /api/personas/thread-types - Get thread type reference

Integration

  • Service discovery for Agent Zero endpoint resolution
  • Archon prompt template enhancement
  • Form-based behavior weight overrides
  • Tuple return pattern matching Archon service conventions

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Persona API: list personas, view persona details, and create agents from personas
    • Prometheus metrics endpoint for monitoring and request metrics
    • Claude Code MCP adapter for command/agent integrations
  • Dependencies

    • prometheus-client added for metrics
  • Chores

    • Dependabot configured for weekly updates
    • Repository CODEOWNERS added
    • Multiple submodules added/updated and pointers refreshed

PMOVES.AI and others added 4 commits December 7, 2025 14:26
New module: python/pmoves_mcp/
- claude_code_adapter.py: Async MCP adapter for Claude Code CLI
- __init__.py: Module exports

Features:
- Execute TAC slash commands via Agent Zero's MCP interface
- ClaudeCodeMCPAdapter with async httpx client
- CommandResult dataclass for structured responses
- ARCHON_MCP_TOOLS registration for Archon integration

Available commands through adapter:
- /search:hirag, /search:supaserch, /search:deepresearch
- /health:check-all, /health:metrics
- /agents:status, /agents:mcp-query
- /deploy:smoke-test, /deploy:services, /deploy:up
- /botz:init, /botz:profile, /botz:mcp, /botz:secrets

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ation

* chore(security): add CODEOWNERS and Dependabot configuration

Adds repository security files:
- CODEOWNERS for PR review routing
- dependabot.yml for automated security updates

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(hardened): Add nested submodule integrations for standalone operation

- Add .gitmodules with 7 nested integrations:
  - PMOVES-Agent-Zero (agent orchestration)
  - PMOVES-BoTZ (MCP tools)
  - PMOVES-HiRAG (knowledge retrieval)
  - PMOVES-Deep-Serch (deep research)
  - docling (document processing)
  - PMOVES-BotZ-gateway (MCP gateway)
  - PMOVES-tensorzero (TensorZero client)

- Fix PydanticAI Agent initialization (remove invalid result_type parameter)

Enables Archon to run standalone with PMOVES.AI service connections.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(pmoves): add Claude Code MCP adapter for PMOVES.AI integration

New module: python/pmoves_mcp/
- claude_code_adapter.py: Async MCP adapter for Claude Code CLI
- __init__.py: Module exports

Features:
- Execute TAC slash commands via Agent Zero's MCP interface
- ClaudeCodeMCPAdapter with async httpx client
- CommandResult dataclass for structured responses
- ARCHON_MCP_TOOLS registration for Archon integration

Available commands through adapter:
- /search:hirag, /search:supaserch, /search:deepresearch
- /health:check-all, /health:metrics
- /agents:status, /agents:mcp-query
- /deploy:smoke-test, /deploy:services, /deploy:up
- /botz:init, /botz:profile, /botz:mcp, /botz:secrets

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: Restore fail-fast behavior for API key validation in upload

- Remove HTTPException catch that was allowing uploads to proceed with invalid credentials
- Aligns with beta guidelines: authentication failures should halt execution
- Addresses code review feedback from PR #1

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: POWERFULMOVES <POWERFULMOVES@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: PMOVES.AI <claude@pmoves.ai>
Add persona management service and API routes for Archon, integrating
with Agent Zero's persona-based agent creation.

Features:
- PersonaService with Supabase integration for persona fetching
- build_system_prompt() for enhanced prompt generation with Archon templates
- create_agent_with_persona() for Agent Zero integration
- GET /api/personas - List all personas (with active_only filter)
- GET /api/personas/{id} - Get persona details
- POST /api/personas/agent/create - Create agent from persona
- GET /api/personas/thread-types - Get thread type reference

Integration:
- Service discovery for Agent Zero endpoint resolution
- Archon prompt template enhancement
- Form-based behavior weight overrides
- Tuple return pattern matching Archon service conventions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 15, 2026

📝 Walkthrough

Walkthrough

Adds repository metadata and Dependabot config, updates gitsubmodules, introduces a Claude Code MCP adapter package, adds a PersonaService and FastAPI persona endpoints, instruments Prometheus metrics and /metrics, and makes small agent/API adjustments.

Changes

Cohort / File(s) Summary
Repository metadata & automation
\.github/CODEOWNERS, \.github/dependabot.yml, \.gitmodules
Adds CODEOWNERS, adds Dependabot weekly schedules, and declares/configures multiple git submodules.
Submodule pointers
external/PMOVES-Agent-Zero, external/PMOVES-BoTZ, external/PMOVES-Deep-Serch, external/PMOVES-HiRAG, pmoves_multi_agent_pro_pack/...
Adds/updates gitlink commit references for several submodules (metadata-only).
MCP adapter package
python/pmoves_mcp/__init__.py, python/pmoves_mcp/claude_code_adapter.py
New package exposing ClaudeCodeMCPAdapter, CommandResult, ARCHON_MCP_TOOLS, and create_adapter; async HTTP client, command invocation, helpers, and error handling.
Persona service & API
python/src/server/services/persona_service.py, python/src/server/api_routes/persona_api.py
Adds PersonaService (models, Supabase-backed list/get, prompt building, behavior-weight adjustments, Agent Zero agent creation) and FastAPI persona endpoints with request/response models and error handling.
Server metrics & integration
python/src/server/main.py, python/pyproject.toml
Introduces Prometheus metrics (Counters/Histogram), registers persona router, exposes /metrics, and adds prometheus-client dependency.
Agent & minor API edits
python/src/agents/document_agent.py, python/src/server/api_routes/knowledge_api.py
Removes result_type=DocumentOperation from Agent instantiation; minor comment/whitespace edits in knowledge API.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant API as Persona API
    participant Service as PersonaService
    participant DB as Supabase
    participant AgentZero as Agent Zero

    Client->>API: POST /api/personas/agent/create (persona_id, form, overrides)
    API->>Service: create_agent_with_persona(persona_id, form, overrides, agent_name)
    Service->>DB: SELECT persona by id
    DB-->>Service: persona data
    Service->>Service: build_system_prompt(persona, form, overrides)
    Service->>DB: SELECT archon prompt enhancements
    DB-->>Service: enhancements
    Service->>AgentZero: POST /api/persona/agent/create (system_prompt, config)
    AgentZero-->>Service: {agent_id, status}
    Service-->>API: AgentCreateResponse
    API-->>Client: 200 + AgentCreateResponse
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐇 I hopped through manifests, prompts, and queues,
Wired metrics to count our curious hues,
A Claude adapter ready, nimble and bright,
Personas stitched for every midnight flight,
Submodules in line — let the agents take flight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description covers key features and API endpoints but lacks required sections: Type of Change, Testing, Checklist, and Test Evidence are missing from the template. Add missing template sections: mark Type of Change (New feature), confirm Testing status, complete Checklist items, and provide test commands or evidence that changes were tested.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding persona service and API routes for agent creation, which aligns with the primary objective of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 93.33% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/personas-first-architecture

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In @.github/dependabot.yml:
- Around line 1-20: The dependabot config currently points all package-ecosystem
entries to directory "/" which won't find manifests; update the entries so
"package-ecosystem: pip" uses directory "/python", add two "package-ecosystem:
npm" entries with directories "/archon-ui-main" and "/docs" (instead of just one
at "/"), and add three "package-ecosystem: docker" entries with directories
"/archon-ui-main", "/python", and "/docs"; keep the existing "package-ecosystem:
github-actions" entry at directory "/".

In `@external/PMOVES-Agent-Zero`:
- Line 1: The Agent Zero submodule is added but never used; implement
integration by invoking create_agent_with_persona() from the application
startup/agent factory code (e.g., the function that initializes agents or
services) and wiring its returned agent into the existing agent orchestration
flow; additionally implement service discovery for Agent Zero endpoint
resolution (resolve via environment variable, config file, DNS SRV or the
existing service-discovery utility used elsewhere) and add error handling/retry
and unit tests and docs for the new integration points so the submodule is
actually exercised at runtime.

In `@external/PMOVES-BoTZ`:
- Line 1: The submodule PMOVES-BoTZ is pinned to a non-existent commit
(b39e3b4bf3974296e1d78f469ff1d0ae19ed551b) on the expected branch
PMOVES.AI-Edition-Hardened; update the submodule pointer to a valid commit on
that branch (for example the current branch HEAD
2e98d587cc19b58945f578dc286c205620f4c1b2) or correct the commit hash, then
update the repository’s submodule reference (PMOVES-BoTZ) so
.gitmodules/.gitlinks and the submodule working tree point to the valid commit
and commit that change in this repo.

In `@pmoves_multi_agent_pro_pack/docling`:
- Line 1: The .gitmodules entry for the submodule named
"pmoves_multi_agent_pro_pack/docling" is missing the branch specification;
update the .gitmodules entry for submodule "pmoves_multi_agent_pro_pack/docling"
to include branch = PMOVES.AI-Edition-Hardened so it matches other submodules,
and commit that change; then scan the codebase for any references (imports,
package/dependency entries, config keys) to "docling" or
pmoves_multi_agent_pro_pack/docling to confirm whether the submodule is actually
used—if no integration exists, either document that docling is planned for
future use in the PR description or remove the submodule addition to avoid
premature inclusion.

In `@python/pyproject.toml`:
- Around line 67-68: Add prometheus-client>=0.20.0 to the dependency-groups.all
entry in pyproject.toml so CI and local installs include the package used by
src/server/main.py; locate the dependency-groups section and append
"prometheus-client>=0.20.0" to the all group (matching the existing string-list
format) so imports of prometheus_client no longer fail.

In `@python/src/server/services/persona_service.py`:
- Around line 108-148: get_persona is declared async but calls the synchronous
Supabase client (self.supabase_client.table(...).select(...).eq(...).execute()),
which blocks the event loop; fix by running the blocking DB call inside
asyncio.to_thread (wrap the sequence that performs .execute() and builds
persona_data/Persona in a to_thread call) or alternatively remove the
async/await from get_persona if callers expect a regular function—update
references to get_persona accordingly; ensure exceptions and logging
(logger.error, logger.info, logger.warning) remain inside the executed/threaded
context or are handled after awaiting the to_thread result.
🧹 Nitpick comments (11)
.github/CODEOWNERS (2)

6-6: Expand Dockerfile pattern to include subdirectories.

The current pattern only matches Dockerfile at the repository root. Consider using a wildcard pattern to ensure all Dockerfiles (including those in subdirectories like services/api/Dockerfile or variants like Dockerfile.prod) are owned by the infra team.

🔧 Suggested pattern improvement
-Dockerfile `@POWERFULMOVES/pmoves-infra`
+*Dockerfile* `@POWERFULMOVES/pmoves-infra`

This matches any file containing "Dockerfile" anywhere in the path, ensuring comprehensive infrastructure team ownership.


2-7: Consider adding patterns for configuration and CI/CD files.

While the current ownership rules cover the main code types, you might want to add patterns for other important file types to ensure comprehensive code review coverage.

📋 Optional additional patterns
 * `@POWERFULMOVES/pmoves-core`
 *.py `@POWERFULMOVES/pmoves-python`
 *.ts `@POWERFULMOVES/pmoves-frontend`
 *.tsx `@POWERFULMOVES/pmoves-frontend`
+*.yml `@POWERFULMOVES/pmoves-infra`
+*.yaml `@POWERFULMOVES/pmoves-infra`
+.github/workflows/* `@POWERFULMOVES/pmoves-infra`
+package*.json `@POWERFULMOVES/pmoves-frontend`
+*.md `@POWERFULMOVES/pmoves-core`
 *Dockerfile* `@POWERFULMOVES/pmoves-infra`
 docker-compose*.yml `@POWERFULMOVES/pmoves-infra`

This adds coverage for configuration files, CI/CD workflows, package manifests, and documentation.

.gitmodules (1)

29-41: Add explicit branch specifications for Pro Pack submodules for consistency and clarity.

The Pro Pack submodules default to tracking the remote repositories' main branch, while external submodules explicitly specify branch = PMOVES.AI-Edition-Hardened. This inconsistency makes the intent less clear.

Verification shows that only PMOVES-BotZ-gateway maintains a PMOVES.AI-Edition-Hardened branch; the other two repositories (docling and PMOVES-tensorzero) do not have this branch, confirming that Pro Pack submodules intentionally track default branches. To improve maintainability and make this design choice explicit, consider adding branch = main (or the appropriate default branch) to the Pro Pack submodule entries for consistency with the pattern used in external submodules.

python/pmoves_mcp/claude_code_adapter.py (3)

41-52: Consider adding async context manager support for cleaner resource management.

The adapter creates an HTTP client that requires explicit cleanup via close(). Adding __aenter__/__aexit__ would enable async with usage and ensure the client is properly closed.

♻️ Suggested improvement
+    async def __aenter__(self) -> "ClaudeCodeMCPAdapter":
+        """Enter async context."""
+        return self
+
+    async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
+        """Exit async context and close client."""
+        await self.close()
+
     async def close(self):
         """Close the HTTP client."""
         if self._client:
             await self._client.aclose()
             self._client = None

95-106: HTTP error handling could preserve more diagnostic information.

The HTTPStatusError handler only captures the status code. Including the response body (truncated) would aid debugging when Agent Zero returns error details.

♻️ Suggested improvement
         except httpx.HTTPStatusError as e:
+            # Truncate response body for safety
+            error_body = e.response.text[:200] if e.response.text else ""
             return CommandResult(
                 success=False,
                 output=None,
-                error=f"HTTP error: {e.response.status_code}"
+                error=f"HTTP error: {e.response.status_code} - {error_body}"
             )

108-143: Silent exception swallowing hinders debugging.

Both list_available_commands and get_command_help silently catch and discard all exceptions. Per coding guidelines, errors should be logged with context even when returning fallback values.

♻️ Suggested improvement
+import logging
+
+logger = logging.getLogger(__name__)
+
     async def list_available_commands(self) -> List[str]:
         """List all available Claude Code slash commands."""
         payload = {
             "instrument": "claude_code",
             "action": "list_commands"
         }

         try:
             response = await self.client.post(
                 f"{self.agent_zero_url}/mcp/execute",
                 json=payload
             )
             response.raise_for_status()
             data = response.json()
             return data.get("commands", [])
-        except Exception:
+        except Exception as e:
+            logger.warning(f"Failed to list available commands: {e}")
             return []

     async def get_command_help(self, command: str) -> Optional[str]:
         """Get help text for a specific command."""
         payload = {
             "instrument": "claude_code",
             "action": "get_command_help",
             "params": {"command": command}
         }

         try:
             response = await self.client.post(
                 f"{self.agent_zero_url}/mcp/execute",
                 json=payload
             )
             response.raise_for_status()
             data = response.json()
             return data.get("help")
-        except Exception:
+        except Exception as e:
+            logger.warning(f"Failed to get command help for '{command}': {e}")
             return None
python/src/server/api_routes/persona_api.py (4)

20-26: Unused import: PersonaCreateRequest.

PersonaCreateRequest is imported from persona_service but AgentCreateRequest (defined locally at line 50) is used instead. Consider removing the unused import or using the service-defined model for consistency.

♻️ Remove unused import
 from ..services.persona_service import (
     AgentCreateResponse,
     Persona,
-    PersonaCreateRequest,
     PersonaService,
     get_persona_service,
 )

50-56: AgentCreateRequest duplicates PersonaCreateRequest from service layer.

This model is nearly identical to PersonaCreateRequest in persona_service.py. Consider reusing the service model to reduce duplication and ensure consistency.


269-272: Consider returning 404 when persona is not found.

The endpoint always returns 500 on failure. When the service returns an error indicating the persona wasn't found, a 404 response would be more semantically correct and help clients distinguish between "bad input" and "server error".

♻️ Suggested improvement
         if not success:
             error_msg = result.get("error", "Failed to create agent")
             logger.error(f"Failed to create agent from persona {request.persona_id}: {error_msg}")
-            raise HTTPException(status_code=500, detail=error_msg)
+            # Return 404 if persona not found, 500 for other errors
+            if "not found" in error_msg.lower():
+                raise HTTPException(status_code=404, detail=error_msg)
+            raise HTTPException(status_code=500, detail=error_msg)

329-356: LGTM! Static reference data is appropriate for initial implementation.

The TODO at line 329 correctly notes future integration with Agent Zero's thread type API. The static data provides a working reference endpoint.

Would you like me to open an issue to track the Agent Zero thread type API integration mentioned in the TODO?

python/src/server/main.py (1)

20-20: Prometheus metrics wiring looks solid; confirm deployment mode

If you deploy with multiple worker processes, prometheus_client needs multiprocess setup (or a single worker) to avoid incomplete or duplicated metrics. Please verify your deployment configuration aligns with this.

Also applies to: 69-90, 289-293

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 55343d2 and d14166c.

📒 Files selected for processing (18)
  • .github/CODEOWNERS
  • .github/dependabot.yml
  • .gitmodules
  • external/PMOVES-Agent-Zero
  • external/PMOVES-BoTZ
  • external/PMOVES-Deep-Serch
  • external/PMOVES-HiRAG
  • pmoves_multi_agent_pro_pack/PMOVES-BotZ-gateway
  • pmoves_multi_agent_pro_pack/PMOVES-tensorzero
  • pmoves_multi_agent_pro_pack/docling
  • python/pmoves_mcp/__init__.py
  • python/pmoves_mcp/claude_code_adapter.py
  • python/pyproject.toml
  • python/src/agents/document_agent.py
  • python/src/server/api_routes/knowledge_api.py
  • python/src/server/api_routes/persona_api.py
  • python/src/server/main.py
  • python/src/server/services/persona_service.py
💤 Files with no reviewable changes (1)
  • python/src/agents/document_agent.py
🧰 Additional context used
📓 Path-based instructions (6)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Never silently accept bad data — use Pydantic to raise validation errors for data corruption

Files:

  • python/pmoves_mcp/__init__.py
  • python/src/server/api_routes/knowledge_api.py
  • python/pmoves_mcp/claude_code_adapter.py
  • python/src/server/main.py
  • python/src/server/services/persona_service.py
  • python/src/server/api_routes/persona_api.py
python/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

python/**/*.py: Python code should use 120 character line length
Use Ruff for linting Python code — checks for errors, warnings, unused imports
Use Mypy for type checking Python code to ensure type safety

Files:

  • python/pmoves_mcp/__init__.py
  • python/src/server/api_routes/knowledge_api.py
  • python/pmoves_mcp/claude_code_adapter.py
  • python/src/server/main.py
  • python/src/server/services/persona_service.py
  • python/src/server/api_routes/persona_api.py
python/src/server/api_routes/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

API routes should follow the pattern: API Route → Service → Database (Service Layer Pattern)

Use custom exceptions from python/src/server/exceptions.py for error handling in API routes and services

Files:

  • python/src/server/api_routes/knowledge_api.py
  • python/src/server/api_routes/persona_api.py
python/src/server/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Repository configuration (GITHUB_REPO_OWNER, GITHUB_REPO_NAME) is centralized in python/src/server/config/version.py

python/src/server/**/*.py: Service startup failures, missing configuration, database connection failures, authentication/authorization failures, data corruption, and critical dependencies unavailable should stop execution and bubble up immediately with clear errors
For batch processing, background tasks, WebSocket events, and optional features: complete what you can and report detailed failures, but never skip failed items by storing corrupted data
Implement service layer pattern with structure: API Route → Service → Database
Use async/await patterns for asynchronous operations in Python backend instead of callback-based approaches
For API routes, follow the pattern: create route handler in api_routes/, add service logic in services/, and include router in main.py

Files:

  • python/src/server/api_routes/knowledge_api.py
  • python/src/server/main.py
  • python/src/server/services/persona_service.py
  • python/src/server/api_routes/persona_api.py
python/src/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

python/src/**/*.py: Include context about what was being attempted in error messages, preserve full stack traces with exc_info=True in Python logging, use specific exception types, include relevant IDs/URLs, and never return None/null to indicate failure
Use Python 3.12 with 120 character line length in backend code
Use Ruff linter for Python backend code to check for errors, warnings, and unused imports
Use Mypy for type checking in Python backend to ensure type safety

Files:

  • python/src/server/api_routes/knowledge_api.py
  • python/src/server/main.py
  • python/src/server/services/persona_service.py
  • python/src/server/api_routes/persona_api.py
python/src/server/services/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use custom exceptions from python/src/server/exceptions.py and register exception handlers in main.py

Files:

  • python/src/server/services/persona_service.py
🧠 Learnings (1)
📚 Learning: 2026-01-14T00:30:34.900Z
Learnt from: CR
Repo: POWERFULMOVES/PMOVES-Archon PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-14T00:30:34.900Z
Learning: Applies to python/src/server/**/*.py : For API routes, follow the pattern: create route handler in api_routes/, add service logic in services/, and include router in main.py

Applied to files:

  • python/src/server/main.py
🧬 Code graph analysis (3)
python/pmoves_mcp/__init__.py (1)
python/pmoves_mcp/claude_code_adapter.py (3)
  • ClaudeCodeMCPAdapter (29-169)
  • CommandResult (20-26)
  • create_adapter (259-265)
python/src/server/api_routes/knowledge_api.py (1)
python/src/server/services/credential_service.py (1)
  • get_active_provider (426-501)
python/src/server/api_routes/persona_api.py (1)
python/src/server/services/persona_service.py (7)
  • AgentCreateResponse (67-74)
  • Persona (31-55)
  • PersonaService (81-418)
  • get_persona_service (428-438)
  • list_personas (150-190)
  • get_persona (108-148)
  • create_agent_with_persona (192-295)
🔇 Additional comments (24)
.github/CODEOWNERS (1)

1-7: LGTM! Well-structured code ownership assignments.

The CODEOWNERS file is properly formatted with clear team assignments for different file types. The global default owner and language-specific patterns follow GitHub CODEOWNERS conventions.

pmoves_multi_agent_pro_pack/PMOVES-tensorzero (1)

1-1: Verify submodule compatibility before merging.

The PMOVES-tensorzero submodule pointer has been updated to commit dcc5c5226a05f2faaaf2b1c32a71f5313a099629. Ensure that:

  • The changes in this commit are compatible with the main project
  • All tests pass with the updated submodule
  • No breaking changes or security issues were introduced

Since the submodule is not initialized in this environment, clone it or access the external repository at https://github.com/POWERFULMOVES/PMOVES-tensorzero.git to review the actual changes in that commit.

pmoves_multi_agent_pro_pack/PMOVES-BotZ-gateway (1)

1-1: The submodule pointer is valid. Commit b0148562876389441f12c728d471683e567dee92 exists in the remote repository and is the current HEAD of both the main and PMOVES.AI-Edition-Hardened branches in the PMOVES-BotZ-gateway repository, confirming compatibility and stability.

external/PMOVES-Deep-Serch (1)

1-1: Deep-Serch submodule is added but not utilized in the codebase.

The submodule configuration is properly set up with the correct branch (PMOVES.AI-Edition-Hardened) and commit hash (41fa1d8cad4ea8a778eb1c536d48f7e8e91d2552). However, a search of the entire codebase reveals zero references to Deep-Serch—no imports, no function calls, and no integration with the persona service or other functionality.

Clarify whether Deep-Serch is needed for this PR or if it should be removed. If it is needed, integrate it into the codebase as described in the PR objectives.

external/PMOVES-HiRAG (1)

1-1: The .gitmodules file correctly configures the PMOVES-HiRAG submodule with the proper URL (https://github.com/POWERFULMOVES/PMOVES-HiRAG.git) and branch (PMOVES.AI-Edition-Hardened). The submodule is properly integrated alongside other project dependencies. No issues found.

.gitmodules (2)

5-26: LGTM! Consistent branch specification for external submodules.

The external submodules are properly configured with explicit branch tracking to PMOVES.AI-Edition-Hardened, which provides predictable behavior and clear integration points.


5-41: All submodule repositories and branches are properly configured and accessible.

Verification confirms all 7 repositories are accessible and all 4 specified PMOVES.AI-Edition-Hardened branches exist. No configuration issues detected.

python/src/server/api_routes/knowledge_api.py (1)

903-910: LGTM! Good documentation of fail-fast behavior.

The added comments clearly document the API key validation pattern, improving code readability for future maintainers.

python/pmoves_mcp/claude_code_adapter.py (4)

19-26: LGTM!

The CommandResult dataclass is well-structured with appropriate optional fields and sensible defaults.


147-169: LGTM!

The convenience methods provide a clean API surface by wrapping common slash command patterns.


175-255: Verify schema format matches Archon's MCP tool registration expectations.

The tool definitions use "optional": True for optional parameters, which is not standard JSON Schema (typically uses a required array). Confirm this format is expected by Archon's MCP tool registration system.


258-265: LGTM!

The factory function provides a clean instantiation pattern with sensible defaults.

python/pmoves_mcp/__init__.py (1)

1-20: LGTM!

The package initialization correctly exposes all public entities from the adapter module with a clear __all__ definition.

python/src/server/services/persona_service.py (8)

31-74: LGTM!

The Pydantic models are well-defined with appropriate types and defaults. They follow the coding guideline to use Pydantic for validation.


91-106: LGTM!

The initialization properly uses service discovery for the Agent Zero URL and configures appropriate timeouts for HTTP operations.


150-190: LGTM with same note as get_persona.

The method logic is correct. The same sync-in-async consideration applies here as noted for get_persona.


192-295: LGTM!

The method properly orchestrates agent creation with comprehensive error handling for HTTP errors, timeouts, and generic exceptions. The tuple return pattern follows Archon conventions as mentioned in the PR objectives.


297-347: LGTM!

The method correctly combines prompt components with good defensive fallback behavior on errors.


349-389: LGTM!

The method appropriately handles the optional nature of enhancements with graceful fallback to None.


391-418: LGTM!

Clean implementation converting behavior weights to human-readable guidelines with appropriate thresholds.


425-447: LGTM!

The singleton pattern is appropriate for the service layer, and __all__ correctly exposes all public entities.

python/src/server/api_routes/persona_api.py (2)

70-139: LGTM!

The endpoint correctly follows the service layer pattern with proper error handling and logging.


142-199: LGTM!

The endpoint correctly distinguishes between 404 (not found) and 500 (server error) responses with appropriate logging.

python/src/server/main.py (1)

31-31: Persona router registration looks good

Import + inclusion are clean and consistent with router wiring.

Also applies to: 215-215

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread .github/dependabot.yml
Comment on lines +1 to +20
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all dependency manifest files
echo "=== Python dependency files ==="
find . -type f \( -name "requirements*.txt" -o -name "pyproject.toml" -o -name "setup.py" -o -name "setup.cfg" \) 2>/dev/null | head -20

echo -e "\n=== npm/Node dependency files ==="
find . -type f -name "package.json" 2>/dev/null | head -20

echo -e "\n=== Docker files ==="
find . -type f -iname "*dockerfile*" 2>/dev/null | head -20

echo -e "\n=== Repository root files ==="
ls -la / | grep -E "(requirements|pyproject|setup|package\.json|Dockerfile)" || echo "No match at root"

echo -e "\n=== Directory structure overview ==="
find . -maxdepth 2 -type d | head -20

Repository: POWERFULMOVES/PMOVES-Archon

Length of output: 804


Dependabot configuration needs directory path corrections.

The configuration won't detect dependencies in the current form. Dependency manifest files are located in subdirectories, not the repository root:

  • python/pyproject.toml (not detected by pip with directory /)
  • archon-ui-main/package.json and docs/package.json (not detected by npm with directory /)
  • Dockerfiles in archon-ui-main/, python/, and docs/ (not detected by docker with directory /)

Update the configuration to add entries targeting the correct directories:

Example corrections
version: 2
updates:
  - package-ecosystem: "pip"
    directory: "/python"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 5
  - package-ecosystem: "npm"
    directory: "/archon-ui-main"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 5
  - package-ecosystem: "npm"
    directory: "/docs"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 5
  - package-ecosystem: "docker"
    directory: "/archon-ui-main"
    schedule:
      interval: "weekly"
  - package-ecosystem: "docker"
    directory: "/python"
    schedule:
      interval: "weekly"
  - package-ecosystem: "docker"
    directory: "/docs"
    schedule:
      interval: "weekly"
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
🤖 Prompt for AI Agents
In @.github/dependabot.yml around lines 1 - 20, The dependabot config currently
points all package-ecosystem entries to directory "/" which won't find
manifests; update the entries so "package-ecosystem: pip" uses directory
"/python", add two "package-ecosystem: npm" entries with directories
"/archon-ui-main" and "/docs" (instead of just one at "/"), and add three
"package-ecosystem: docker" entries with directories "/archon-ui-main",
"/python", and "/docs"; keep the existing "package-ecosystem: github-actions"
entry at directory "/".

Comment thread external/PMOVES-Agent-Zero Outdated
@@ -0,0 +1 @@
Subproject commit 8642210ff15ecbfc0858819ac0bfe041f14df8fb
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify Agent Zero submodule configuration and commit validity

echo "=== Checking .gitmodules configuration ==="
if [ -f ".gitmodules" ]; then
  cat .gitmodules
else
  echo ".gitmodules not found"
fi

echo -e "\n=== Checking submodule status ==="
git config --file .gitmodules --get-regexp PMOVES-Agent-Zero || echo "No PMOVES-Agent-Zero entry in .gitmodules"

echo -e "\n=== Listing files in external directory ==="
ls -la external/ 2>/dev/null || echo "external directory not found"

echo -e "\n=== Checking git status for submodules ==="
git status --short 2>/dev/null | grep -E "(\?|@)" || echo "No submodule changes in status"

echo -e "\n=== Checking external/PMOVES-Agent-Zero reference ==="
if [ -f "external/PMOVES-Agent-Zero" ]; then
  cat external/PMOVES-Agent-Zero
else
  echo "external/PMOVES-Agent-Zero file not found"
fi

Repository: POWERFULMOVES/PMOVES-Archon

Length of output: 2284


🏁 Script executed:

#!/bin/bash
# Search for references to create_agent_with_persona and Agent Zero integration

echo "=== Searching for create_agent_with_persona references ==="
rg "create_agent_with_persona" --type python --type js --type ts 2>/dev/null || echo "No matches found"

echo -e "\n=== Searching for Agent Zero or PMOVES-Agent-Zero references ==="
rg "(PMOVES-Agent-Zero|Agent.?Zero)" --type python --type js --type ts 2>/dev/null || echo "No matches found"

echo -e "\n=== Checking for integration code mentioning agent integration ==="
rg "persona.*agent|agent.*persona" --type python --type js --type ts -i 2>/dev/null | head -20

Repository: POWERFULMOVES/PMOVES-Archon

Length of output: 297


Agent Zero submodule is configured but integration code is missing.

The .gitmodules configuration is correct. However, the PR objectives mention integrating with Agent Zero via create_agent_with_persona() and "Service discovery for Agent Zero endpoint resolution," but no integration code exists in the codebase. The submodule is added but not utilized—this implementation is incomplete.

🤖 Prompt for AI Agents
In `@external/PMOVES-Agent-Zero` at line 1, The Agent Zero submodule is added but
never used; implement integration by invoking create_agent_with_persona() from
the application startup/agent factory code (e.g., the function that initializes
agents or services) and wiring its returned agent into the existing agent
orchestration flow; additionally implement service discovery for Agent Zero
endpoint resolution (resolve via environment variable, config file, DNS SRV or
the existing service-discovery utility used elsewhere) and add error
handling/retry and unit tests and docs for the new integration points so the
submodule is actually exercised at runtime.

Comment thread external/PMOVES-BoTZ Outdated
Comment thread pmoves_multi_agent_pro_pack/docling Outdated
Comment thread python/pyproject.toml
Comment thread python/src/server/services/persona_service.py
POWERFULMOVES and others added 2 commits January 31, 2026 16:04
- Fix Dependabot directory paths (pip → /python, npm → /archon-ui-main)
- Add missing prometheus-client to dependency-groups.all
- Add branch specification to docling submodule
- Fix get_persona async/sync mismatch - wrap blocking Supabase call in asyncio.to_thread
- Add asyncio import to persona_service.py

Note: PMOVES-BoTZ submodule reference requires separate update via submodule sync.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ion-Hardened

The previous submodule reference (b39e3b4bf3...) was not found on the remote.
Updated to commit 8461b77c3768625da36d8379912db6e263610b45 which exists on PMOVES.AI-Edition-Hardened.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@python/src/server/services/persona_service.py`:
- Around line 174-190: The async list_personas method is making synchronous
Supabase calls (building query, calling order().execute() and constructing
Persona objects) which blocks the event loop; wrap the blocking parts in
asyncio.to_thread so the query execution and the Persona(**p) construction run
off the event loop. Specifically, inside list_personas replace the synchronous
sequence that creates query, calls query.order("name").execute() (response), and
builds personas = [Persona(**p) for p in response.data] with an
asyncio.to_thread-wrapped helper or lambda that performs those steps and returns
response.data (or the personas list), then await that to continue; ensure you
still log retrieved count and return the same dict structure.
🧹 Nitpick comments (2)
python/src/server/services/persona_service.py (2)

55-56: Consider adding datetime validation for timestamp fields.

The created_at and updated_at fields accept raw strings without validation. While Supabase likely returns valid ISO timestamps, adding validation would catch data corruption early per the coding guidelines.

♻️ Optional: Add datetime validation
+from datetime import datetime
+from pydantic import field_validator
+
 class Persona(BaseModel):
     # ... existing fields ...
-    created_at: str | None = None
-    updated_at: str | None = None
+    created_at: datetime | None = None
+    updated_at: datetime | None = None
+
+    `@field_validator`("created_at", "updated_at", mode="before")
+    `@classmethod`
+    def parse_timestamp(cls, v: str | None) -> datetime | None:
+        if v is None:
+            return None
+        if isinstance(v, datetime):
+            return v
+        return datetime.fromisoformat(v.replace("Z", "+00:00"))

Based on coding guidelines: "Never silently accept bad data — use Pydantic to raise validation errors for data corruption."


371-393: Blocking database call in sync method called from async context.

This method performs a synchronous Supabase query while being called (indirectly) from the async create_agent_with_persona. Consider either making this method async with asyncio.to_thread, or pre-fetching enhancements before building the prompt.

♻️ Option: Make method async
-    def get_archon_prompt_enhancements(self, form_name: str) -> str | None:
+    async def get_archon_prompt_enhancements(self, form_name: str) -> str | None:
         try:
             if not form_name:
                 return None
 
-            response = (
-                self.supabase_client.table("archon_prompts")
-                .select("prompt")
-                .eq("prompt_name", f"form_{form_name}_enhancements")
-                .execute()
-            )
+            def _fetch():
+                return (
+                    self.supabase_client.table("archon_prompts")
+                    .select("prompt")
+                    .eq("prompt_name", f"form_{form_name}_enhancements")
+                    .execute()
+                )
+
+            response = await asyncio.to_thread(_fetch)

This would also require making build_system_prompt async.

Comment thread python/src/server/services/persona_service.py
POWERFULMOVES and others added 2 commits February 3, 2026 07:06
- Fix dependabot.yml: Point pip to /python, npm to /archon-ui-main
- Add branch = main to docling submodule in .gitmodules
- Add prometheus-client>=0.20.0 to all dependency group

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- list_personas: Wrap blocking Supabase call in asyncio.to_thread
- build_system_prompt: Make async to avoid blocking event loop
- get_archon_prompt_enhancements: Make async and wrap Supabase call

This ensures all async methods properly avoid blocking the event loop
when calling synchronous Supabase client operations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@python/src/server/services/persona_service.py`:
- Around line 356-399: The current get_archon_prompt_enhancements function
conflates “not found” and “lookup failure” by returning None on exceptions;
change error handling to raise a custom exception (e.g., PromptLookupError) on
unexpected failures while still returning None when the prompt simply isn't
present. Implement or import a custom exception class (PromptLookupError) and in
the except block raise PromptLookupError(...) from e with a clear message
including form_name; leave the early return None when form_name is falsy and the
branch that returns None when response.data is empty untouched so callers like
build_system_prompt can distinguish not-found vs. failure and fall back
appropriately.
- Around line 92-107: The PersonaService __init__ must fail fast when critical
dependencies are missing: after resolving supabase_client (via supabase_client
or get_supabase_client()) and agent_zero_url (via get_agents_url()), validate
both are present and raise a custom exception (e.g., MissingDependencyError) if
either is None/empty; update PersonaService.__init__ to perform these checks
immediately (before setting timeout) and include clear messages referencing
supabase_client and agent_zero_url so startup aborts rather than deferring
runtime failures.
- Around line 109-303: The service currently returns (bool, dict) error payloads
from get_persona, list_personas and create_agent_with_persona; change these to
raise the appropriate custom exceptions from the exceptions module (e.g.,
NotFoundError/PersonaNotFoundError, ServiceError, ExternalAPIError) instead of
returning False with an error dict, and return the expected domain objects
directly on success (e.g., get_persona returns Persona, list_personas returns
list[Persona] and count, create_agent_with_persona returns the created agent
info dict). Specifically: in get_persona replace the "return False, {'error':
...}" paths with raising the NotFoundError/PersonaNotFoundError and on
unexpected exceptions raise ServiceError; in list_personas raise ServiceError on
failures instead of returning error dicts; in create_agent_with_persona raise
ExternalAPIError or ServiceError for httpx.HTTPStatusError/TimeoutException and
raise ServiceError for other exceptions, while returning the agent_response dict
on success. Update imports to use the exception classes and ensure callers (API
layer) convert those exceptions to HTTP responses.
- Around line 291-299: The HTTP error handlers for agent creation drop stack
traces and omit persona/context: update the except blocks handling
httpx.HTTPStatusError and httpx.TimeoutException in persona_service.py to call
logger.exception(...) and include persona_id and endpoint in the message (e.g.,
f"HTTP error creating agent for persona {persona_id} at {endpoint}: {e}"), so
the full traceback is recorded; also add persona_id and endpoint into the
returned error dictionaries (preserve e.response.status_code and e.response.text
for HTTPStatusError and include timeout details for TimeoutException) while
keeping the same boolean return shape used by the create agent flow.
- Around line 48-65: The behavior_weights dict and
PersonaCreateRequest.overrides are currently unvalidated and can accept invalid
floats; update their types to use Pydantic v2 Annotated constrained floats so
values are limited to 0.0–1.0 (and reject NaN/Infinity). Concretely, change
behavior_weights to dict[str, Annotated[float, Field(ge=0.0, le=1.0)]] =
Field(default_factory=dict) and change PersonaCreateRequest.overrides to
dict[str, Annotated[float, Field(ge=0.0, le=1.0)]] | None = Field(None, ...) to
enforce the range used by _build_behavior_adjustments and prevent bad data.

Comment on lines +48 to +65
id: str
name: str
description: str
system_prompt: str
behavior_weights: dict[str, float] = Field(default_factory=dict)
is_active: bool = True
archon_enhancements: dict[str, str] | None = None
created_at: str | None = None
updated_at: str | None = None


class PersonaCreateRequest(BaseModel):
"""Request model for creating a new agent with persona."""

persona_id: str = Field(..., description="ID of persona to use")
form_name: str | None = Field(None, description="Optional Archon form for behavior overrides")
overrides: dict[str, Any] | None = Field(None, description="Custom behavior weight overrides")
agent_name: str | None = Field(None, description="Custom name for the agent (defaults to persona name)")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

head -80 python/src/server/services/persona_service.py | cat -n

Repository: POWERFULMOVES/PMOVES-Archon

Length of output: 3328


🌐 Web query:

Pydantic v2 constrain dict value floats using Annotated Field ge le validators

💡 Result:

To constrain dictionary values (floats) in Pydantic v2, put the constraints on the value type, not on the dict itself:

from typing import Annotated
from pydantic import BaseModel, Field

UnitFloat = Annotated[float, Field(ge=0.0, le=1.0)]  # ge/le supported on Field for floats [1]

class Model(BaseModel):
    weights: dict[str, UnitFloat]  # constraints apply to every dict value

This is the recommended v2 style (instead of confloat(...))—Pydantic explicitly discourages confloat in favor of Annotated[..., Field(...)]. [1]

If you need custom per-item validation (beyond ge/le)

Use an Annotated validator on the value type:

from typing import Annotated
from pydantic import BaseModel
from pydantic.functional_validators import AfterValidator

def finite(v: float) -> float:
    if v != v or v in (float("inf"), float("-inf")):
        raise ValueError("must be finite")
    return v

FiniteFloat = Annotated[float, AfterValidator(finite)]  # annotated validators [2]

class Model(BaseModel):
    weights: dict[str, FiniteFloat]

Or validate the whole dict with @field_validator(..., mode="after") and iterate items (same validator concepts apply). [3]

Sources:
[1] Pydantic types / confloat docs (recommends Annotated[..., Field(gt/ge/lt/le...)]) (docs.pydantic.dev)
[2] Pydantic “Annotated Validators” (AfterValidator, etc.) (docs.pydantic.dev)
[3] Pydantic validator concepts (field_validator, before/after modes) (docs.pydantic.dev)

Citations:


🏁 Script executed:

grep -n "behavior_weights\|overrides" python/src/server/services/persona_service.py | head -30

Repository: POWERFULMOVES/PMOVES-Archon

Length of output: 1478


🏁 Script executed:

sed -n '100,200p' python/src/server/services/persona_service.py | cat -n

Repository: POWERFULMOVES/PMOVES-Archon

Length of output: 4618


Add Pydantic constraints for behavior weights and overrides to validate range and prevent data corruption.

The behavior_weights field (line 52) accepts any float value without validation, violating the "never silently accept bad data" guideline. The overrides field (line 64) is even more permissive—typed as dict[str, Any] instead of a constrained float type. The code's own docstring (_build_behavior_adjustments at line 401) documents the intended range as 0.0–1.0, but Pydantic currently allows invalid values (e.g., 1.5, −0.5, NaN, infinity) to pass through.

Use Pydantic v2's Annotated[float, Field(...)] pattern to constrain dict values:

Suggested fix
-from typing import Any
+from typing import Any, Annotated

-from pydantic import BaseModel, Field
+from pydantic import BaseModel, Field
+
+BehaviorWeight = Annotated[float, Field(ge=0.0, le=1.0)]

 class Persona(BaseModel):
-    behavior_weights: dict[str, float] = Field(default_factory=dict)
+    behavior_weights: dict[str, BehaviorWeight] = Field(default_factory=dict)

 class PersonaCreateRequest(BaseModel):
-    overrides: dict[str, Any] | None = Field(None, description="Custom behavior weight overrides")
+    overrides: dict[str, BehaviorWeight] | None = Field(None, description="Custom behavior weight overrides")
🤖 Prompt for AI Agents
In `@python/src/server/services/persona_service.py` around lines 48 - 65, The
behavior_weights dict and PersonaCreateRequest.overrides are currently
unvalidated and can accept invalid floats; update their types to use Pydantic v2
Annotated constrained floats so values are limited to 0.0–1.0 (and reject
NaN/Infinity). Concretely, change behavior_weights to dict[str, Annotated[float,
Field(ge=0.0, le=1.0)]] = Field(default_factory=dict) and change
PersonaCreateRequest.overrides to dict[str, Annotated[float, Field(ge=0.0,
le=1.0)]] | None = Field(None, ...) to enforce the range used by
_build_behavior_adjustments and prevent bad data.

Comment on lines +92 to +107
def __init__(self, supabase_client=None):
"""
Initialize the PersonaService.

Args:
supabase_client: Optional Supabase client instance. If not provided,
uses the global client from utils.
"""
self.supabase_client = supabase_client or get_supabase_client()
self.agent_zero_url = get_agents_url()
self.timeout = httpx.Timeout(
connect=5.0,
read=30.0,
write=10.0,
pool=5.0,
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fail fast when critical dependencies are missing.

Service startup should abort if Supabase or Agent Zero configuration is missing. Right now, agent_zero_url can be empty/None and the service still starts, delaying failure until runtime.

🚨 Suggested fail-fast checks (use your custom exception type)
 from ..config.service_discovery import get_agents_url
 from ..utils import get_supabase_client
+from ..exceptions import ServiceConfigurationError  # replace with your actual config exception

@@
     def __init__(self, supabase_client=None):
@@
-        self.supabase_client = supabase_client or get_supabase_client()
-        self.agent_zero_url = get_agents_url()
+        self.supabase_client = supabase_client or get_supabase_client()
+        if not self.supabase_client:
+            raise ServiceConfigurationError("Supabase client is not configured")
+
+        self.agent_zero_url = get_agents_url()
+        if not self.agent_zero_url:
+            raise ServiceConfigurationError("Agent Zero service URL is not configured")
🤖 Prompt for AI Agents
In `@python/src/server/services/persona_service.py` around lines 92 - 107, The
PersonaService __init__ must fail fast when critical dependencies are missing:
after resolving supabase_client (via supabase_client or get_supabase_client())
and agent_zero_url (via get_agents_url()), validate both are present and raise a
custom exception (e.g., MissingDependencyError) if either is None/empty; update
PersonaService.__init__ to perform these checks immediately (before setting
timeout) and include clear messages referencing supabase_client and
agent_zero_url so startup aborts rather than deferring runtime failures.

Comment on lines +109 to +303
async def get_persona(self, persona_id: str) -> tuple[bool, dict[str, Any]]:
"""
Retrieve a specific persona by ID from Supabase.

Args:
persona_id: Unique identifier of the persona to retrieve

Returns:
Tuple of (success, result_dict) where result_dict contains:
- persona: Persona object on success
- error: Error message string on failure

Example:
success, result = await persona_service.get_persona("dev_assistant")
if success:
persona = result["persona"]
print(f"Found persona: {persona.name}")
"""
try:
logger.info(f"Fetching persona: {persona_id}")

# Run blocking Supabase call in thread pool to avoid blocking event loop
response = await asyncio.to_thread(
lambda: (
self.supabase_client.table("archon_personas")
.select("*")
.eq("id", persona_id)
.execute()
)
)

if not response.data:
logger.warning(f"Persona not found: {persona_id}")
return False, {"error": f"Persona with ID '{persona_id}' not found"}

persona_data = response.data[0]
persona = Persona(**persona_data)

logger.info(f"Successfully retrieved persona: {persona.name}")
return True, {"persona": persona}

except Exception as e:
logger.error(f"Error fetching persona {persona_id}: {e}", exc_info=True)
return False, {"error": f"Failed to retrieve persona: {str(e)}"}

async def list_personas(self, active_only: bool = True) -> tuple[bool, dict[str, Any]]:
"""
List all available personas from Supabase.

Args:
active_only: If True, only return personas where is_active=True.
If False, return all personas including inactive ones.

Returns:
Tuple of (success, result_dict) where result_dict contains:
- personas: List of Persona objects
- total_count: Total number of personas returned
- error: Error message string on failure

Example:
success, result = await persona_service.list_personas(active_only=True)
if success:
for persona in result["personas"]:
print(f"{persona.name}: {persona.description}")
"""
try:
logger.info(f"Listing personas (active_only={active_only})")

# Build query function to run in thread pool
def build_and_execute_query():
query = self.supabase_client.table("archon_personas").select("*")
if active_only:
query = query.eq("is_active", True)
return query.order("name").execute()

# Run blocking Supabase call in thread pool to avoid blocking event loop
response = await asyncio.to_thread(build_and_execute_query)

personas = [Persona(**p) for p in response.data]

logger.info(f"Retrieved {len(personas)} personas")
return True, {
"personas": personas,
"total_count": len(personas)
}

except Exception as e:
logger.error(f"Error listing personas: {e}", exc_info=True)
return False, {"error": f"Failed to list personas: {str(e)}"}

async def create_agent_with_persona(
self,
persona_id: str,
form_name: str | None = None,
overrides: dict[str, Any] | None = None,
agent_name: str | None = None
) -> tuple[bool, dict[str, Any]]:
"""
Create a new agent in Agent Zero using the specified persona.

This method:
1. Fetches the persona from Supabase
2. Enhances the system prompt with Archon-specific additions
3. Applies form-based behavior weight overrides if specified
4. Calls Agent Zero's /api/persona/agent/create endpoint

Args:
persona_id: ID of persona to use for agent creation
form_name: Optional Archon form name for behavior overrides
overrides: Optional dict of behavior weight overrides (e.g., {"creativity": 0.8})
agent_name: Optional custom name for the agent (defaults to persona name)

Returns:
Tuple of (success, result_dict) where result_dict contains:
- agent_id: ID of created agent (on success)
- status: Agent status from Agent Zero
- message: Status message
- persona_id: ID of persona used
- system_prompt: Enhanced system prompt applied to agent
- error: Error message string on failure

Example:
success, result = await persona_service.create_agent_with_persona(
persona_id="code_reviewer",
form_name="strict_review",
overrides={"creativity": 0.3, "formality": 0.9}
)
if success:
print(f"Agent created: {result['agent_id']}")
"""
try:
# Step 1: Fetch persona from Supabase
persona_success, persona_result = await self.get_persona(persona_id)
if not persona_success:
return False, {"error": f"Failed to fetch persona: {persona_result.get('error')}"}

persona: Persona = persona_result["persona"]

# Step 2: Build enhanced system prompt
system_prompt = await self.build_system_prompt(persona, form_name)

# Step 3: Apply behavior weight overrides
behavior_weights = persona.behavior_weights.copy() if persona.behavior_weights else {}
if overrides:
behavior_weights.update(overrides)

# Step 4: Prepare agent creation request
request_data = {
"name": agent_name or persona.name,
"system_prompt": system_prompt,
"behavior_weights": behavior_weights,
"persona_id": persona_id,
"metadata": {
"form_name": form_name,
"archon_enhanced": True
}
}

logger.info(f"Creating agent with persona '{persona.name}' at Agent Zero")

# Step 5: Call Agent Zero API
endpoint = f"{self.agent_zero_url}/api/persona/agent/create"

async with httpx.AsyncClient(timeout=self.timeout) as client:
response = await client.post(
endpoint,
json=request_data,
headers={"Content-Type": "application/json"}
)
response.raise_for_status()
agent_response = response.json()

logger.info(f"Agent created successfully: {agent_response.get('agent_id')}")

return True, {
"agent_id": agent_response.get("agent_id"),
"status": agent_response.get("status", "created"),
"message": agent_response.get("message", "Agent created successfully"),
"persona_id": persona_id,
"system_prompt": system_prompt
}

except httpx.HTTPStatusError as e:
logger.error(f"HTTP error creating agent: {e.response.status_code} - {e.response.text}")
return False, {
"error": f"Agent Zero returned error: {e.response.status_code}",
"details": e.response.text
}
except httpx.TimeoutException:
logger.error("Timeout creating agent in Agent Zero")
return False, {"error": "Request to Agent Zero timed out"}
except Exception as e:
logger.error(f"Error creating agent with persona: {e}", exc_info=True)
return False, {"error": f"Failed to create agent: {str(e)}"}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use service-layer custom exceptions instead of (bool, dict) returns.

The service methods return error dictionaries instead of raising custom exceptions, which conflicts with the service-layer guidance and makes failure handling inconsistent across routes. Please raise exceptions from python/src/server/exceptions.py and let the API layer translate them to HTTP responses.

🤖 Prompt for AI Agents
In `@python/src/server/services/persona_service.py` around lines 109 - 303, The
service currently returns (bool, dict) error payloads from get_persona,
list_personas and create_agent_with_persona; change these to raise the
appropriate custom exceptions from the exceptions module (e.g.,
NotFoundError/PersonaNotFoundError, ServiceError, ExternalAPIError) instead of
returning False with an error dict, and return the expected domain objects
directly on success (e.g., get_persona returns Persona, list_personas returns
list[Persona] and count, create_agent_with_persona returns the created agent
info dict). Specifically: in get_persona replace the "return False, {'error':
...}" paths with raising the NotFoundError/PersonaNotFoundError and on
unexpected exceptions raise ServiceError; in list_personas raise ServiceError on
failures instead of returning error dicts; in create_agent_with_persona raise
ExternalAPIError or ServiceError for httpx.HTTPStatusError/TimeoutException and
raise ServiceError for other exceptions, while returning the agent_response dict
on success. Update imports to use the exception classes and ensure callers (API
layer) convert those exceptions to HTTP responses.

Comment on lines +291 to +299
except httpx.HTTPStatusError as e:
logger.error(f"HTTP error creating agent: {e.response.status_code} - {e.response.text}")
return False, {
"error": f"Agent Zero returned error: {e.response.status_code}",
"details": e.response.text
}
except httpx.TimeoutException:
logger.error("Timeout creating agent in Agent Zero")
return False, {"error": "Request to Agent Zero timed out"}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

head -c 30000 python/src/server/services/persona_service.py | wc -l

Repository: POWERFULMOVES/PMOVES-Archon

Length of output: 74


🏁 Script executed:

sed -n '280,310p' python/src/server/services/persona_service.py

Repository: POWERFULMOVES/PMOVES-Archon

Length of output: 1507


🏁 Script executed:

# Check the full function context to see variable availability
sed -n '250,320p' python/src/server/services/persona_service.py

Repository: POWERFULMOVES/PMOVES-Archon

Length of output: 3017


🌐 Web query:

httpx HTTPStatusError logging best practices attributes exc_info

💡 Result:

What httpx.HTTPStatusError gives you (useful log attributes)

When you call response.raise_for_status() and catch httpx.HTTPStatusError, the exception includes:

  • exc.request → the originating Request (e.g., exc.request.method, exc.request.url) [1]
  • exc.response → the Response object (e.g., exc.response.status_code, headers, body accessors) [1]
  • It’s defined as HTTPStatusError(message, *, request, response) in the API reference (so request/response are always present) [2]

Logging best practices (incl. exc_info)

  1. Prefer logger.exception(...) inside an except block
    It records the stack trace automatically and is clearer than logger.error(..., exc_info=True) [3][4].

  2. If you want structured logs, log the key HTTP fields + the exception

    • Log: method, URL, status code, maybe a request id / correlation id.
    • Avoid dumping full headers/bodies by default (often contain secrets/PII).
  3. Use exc_info correctly

    • In Python logging, exc_info can be True, an exception tuple, or (on modern Python) an exception instance; otherwise it falls back to sys.exc_info() [3].
    • Linters (e.g., Ruff) explicitly recommend logger.exception(...) over logger.error(..., exc_info=True) for readability/intent [4].

Example: good, low-risk logging pattern

import httpx
import logging

log = logging.getLogger(__name__)

try:
    r = httpx.get("https://api.example.com/resource")
    r.raise_for_status()
except httpx.HTTPStatusError as exc:
    # structured fields
    log.exception(
        "Upstream returned error response",
        extra={
            "http_method": exc.request.method,
            "url": str(exc.request.url),
            "status_code": exc.response.status_code,
        },
    )
    # optionally: include a *small*, sanitized snippet if safe
    # log.debug("Response snippet: %r", exc.response.text[:500])
    raise

Rule of thumb: log exc.request.*, exc.response.status_code, and the traceback (via logger.exception) as your default; only add headers/body selectively and sanitized. [1][3]

Sources: HTTPX Quickstart/Exceptions docs for .request/.response on HTTPStatusError [1][2]; Python logging docs for exc_info behavior [3]; Ruff guidance on preferring logger.exception [4].


Preserve stack traces and include endpoint/persona context in httpx-specific error handlers.

The httpx exception handlers drop the stack trace and omit critical context (persona_id, endpoint), making incident triage harder. Both persona_id and endpoint are available in scope at this location.

Note: Python logging best practice prefers logger.exception() over logger.error(..., exc_info=True) in except blocks—it automatically captures the full stack trace and signals intent more clearly.

📌 Suggested logging improvement
         except httpx.HTTPStatusError as e:
-            logger.error(f"HTTP error creating agent: {e.response.status_code} - {e.response.text}")
+            logger.exception(
+                f"HTTP error creating agent | persona_id={persona_id} | endpoint={endpoint} | "
+                f"status={e.response.status_code} | response={e.response.text}"
+            )
             return False, {
                 "error": f"Agent Zero returned error: {e.response.status_code}",
                 "details": e.response.text
             }
         except httpx.TimeoutException:
-            logger.error("Timeout creating agent in Agent Zero")
+            logger.exception(
+                f"Timeout creating agent | persona_id={persona_id} | endpoint={endpoint}"
+            )
             return False, {"error": "Request to Agent Zero timed out"}
🤖 Prompt for AI Agents
In `@python/src/server/services/persona_service.py` around lines 291 - 299, The
HTTP error handlers for agent creation drop stack traces and omit
persona/context: update the except blocks handling httpx.HTTPStatusError and
httpx.TimeoutException in persona_service.py to call logger.exception(...) and
include persona_id and endpoint in the message (e.g., f"HTTP error creating
agent for persona {persona_id} at {endpoint}: {e}"), so the full traceback is
recorded; also add persona_id and endpoint into the returned error dictionaries
(preserve e.response.status_code and e.response.text for HTTPStatusError and
include timeout details for TimeoutException) while keeping the same boolean
return shape used by the create agent flow.

Comment on lines +356 to +399
async def get_archon_prompt_enhancements(self, form_name: str) -> str | None:
"""
Retrieve Archon-specific prompt enhancements for a given form.

This method looks up form-specific prompt additions from the archon_prompts
table, allowing Archon to layer domain-specific behavior on top of base personas.

Args:
form_name: Name of the Archon form to retrieve enhancements for

Returns:
String of prompt enhancements or None if not found

Example:
enhancements = await persona_service.get_archon_prompt_enhancements("code_review")
# Returns: "Focus on: security vulnerabilities, performance issues,
# code maintainability, and adherence to project standards."
"""
try:
if not form_name:
return None

# Query archon_prompts table for form enhancements
# Run blocking Supabase call in thread pool to avoid blocking event loop
response = await asyncio.to_thread(
lambda: (
self.supabase_client.table("archon_prompts")
.select("prompt")
.eq("prompt_name", f"form_{form_name}_enhancements")
.execute()
)
)

if response.data:
enhancements = response.data[0].get("prompt", "")
logger.debug(f"Retrieved Archon enhancements for form: {form_name}")
return enhancements
else:
logger.debug(f"No Archon enhancements found for form: {form_name}")
return None

except Exception as e:
logger.warning(f"Error retrieving Archon prompt enhancements for '{form_name}': {e}")
return None
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Don’t return None on enhancement lookup failures.

None is used both for “not found” and “lookup failed,” which hides failures and violates the “never return None to indicate failure” rule. Raise a custom exception on errors (and let build_system_prompt fall back) or return an explicit error result.

💡 Example: raise on failure to distinguish from “not found”
-        except Exception as e:
-            logger.warning(f"Error retrieving Archon prompt enhancements for '{form_name}': {e}")
-            return None
+        except Exception as e:
+            logger.error(
+                f"Error retrieving Archon prompt enhancements for '{form_name}': {e}",
+                exc_info=True
+            )
+            raise
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async def get_archon_prompt_enhancements(self, form_name: str) -> str | None:
"""
Retrieve Archon-specific prompt enhancements for a given form.
This method looks up form-specific prompt additions from the archon_prompts
table, allowing Archon to layer domain-specific behavior on top of base personas.
Args:
form_name: Name of the Archon form to retrieve enhancements for
Returns:
String of prompt enhancements or None if not found
Example:
enhancements = await persona_service.get_archon_prompt_enhancements("code_review")
# Returns: "Focus on: security vulnerabilities, performance issues,
# code maintainability, and adherence to project standards."
"""
try:
if not form_name:
return None
# Query archon_prompts table for form enhancements
# Run blocking Supabase call in thread pool to avoid blocking event loop
response = await asyncio.to_thread(
lambda: (
self.supabase_client.table("archon_prompts")
.select("prompt")
.eq("prompt_name", f"form_{form_name}_enhancements")
.execute()
)
)
if response.data:
enhancements = response.data[0].get("prompt", "")
logger.debug(f"Retrieved Archon enhancements for form: {form_name}")
return enhancements
else:
logger.debug(f"No Archon enhancements found for form: {form_name}")
return None
except Exception as e:
logger.warning(f"Error retrieving Archon prompt enhancements for '{form_name}': {e}")
return None
async def get_archon_prompt_enhancements(self, form_name: str) -> str | None:
"""
Retrieve Archon-specific prompt enhancements for a given form.
This method looks up form-specific prompt additions from the archon_prompts
table, allowing Archon to layer domain-specific behavior on top of base personas.
Args:
form_name: Name of the Archon form to retrieve enhancements for
Returns:
String of prompt enhancements or None if not found
Example:
enhancements = await persona_service.get_archon_prompt_enhancements("code_review")
# Returns: "Focus on: security vulnerabilities, performance issues,
# code maintainability, and adherence to project standards."
"""
try:
if not form_name:
return None
# Query archon_prompts table for form enhancements
# Run blocking Supabase call in thread pool to avoid blocking event loop
response = await asyncio.to_thread(
lambda: (
self.supabase_client.table("archon_prompts")
.select("prompt")
.eq("prompt_name", f"form_{form_name}_enhancements")
.execute()
)
)
if response.data:
enhancements = response.data[0].get("prompt", "")
logger.debug(f"Retrieved Archon enhancements for form: {form_name}")
return enhancements
else:
logger.debug(f"No Archon enhancements found for form: {form_name}")
return None
except Exception as e:
logger.error(
f"Error retrieving Archon prompt enhancements for '{form_name}': {e}",
exc_info=True
)
raise
🤖 Prompt for AI Agents
In `@python/src/server/services/persona_service.py` around lines 356 - 399, The
current get_archon_prompt_enhancements function conflates “not found” and
“lookup failure” by returning None on exceptions; change error handling to raise
a custom exception (e.g., PromptLookupError) on unexpected failures while still
returning None when the prompt simply isn't present. Implement or import a
custom exception class (PromptLookupError) and in the except block raise
PromptLookupError(...) from e with a clear message including form_name; leave
the early return None when form_name is falsy and the branch that returns None
when response.data is empty untouched so callers like build_system_prompt can
distinguish not-found vs. failure and fall back appropriately.

POWERFULMOVES and others added 3 commits February 3, 2026 07:45
…Hardened

Updated nested submodule pointers to match latest commits:
- PMOVES-Agent-Zero (PMOVES.AI-Edition-Hardened)
- PMOVES-Deep-Serch (PMOVES.AI-Edition-Hardened)
- PMOVES-HiRAG (PMOVES.AI-Edition-Hardened)
- PMOVES-BotZ-gateway (main)
- PMOVES-tensorzero (main)
- docling (main)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@POWERFULMOVES
Copy link
Copy Markdown
Owner Author

Closing in favor of #3 with clean merge state

POWERFULMOVES added a commit that referenced this pull request Feb 12, 2026
feat(hirag,eval): hybrid Hi-RAG + eval sweeps & datasets
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant