diff --git a/docs/best-practices/agent-retry-strategies.mdx b/docs/best-practices/agent-retry-strategies.mdx
index 21230bc9..27e79dfb 100644
--- a/docs/best-practices/agent-retry-strategies.mdx
+++ b/docs/best-practices/agent-retry-strategies.mdx
@@ -898,6 +898,40 @@ async def test_hedged_requests():
assert call_count == 2 # Both requests started
```
+## Built-in Task Guardrail Retries
+
+PraisonAI provides built-in retry functionality specifically for guardrail validation failures, distinct from the generic `ExponentialBackoffRetry` patterns above.
+
+
+Guardrail retries are handled automatically by the executor when `Task(guardrail=..., max_retries=...)` is configured. This is separate from manual retry implementations.
+
+
+```python
+from praisonaiagents import Agent, Task, PraisonAIAgents
+
+def validate_content(output):
+ """Built-in guardrail with retry support"""
+ if len(output.raw.split()) < 50:
+ return False, "Content too short - needs at least 50 words"
+ return True, output
+
+task = Task(
+ description="Write a detailed explanation",
+ agent=agent,
+ guardrail=validate_content,
+ max_retries=3, # Built-in executor-level retry
+ retry_with_feedback=True
+)
+
+# The executor automatically handles:
+# - Guardrail validation
+# - Retry logic on failure
+# - Feedback to agent on retry
+# - Final failure after max_retries
+```
+
+This differs from manual retry strategies as it's integrated into the task execution workflow and handles the retry loop at the executor level.
+
## Conclusion
Implementing robust retry strategies is essential for building resilient multi-agent systems. By choosing the appropriate retry pattern and configuring it correctly, you can handle transient failures gracefully while avoiding issues like retry storms and cascading failures.
\ No newline at end of file
diff --git a/docs/best-practices/memory-cleanup.mdx b/docs/best-practices/memory-cleanup.mdx
index d6919ff0..530893d2 100644
--- a/docs/best-practices/memory-cleanup.mdx
+++ b/docs/best-practices/memory-cleanup.mdx
@@ -89,6 +89,19 @@ class MemoryEfficientConversationManager:
### 2. Agent Memory Management
+Memory construction is now thread-safe and async-safe. Concurrent `Task`s sharing a `memory_config` will coordinate through locks rather than each creating duplicate stores.
+
+```python
+from praisonaiagents import Agent, Task, PraisonAIAgents
+
+memory_config = {"storage": {"path": "./shared.db"}, "provider": "file"}
+
+agents = [Agent(name=f"A{i}", instructions="Summarize one line.") for i in range(4)]
+tasks = [Task(description=f"Summarize doc {i}.", agent=agents[i], config={"memory_config": memory_config}) for i in range(4)]
+
+PraisonAIAgents(agents=agents, tasks=tasks).start()
+```
+
Implement memory limits and cleanup for agents:
```python
@@ -371,6 +384,24 @@ class AutomaticMemoryManager:
schedule.every(5).minutes.do(conditional_cleanup)
```
+### 3. Agent Garbage Collection Safety Net
+
+Since PR #1514, `Agent.__del__` runs a best-effort `close_connections()` during garbage collection as a safety net. However, this may be skipped by the Python interpreter and **must not be relied upon**. Always use explicit cleanup:
+
+```python
+# Preferred: Explicit cleanup
+agent = Agent(name="Analyst", instructions="Analyze data.")
+try:
+ result = agent.start("Analyze quarterly numbers.")
+finally:
+ agent.close() # Guaranteed cleanup
+
+# Better: Context manager (recommended)
+with Agent(name="Analyst", instructions="Analyze data.") as agent:
+ result = agent.start("Analyze quarterly numbers.")
+# Cleanup happens automatically here
+```
+
## Best Practices
### 1. Use Context Managers
diff --git a/docs/features/guardrails.mdx b/docs/features/guardrails.mdx
index bceef5c7..606e31cc 100644
--- a/docs/features/guardrails.mdx
+++ b/docs/features/guardrails.mdx
@@ -254,7 +254,7 @@ guardrail = LLMGuardrail(
### Retry Configuration
-Configure retry behaviour for failed validations:
+Configure retry behaviour for failed validations (works in both sync and async execution paths):
```python
task = Task(
@@ -267,6 +267,35 @@ task = Task(
)
```
+**Execution Order**: Guardrail validation → retry (if failed) or pass → memory/user callbacks → task marked `completed`.
+
+### How Retries Work
+
+When a guardrail validation fails:
+1. **Before PR #1514**: Async execution bypassed retry logic
+2. **After PR #1514**: Both sync and async execution paths properly retry failed validations
+
+The executor increments `task.retry_count`, sets `task.status = "in progress"`, logs the retry, and continues the execution loop. On final failure after `max_retries`, it raises an exception. When a guardrail returns a modified `TaskOutput` or string, the downstream `task.result` and any memory callbacks receive the **modified** value, not the original.
+
+```python
+from praisonaiagents import Agent, Task, PraisonAIAgents
+
+def must_mention_price(output):
+ ok = "$" in output.raw
+ return (ok, output if ok else "Rewrite and include a price in USD.")
+
+agent = Agent(name="Writer", instructions="Write a one-line product blurb.")
+
+task = Task(
+ description="Write a blurb for a coffee mug.",
+ agent=agent,
+ guardrail=must_mention_price,
+ max_retries=3,
+)
+
+PraisonAIAgents(agents=[agent], tasks=[task]).start()
+```
+
### Composite Guardrails
Combine multiple validation criteria:
diff --git a/docs/features/resource-lifecycle.mdx b/docs/features/resource-lifecycle.mdx
index 21b0dac6..97173f96 100644
--- a/docs/features/resource-lifecycle.mdx
+++ b/docs/features/resource-lifecycle.mdx
@@ -83,6 +83,7 @@ sequenceDiagram
User->>Team: (exit with block)
Team->>Agents: close() each
Team->>Memory: close()
+ Note right of Memory: Closes SQLite, MongoDB, etc.
Team-->>User: cleanup complete
```
@@ -208,6 +209,21 @@ async with PraisonAIAgents(agents=[agent]) as workflow:
```
+
+Since PR #1514, `Memory.close_connections()` also closes MongoDB clients when present. Multiple calls to `close_connections()` are safe (idempotent). Agent `__del__` provides a safety net but should not be relied upon:
+
+```python
+# Explicit cleanup (preferred)
+with Agent(name="Analyst", instructions="Analyze quarterly numbers.") as agent:
+ agent.start("Summarize Q1 revenue.")
+# MongoDB / SQLite / registered connections closed here.
+
+# Async form
+async with Agent(name="Analyst", instructions="...") as agent:
+ await agent.astart("...")
+```
+
+
Once you exit a `with` block, consider the AgentTeam closed. Create a new one for additional work.
diff --git a/docs/features/thread-safety.mdx b/docs/features/thread-safety.mdx
index 72a89f0a..e278da61 100644
--- a/docs/features/thread-safety.mdx
+++ b/docs/features/thread-safety.mdx
@@ -53,6 +53,18 @@ Prior to PR #1488, chat_history mutations bypassed thread-safety locks at 31+ ca
- `chat_history` setter now acquires the `AsyncSafeState` lock for assignments
+#### What changed in PR #1514
+
+
+PR #1514 enhanced thread-safety in three key areas:
+
+**1. Locked Memory Initialization**: `Task.initialize_memory()` now uses `threading.Lock` with double-checked locking pattern. A new async variant `initialize_memory_async()` uses `asyncio.Lock` and offloads construction with `asyncio.to_thread()` to prevent event loop blocking.
+
+**2. Async-Locked Workflow State**: New `_set_workflow_finished(value)` method uses async locks to safely update workflow completion status across concurrent tasks.
+
+**3. Non-Mutating Task Context**: Task execution no longer mutates `task.description` during runs. Per-execution context is stored in `_execution_context` field, keeping the user-facing `task.description` stable across multiple executions.
+
+
#### Safe operations
```python