ADR-0026: Project Detection for Session Organization¶
Status: Accepted Date: 2026-05-23
Context¶
Lion Studio's Runs page lists all sessions in a flat reverse-chronological list. As the session count grows across multiple repositories (lionagi, ag2, khive-oss, khive-cloud, etc.), the list becomes unusable — there is no way to scope sessions to a specific project.
The triggering need: Ocean works across 5–10 repos daily and wants sessions grouped by named project in Studio, with GitHub integration potential. The question is how to detect which project a session belongs to at creation time.
Two design patterns were evaluated by agent deliberation:
- Centralized registry (
~/.lionagi/projects.yaml): a single file mapping project names to git remotes and directory paths. Detection checks remotes/paths against the registry. - Per-repo config (
.lionagi/config.tomlin each repo): each repo declares its own project identity. Detection walks up from cwd to find the config file.
Decision¶
Per-repo config with global fallback. Three new artifacts:
1. .lionagi/config.toml (committed, per-repo)¶
Static identity declaration — safe to commit, no secrets. Separate from .lionagi/settings.yaml which may contain hooks, model keys, and sensitive local config.
[project]
name = "lionagi"
github = "ohdearquant/lionagi"
2. Global fallback in ~/.lionagi/settings.yaml¶
For repos the user doesn't control (e.g., a fork of ag2 where committing .lionagi/config.toml is not practical):
project_overrides:
"ag2ai/ag2": "ag2"
"/Users/lion/forks/ag2": "ag2"
Keys can be org/repo (matched against git remote) or absolute directory paths (prefix-matched against cwd).
3. Detection cascade (at session creation)¶
- Walk up from cwd to git root → read
.lionagi/config.toml→ use[project].name - If absent → load global
~/.lionagi/settings.yaml→ checkproject_overridesfor git remote match (org/repo), then cwd prefix match - If absent → parse git remote URL → derive
org/repoas fallback project name - If non-git →
null(shown as "Unassigned" in Studio)
4. Schema changes¶
Two new columns on sessions: - project TEXT — the resolved project name - project_source TEXT — detection method: config_toml, global_override, git_remote, or null
project_source lets Studio surface detection confidence and flag low-confidence assignments.
5. Implementation module¶
New module lionagi/cli/_project.py: - detect_project(cwd: Path | None = None) -> tuple[str | None, str | None] Returns (project_name, project_source). - Called by all three create_session sites: cli/agent.py, cli/orchestrate/_orchestration.py, and cli/state.py (import path).
Consequences¶
Positive - Fresh clone of any owned repo auto-detects project (zero config if .lionagi/config.toml committed) - Blast radius of missing config bounded to one repo, not global - Monorepos handled naturally via nested .lionagi/config.toml at subdirectory level - Aligns with existing settings merge pattern (project-level files override global) - project_source column enables Studio to distinguish high-confidence from inferred labels
Negative - Repos the user doesn't control require manual project_overrides in global settings - Non-git directories always land in "Unassigned" — no auto-detection path - New config file (.lionagi/config.toml) alongside existing .lionagi/settings.yaml — two files in .lionagi/ with different commit policies
Alternatives Considered¶
| Alternative | Why Rejected |
|---|---|
Centralized ~/.lionagi/projects.yaml | Global registry drifts from reality; git remote renames cause silent mis-categorization; all projects break if file missing on fresh machine; new infrastructure with no precedent in codebase |
pyproject.toml [tool.lionagi] | Can't add to repos the user doesn't control; pollutes upstream repos |
Extend .lionagi/settings.yaml with project key | settings.yaml is gitignored/local (may contain secrets); project identity should travel with the repo |
References¶
- Agent deliberation: 3×suggester (Simplify, Risk, System perspectives)
- Existing settings merge:
lionagi/agent/settings.pyload_settings() - Session creation sites:
cli/agent.py:292,cli/orchestrate/_orchestration.py:558,cli/state.py:237