Skip to content

[G1] Canonical project identity (git-remote project_key) on every session and event, for all sources #175

Closed

Description

@willwashburn

Part of #160 (group 1 — independent, small).

Problem

Consumers group by project. burn derives project / project_key per turn in crates/relayburn-sdk/src/reader/git.rs:1-60: walk up from cwd to the nearest .git, read .git/config, canonicalize the origin remote URL to host/path (strip scheme, git@ user, .git suffix, case), and fall back to the directory path when there is no remote. burn summary --project and --group-by project (#506) key on it, and subagent sessions inherit the parent's key.

relayhistory records cwd on events and sessions, repo_url/initial_commit only for Codex (from session_meta.payload.git), project on history rows as the raw cwd string, and projectId on cloud envelopes (CHANGELOG.md Unreleased: "repo slug from history.project or the session cwd's git remote … the explicit string unknown when neither is known"). So the canonical key exists in one place (the cloud outbox) but not on the local rows a Rust consumer reads. git_helpers.rs / git_sdk.rs already have remote parsing for commit links.

Scope

  1. Core helper project_identity(cwd: &Path) -> ProjectIdentity { project_key: String /* host/owner/repo or path */, repo_url: Option<String>, git_root: Option<PathBuf>, method: Remote|PathFallback } in ai-hist-core (reuse git_helpers.rs; no subprocess — read .git/config and handle worktrees/gitdir: files). Make its canonicalization rules identical to burn's reader/git.rs and pin them with shared test vectors (git@github.com:Org/Repo.git, https://github.com/org/repo, ssh://git@host:2222/org/repo.git, no-remote, worktree).
  2. Columns: sessions.project_key TEXT, session_events.project_key TEXT (denormalized for grouping without a join), populated at discovery (from the shallow cwd) and hydration; history.project left as-is for compatibility. Index (source, project_key, last_activity_ms).
  3. Codex: prefer payload.git.repository_url over cwd resolution when present; record method.
  4. Delegated children inherit the parent's project_key when their own cwd resolves to nothing (mirror burn Read zstd-compressed Codex rollouts as first-class evidence #506's rollup requirement) and record method = Inherited.
  5. Expose on CatalogSession, SessionEvent, TS contracts; ai-hist sessions list --project <key> filter; stats by_project keyed by project_key (keep the old cwd grouping behind --by-cwd).
  6. The cloud outbox projectId derivation switches to the core helper (one implementation).

Acceptance

  • Test vectors above produce identical keys in relayhistory and burn (burn side: add the same vectors to reader/git.rs tests in the burn characterization issue).
  • Corpus nested-subagent: child sessions carry the parent's project_key with method = Inherited.
  • ai-hist stats --json groups by canonical key so /Users/a/proj and /home/b/proj with the same remote merge.

Out of scope

Monorepo sub-project keys; workspace-roots-based multi-project sessions (record workspace_roots as today).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions