ADR-155: Project-Scoped Session Logs Architecture
Status
ACCEPTED (2026-02-03)
Context
Problem Statement
CODITECT's current session logging architecture (ADR-058) organizes session logs by machine, with logs stored at ~/PROJECTS/.coditect-data/session-logs/{machine-uuid}/. While this approach enables machine-specific session tracking, it creates several problems when working across multiple projects:
-
No project isolation - Session logs from all projects on a machine are intermingled in the same directory. When working on a customer project (e.g., Avivatec FP&A), session data is mixed with internal CODITECT framework work.
-
Session attribution confusion - The
/session-logcommand writes to the daily log for the machine, regardless of which project is active. Entries from different projects appear in the same log file with no clear separation. -
Customer data leakage risk - Customer project session logs (potentially containing sensitive financial data, business logic, or proprietary information) are stored alongside internal project logs. This complicates data governance and compliance (LGPD, SOC 2).
-
Multi-LLM session mixing - When using
/sxto export sessions from multiple LLMs, all exports go to the same pending directories regardless of project context. -
Context query pollution - The
/cxqcommand searches all session data, returning results from unrelated projects and reducing signal-to-noise ratio.
Current State
| Component | Current Behavior | Limitation |
|---|---|---|
| Session logs | ~/.coditect-data/session-logs/{machine-uuid}/ | Machine-scoped, not project-scoped |
/session-log | Writes to daily log for machine | No project isolation |
/sx exports | Pending directories per LLM | No project filtering |
/cx extraction | Processes all pending sessions | No project attribution |
/cxq queries | Searches all messages | No project filtering |
Requirements
- Session logs organized by project, enabling clear separation between projects
- Backward compatibility with existing machine-specific logs
- Support for customer project isolation (tenant-scoped session logs)
- Integration with Project Registry (ADR-144) for project discovery
- Migration path for existing session logs without data loss
- Multi-LLM export support with project attribution
Decision
1. Hierarchical Session Log Directory Structure
Extend the session log directory structure to support project scoping while maintaining backward compatibility:
~/PROJECTS/.coditect-data/
├── session-logs/
│ ├── {machine-uuid}/ # Legacy machine-scoped (backward compat)
│ │ └── YYYY-MM-DD.md # Daily logs
│ │
│ └── projects/ # NEW: Project-scoped logs
│ ├── PILOT/ # Platform project
│ │ ├── {machine-uuid}/
│ │ │ └── YYYY-MM-DD.md
│ │ └── exports/ # Project-specific exports
│ │
│ ├── CUST-avivatec-fpa/ # Customer project
│ │ ├── {machine-uuid}/
│ │ │ └── YYYY-MM-DD.md
│ │ └── exports/
│ │
│ └── PROJ-experiment/ # Personal project
│ ├── {machine-uuid}/
│ └── exports/
│
├── sessions-export-pending-anthropic/ # Legacy (backward compat)
├── sessions-export-pending-codex/
├── sessions-export-pending-gemini/
├── sessions-export-pending-kimi/
│
└── sessions-export-pending/ # NEW: Project-scoped exports
├── PILOT/
│ ├── anthropic/
│ ├── codex/
│ ├── gemini/
│ └── kimi/
└── CUST-avivatec-fpa/
├── anthropic/
└── ...
2. Session Log Scoping Rules
| Project Scope | Log Location | Export Location |
|---|---|---|
platform | projects/PILOT/{machine}/ | sessions-export-pending/PILOT/ |
org | projects/{project_id}/{machine}/ | sessions-export-pending/{project_id}/ |
customer | projects/CUST-{tenant}-{slug}/{machine}/ | sessions-export-pending/CUST-{tenant}-{slug}/ |
project | projects/PROJ-{slug}/{machine}/ | sessions-export-pending/PROJ-{slug}/ |
| Unscoped (legacy) | {machine-uuid}/ | sessions-export-pending-{llm}/ |
3. Command Updates
/session-log Enhancement
# Current behavior (no project context)
/session-log "Work completed"
# Writes to: session-logs/{machine-uuid}/YYYY-MM-DD.md
# NEW: With project context
/session-log "FPA.A.1.1 completed"
# Detects project from $CODITECT_PROJECT or cwd
# Writes to: session-logs/projects/CUST-avivatec-fpa/{machine}/YYYY-MM-DD.md
# Explicit project override
/session-log --project PILOT "Framework work done"
# Writes to: session-logs/projects/PILOT/{machine}/YYYY-MM-DD.md
Implementation in /session-log command:
def get_session_log_path(project_id: Optional[str] = None) -> Path:
"""Get the session log path, scoped to project if available."""
from scripts.core.paths import get_user_data_dir, get_machine_uuid
data_dir = get_user_data_dir()
machine_uuid = get_machine_uuid()
today = datetime.now().strftime('%Y-%m-%d')
# Determine project context
if project_id is None:
project_id = os.environ.get('CODITECT_PROJECT')
if project_id:
# Project-scoped path
log_dir = data_dir / 'session-logs' / 'projects' / project_id / machine_uuid
else:
# Legacy machine-scoped path
log_dir = data_dir / 'session-logs' / machine_uuid
log_dir.mkdir(parents=True, exist_ok=True)
return log_dir / f'{today}.md'
/sx Enhancement
# NEW: Project-scoped export
/sx --project CUST-avivatec-fpa
# Exports to: sessions-export-pending/CUST-avivatec-fpa/{llm}/
# Auto-detect from context
/sx
# If $CODITECT_PROJECT=CUST-avivatec-fpa, uses project-scoped directory
# Otherwise, falls back to legacy directories
/cxq Enhancement
# NEW: Project-filtered query
/cxq --project PILOT "deployment"
# Searches only PILOT session data
# Multi-project query
/cxq --project PILOT,CUST-avivatec-fpa "architecture"
# Exclude project
/cxq --exclude-project CUST-avivatec-fpa "all sessions"
4. Database Schema Updates
Extend sessions.db (Tier 3) to support project attribution:
-- ============================================================
-- ALTER: messages table - add project_id column
-- ============================================================
ALTER TABLE messages ADD COLUMN project_id TEXT;
-- Index for project-scoped queries
CREATE INDEX IF NOT EXISTS idx_messages_project ON messages(project_id);
-- ============================================================
-- View: project_messages
-- Purpose: Filter messages by project
-- ============================================================
CREATE VIEW IF NOT EXISTS project_messages AS
SELECT
m.*,
p.name AS project_name,
p.scope AS project_scope
FROM messages m
LEFT JOIN projects p ON m.project_id = p.project_id
WHERE m.project_id IS NOT NULL;
5. Project Detection Flow
┌─────────────────────────────────────────────────────────────────┐
│ /session-log invocation │
│ │ │
│ ▼ │
│ ┌──────────────────────────────┐ │
│ │ --project flag provided? │ │
│ └──────────────────────────────┘ │
│ │ │ │
│ YES NO │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌──────────────────────────┐ │
│ │ Use provided │ │ Check $CODITECT_PROJECT │ │
│ │ project_id │ │ env var │ │
│ └─────────────────┘ └──────────────────────────┘ │
│ │ │ │ │
│ │ SET NOT SET │
│ │ │ │ │
│ │ ▼ ▼ │
│ │ ┌─────────────┐ ┌──────────────────┐ │
│ │ │ Use env var │ │ Run discover_ │ │
│ │ │ project_id │ │ project(cwd) │ │
│ │ └─────────────┘ └──────────────────┘ │
│ │ │ │ │
│ │ │ FOUND / NOT FOUND │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌────────────────────────────────────┐ ┌──────┐ │
│ │ Write to project-scoped path │ │Legacy│ │
│ │ projects/{project_id}/{machine}/ │ │ path │ │
│ └────────────────────────────────────┘ └──────┘ │
└─────────────────────────────────────────────────────────────────┘
6. Migration Strategy
Phase 1: Schema Addition (Non-Breaking)
- Add
project_idcolumn tomessagestable in sessions.db - Create project-scoped directory structure
- Update
/session-logto detect project context (opt-in)
Phase 2: Backfill Existing Data
def migrate_session_logs_to_project(project_id: str, date_range: tuple):
"""
Migrate existing machine-scoped session logs to project-scoped location.
Uses heuristics to attribute logs to projects based on:
- Task IDs (e.g., FPA.A.1.1 → CUST-avivatec-fpa)
- Directory paths mentioned in logs
- Session metadata
"""
pass
Phase 3: Command Updates
/session-log- Auto-detect project, write to scoped path/sx- Support--projectflag, auto-detect from context/cx- Attribute extracted messages to projects/cxq- Support--projectfiltering
Phase 4: Deprecation
- Add warning when writing to legacy machine-scoped path
- Recommend migration to project-scoped structure
- After 8 weeks, make project scoping the default
7. Customer Data Isolation
For customer scope projects, additional isolation requirements apply:
| Requirement | Implementation |
|---|---|
| Data residency | Session logs stored in project-specific directory |
| Access control | Directory permissions restrict to project owner |
| Backup isolation | Per-project backup to separate GCS buckets |
| Audit trail | All access logged to project-specific audit log |
| Data deletion | /project delete removes all project session data |
def ensure_customer_data_isolation(project_id: str, tenant_id: str):
"""Ensure customer project data is properly isolated."""
project_dir = get_project_session_dir(project_id)
# Set restrictive permissions
os.chmod(project_dir, 0o700)
# Create .tenant file for audit
(project_dir / '.tenant').write_text(tenant_id)
# Initialize project-specific backup config
backup_config = {
'bucket': f'gs://coditect-customer-{tenant_id}-backups',
'encryption': 'customer-managed',
'retention_days': 365
}
(project_dir / '.backup-config.json').write_text(json.dumps(backup_config))
Consequences
Positive
- Clear project isolation - Session logs from different projects are physically separated
- Customer data protection - Customer project data is isolated for compliance (LGPD, SOC 2)
- Focused context queries -
/cxq --projectreturns only relevant results - Multi-project support - Seamless switching between projects with proper log attribution
- Backward compatibility - Existing machine-scoped logs continue to work
- Audit trail - Clear lineage of which sessions belong to which project
Negative
- Directory complexity - More nested directory structure to manage
- Migration effort - Existing logs need attribution/migration
- Storage overhead - Duplicate structure for project-scoped vs legacy paths
- Discovery overhead - Project detection adds latency to log writes
Risks and Mitigations
| Risk | Likelihood | Mitigation |
|---|---|---|
| Logs written to wrong project | Medium | Validate project_id against registry; warn on mismatch |
| Orphaned project directories | Low | Periodic cleanup of directories for deleted projects |
| Performance impact from project detection | Low | Cache project context in environment; lazy detection |
| Migration data loss | Low | Migration is additive; original logs preserved |
Implementation Plan
Week 1: Foundation
- Create project-scoped directory structure
- Add
project_idcolumn to sessions.db messages table - Implement
get_session_log_path()with project support - Update
/session-logcommand to use project context
Week 2: Export Integration
- Update
/sxto support--projectflag - Create project-scoped export directories
- Update
/cxto attribute messages to projects - Add
--projectfilter to/cxq
Week 3: Migration
- Implement session log migration script
- Backfill project_id for existing messages
- Add deprecation warnings for legacy paths
- Document migration process
Week 4: Validation
- Test customer data isolation
- Verify backward compatibility
- Performance testing for project detection
- Update documentation
Implementation Status (J.27)
Track: J.27 (Project-Scoped Session Log Sync) | Updated: 2026-02-08
| Phase | Subtask | Status | Implementation |
|---|---|---|---|
| Foundation | J.27.1 SSOT Location | ✅ Complete | projects/{project_id}/{machine}/ with symlinks |
| Foundation | J.27.2 Sync Script | ✅ Complete | session-log-git-sync.py v4.0.0 |
| Isolation | J.27.3 Multi-Tenant | ✅ Complete | 0o700 permissions, --tenant, PII scanning |
| Migration | J.27.4 Legacy Attribution | ✅ Complete | migrate-legacy-session-logs.py, deprecation warnings |
| Validation | J.27.5 Testing | ✅ Complete | 50 unit tests in test_session_log_git_sync.py |
| Docs | J.27.6 Documentation | ✅ Complete | /sync-logs v2.0.0, CLAUDE.md, ADR diagram |
Sync Architecture Diagram
~/.coditect-data/session-logs/ SESSION-LOG-GIT-SYNC.PY v4.0.0
├── projects/ ┌────────────────────────────┐
│ ├── PILOT/{uuid}/ │ discover_all_logs() │
│ │ ├── SESSION-LOG-2026-01-01.md ─────►│ ├── --project PILOT │
│ │ └── SESSION-LOG-2026-02-08.md ─────►│ ├── --tenant avivatec │
│ └── CUST-avivatec-fpa/{uuid}/ (0o700) │ └── PII scan (CUST-*) │
│ └── SESSION-LOG-2026-01-15.md ─────►│ │
│ │ sync_logs() │
│ (legacy flat symlinks → projects/PILOT/) │ ├── copy to git repo │
│ │ ├── enforce permissions │
└── SESSION-LOG-*.md ──(symlink)──► │ └── git commit + push │
└─────────────┬──────────────┘
│
~/.coditect-data/session-logs-git/
└── machines/{uuid}/
└── projects/
├── PILOT/SESSION-LOG-*.md
└── CUST-avivatec-fpa/SESSION-LOG-*.md
│
git push (ADR-159 routing)
│
github.com/coditect-ai/
coditect-core-sessions-logs (private)
Related ADRs
| ADR | Relationship |
|---|---|
| ADR-058 | Extended by this ADR for project scoping |
| ADR-186 | User data location where session logs reside |
| ADR-118 | sessions.db schema extended with project_id |
| ADR-144 | Project registry used for project discovery |
| ADR-159 | Multi-tenant routing for per-project git repos |