Last Updated: October 5, 2025 Epic: GREAT-4C - Remove Hardcoded User Context Status: Multi-User Architecture Implementation
Provides user-specific context without hardcoding assumptions. Enables multi-user support in canonical handlers by loading context dynamically from session-specific configuration.
Critical Fix: Removes hardcoded “VA/Kind Systems” references that block multi-user deployment.
from services.user_context_service import user_context_service
async def my_handler(intent, session_id):
# Get user context dynamically
context = await user_context_service.get_user_context(session_id)
# Access user-specific data
org = context.organization # User's organization
projects = context.projects # User's active projects
priorities = context.priorities # User's priorities
# Use in response
if priorities:
return f"Your top priority: {priorities[0]}"
else:
return "No priorities set in your configuration"
async def _handle_guidance_query(self, intent, session_id):
"""Handle guidance queries with user-specific context."""
# Get user-specific context (not hardcoded)
user_context = await user_context_service.get_user_context(session_id)
current_time = datetime.now()
current_hour = current_time.hour
# Use user's actual organization/projects
if 6 <= current_hour < 9:
if user_context.organization:
focus = f"Morning development work - perfect time for deep focus on {user_context.organization} priorities."
else:
focus = "Morning development work - perfect time for deep focus."
elif 9 <= current_hour < 12:
if user_context.projects:
focus = f"Mid-morning productivity - ideal for {', '.join(user_context.projects[:2])} work."
else:
focus = "Mid-morning productivity - ideal for project work."
# ... continue pattern
return {
"message": focus,
"context": {
"user_organization": user_context.organization,
"active_projects": user_context.projects
}
}
@dataclass
class UserContext:
"""User-specific context data."""
user_id: str
organization: Optional[str] = None
projects: list = None
priorities: list = None
preferences: Dict[str, Any] = None
def __post_init__(self):
if self.projects is None:
self.projects = []
if self.priorities is None:
self.priorities = []
if self.preferences is None:
self.preferences = {}
user_id: Session identifier (will be actual user ID in future)organization: User’s organization name (extracted from PIPER.md)projects: List of active projects from configurationpriorities: List of current priorities from configurationpreferences: Dictionary of user preferences and settingsContext is loaded from session-specific PIPER.md configuration:
# User Configuration
## Organization
Kind Systems
## Current Projects
- Piper Morgan development
- Q4 onramp implementation
- Documentation updates
## Priorities
- Complete GREAT-4C multi-user support
- Finalize alpha release preparation
- Update architectural documentation
def _extract_organization(self, config: Dict) -> Optional[str]:
"""Extract organization from config."""
for key, value in config.items():
if "organization" in key.lower():
return str(value)
return None
def _extract_projects(self, config: Dict) -> list:
"""Extract projects from config."""
projects = []
for key, value in config.items():
if "project" in key.lower():
if isinstance(value, list):
projects.extend(value)
elif isinstance(value, str):
lines = value.split('\n')
projects.extend([l.strip() for l in lines if l.strip()])
return projects
class UserContextService:
def __init__(self):
self.cache = {} # session_id -> UserContext
async def get_user_context(self, session_id: str) -> UserContext:
# Check cache first
if session_id in self.cache:
return self.cache[session_id]
# Load from configuration
context = await self._load_context_from_config(session_id)
# Cache it
self.cache[session_id] = context
return context
Before (WRONG - Hardcoded):
# This breaks multi-user support!
if config and "VA" in str(config.values()):
focus = "Morning development work - perfect time for deep focus on VA Q4 onramp"
elif "Kind Systems" in str(config.values()):
focus = "Morning development work - perfect time for deep focus on Kind Systems priorities"
Issues with Hardcoded Approach:
After (CORRECT - Dynamic):
# This works for any user!
context = await user_context_service.get_user_context(session_id)
if context.organization:
focus = f"Morning development work - perfect time for deep focus on {context.organization} priorities"
else:
focus = "Morning development work - perfect time for deep focus"
Benefits of Dynamic Approach:
Each session gets its own context:
# User 1 session
user1_context = await user_context_service.get_user_context("session_123")
# Returns: UserContext(organization="Kind Systems", projects=["Piper Morgan"])
# User 2 session
user2_context = await user_context_service.get_user_context("session_456")
# Returns: UserContext(organization="Acme Corp", projects=["Product Launch"])
async def test_multi_user_isolation():
"""Verify different users get different contexts."""
handlers = CanonicalHandlers()
# User 1 query
user1_response = await handlers._handle_guidance_query(
intent, "user1_session"
)
# User 2 query
user2_response = await handlers._handle_guidance_query(
intent, "user2_session"
)
# Verify no hardcoded context leakage
assert "VA Q4" not in user2_response.get("message", "")
assert user1_response != user2_response # Different contexts
def test_no_hardcoded_references():
"""Ensure no hardcoded user context remains."""
handlers_file = Path("services/intent_service/canonical_handlers.py")
content = handlers_file.read_text()
forbidden_patterns = [
r'"VA Q4"',
r'"Kind Systems"',
r'if.*"VA".*in.*str\(',
]
for pattern in forbidden_patterns:
assert not re.search(pattern, content), \
f"Found hardcoded reference: {pattern}"
await user_context_service.get_user_context(session_id)context.organization, context.projects, etc.Context Not Loading:
Hardcoded References Remain:
python3 scripts/audit_hardcoded_context.pyMulti-User Test Failing:
services/intent_service/canonical_handlers.pyservices/configuration/piper_config_loader.pytests/intent/test_no_hardcoded_context.pyStatus: ✅ Architecture defined - Implementation in progress
Critical for: Multi-user deployment, alpha release readiness
Last Updated: October 5, 2025 (GREAT-4C Phase 0)