diff --git a/agentserver/listeners/base.py b/agentserver/listeners/base.py
index 715a0de..36835ed 100644
--- a/agentserver/listeners/base.py
+++ b/agentserver/listeners/base.py
@@ -1,11 +1,13 @@
"""
-Core base class for all listeners in the organism.
+Core base class for all listeners in the xml-pipeline organism.
+
+All capabilities — personalities, tools, gateways — inherit from this class.
"""
from __future__ import annotations
import logging
-from typing import List, Dict, Any, Optional
+from typing import List, Optional, Dict, Any
from lxml import etree
@@ -17,13 +19,14 @@ class XMLListener:
Base class for all capabilities (personalities, tools, gateways).
Subclasses must:
- - Define `listens_to` (list of root tags they handle)
- - Implement `async handle()`
+ - Define `listens_to` as a class attribute (list of root tags they handle)
+ - Implement `async handle()` method
- The `convo_id` received in handle() MUST be preserved in any response payload.
+ The `convo_id` received in handle() MUST be preserved in any response payload
+ (via make_response() helper or manually).
"""
- listens_to: List[str] = [] # Must be overridden in subclass
+ listens_to: List[str] = [] # Must be overridden in subclass — required
def __init__(
self,
@@ -32,6 +35,11 @@ class XMLListener:
config: Optional[Dict[str, Any]] = None,
**kwargs,
):
+ """
+ Args:
+ name: Optional explicit name (defaults to class name)
+ config: Owner-provided configuration from privileged registration
+ """
self.name = name or self.__class__.__name__
self.config = config or {}
self.logger = logging.getLogger(f"{__name__}.{self.name}")
@@ -40,14 +48,14 @@ class XMLListener:
self, msg: etree.Element, convo_id: str
) -> Optional[etree.Element]:
"""
- Handle a message whose root tag matches this listener.
+ Process an incoming message whose root tag matches this listener.
Args:
- msg: The payload element (repaired and C14N'd)
- convo_id: Thread/conversation UUID — must be preserved in response
+ msg: The payload element (already repaired and C14N'd)
+ convo_id: Thread/conversation UUID — must be preserved in any response
Returns:
- Response payload element with the same convo_id, or None
+ Response payload element (with convo_id preserved), or None if no response
"""
raise NotImplementedError(
f"{self.__class__.__name__}.handle() must be implemented"
@@ -62,8 +70,9 @@ class XMLListener:
**attribs,
) -> etree.Element:
"""
- Helper to create a response element with a preserved convo_id attribute.
- Recommended for all listeners.
+ Convenience helper to create a response element with preserved convo_id.
+
+ Strongly recommended for all listeners to ensure thread continuity.
"""
elem = etree.Element(tag, convo_id=convo_id, **attribs)
if text is not None:
@@ -71,4 +80,4 @@ class XMLListener:
return elem
def __repr__(self) -> str:
- return f"<{self.__class__.__name__} listens_to={self.listens_to}>"
\ No newline at end of file
+ return f"<{self.__class__.__name__} name='{self.name}' listens_to={self.listens_to}>"
\ No newline at end of file
diff --git a/docs/logic and iteration.md b/docs/logic and iteration.md
new file mode 100644
index 0000000..2254cef
--- /dev/null
+++ b/docs/logic and iteration.md
@@ -0,0 +1,72 @@
+# Executive Summary: Self-Calling Iteration Framework in xml-pipeline
+
+**Date**: December 29, 2025
+**Component**: Core reasoning mechanism for LLM personalities
+
+### Principle
+
+In xml-pipeline, **multi-step reasoning, planning, tool use, and iteration are implemented through open, auditable self-calls** — not hidden loops or state machines.
+
+The LLM personality participates in the conversation exactly like any other listener: it can send messages to its own root tag.
+
+### How It Works
+
+1. **Conversation thread = memory**
+ Every message (user, personality, tool) is appended to history keyed by `convo_id`.
+
+2. **Self-reflection = self-message**
+ To think step-by-step, the LLM emits its own root tag with the same `convo_id`:
+ ```xml
+
+ First, let's outline the steps...
+
+ ```
+
+3. **MessageBus routes it back** to the same personality instance.
+
+4. **Personality appends its own message** to history and calls the LLM again.
+
+5. **LLM sees full context** — including its previous thoughts — and continues.
+
+6. **Iteration emerges naturally** from repeated self-calls until final response.
+
+### Key Properties
+
+- **No hidden state** — the thread history is the only memory
+- **No special controller** — MessageBus and `convo_id` do all coordination
+- **Fully auditable** — every thought, plan, and intermediate step is a logged message
+- **Tool use fits identically** — tool calls are messages to other root tags
+- **Termination is natural** — LLM decides when to emit final response tag (e.g., ``)
+
+### Structured Planning Support
+
+Personalities are encouraged to use visible structures:
+
+```xml
+
+ List 2
+ List 3
+ List 5
+
+```
+
+This enables:
+- Self-coordination (LLM reads its own plan)
+- GUI progress rendering
+- Explicit termination conditions
+
+### Owner Control
+
+- `iteration-capacity` config (`high`/`medium`/`low`/`none`) tunes how strongly the personality is prompted to iterate
+- All behavior governed by owner-provided prompts and response templates
+- No personality can loop indefinitely without owner consent
+
+### Result
+
+The framework turns **conversation into computation**.
+
+Loops, conditionals, and planning become **visible message patterns** rather than hidden code.
+
+The organism reasons by talking to itself in the open — producing a complete, transparent trace of thought at every step.
+
+This is the core iteration mechanism for all LLM personalities in xml-pipeline.