Skip to main content

Overview

Codaph uses Mubit as a shared collaborative backend for cross-contributor context and semantic query. This enables teams to:
  • Share agent activity across contributors
  • Query semantic context from any contributor’s work
  • Track multi-contributor sessions
  • Synchronize reasoning and file changes

Mubit Memory Engine

From src/lib/memory-mubit.ts:427-830:

Core Concepts

Mubit organizes events into runs. Codaph supports two run scopes:Session Scope (session):
  • Each captured session gets its own Mubit run ID: codaph:<projectId>:<sessionId>
  • Isolated per-session context
  • Best for personal workflows
Project Scope (project):
  • All sessions share one Mubit run ID: codaph:<projectId>
  • Unified cross-contributor context
  • Recommended for teams
Configure run scope:
# During init (recommended)
codaph init
# Select project scope when prompted

# Or manually
codaph setup --mubit-run-scope project

# Environment variable
export CODAPH_MUBIT_RUN_SCOPE=project
Check current scope:
codaph status
# Output: Mubit run scope: project
Codaph resolves project identity in priority order:
  1. Explicit --mubit-project-id flag
  2. MUBIT_PROJECT_ID or CODAPH_PROJECT_ID env var
  3. Project settings (~/.codaph/settings.json)
  4. GitHub auto-detection from git remote origin
  5. Local path hash fallback
GitHub detection (from src/settings-store.ts:detectGitHubDefaults):
# If remote is [email protected]:owner/repo.git
# Project ID: owner/repo
Set project ID manually:
codaph init --mubit-project-id owner/repo

# Or update settings
codaph setup --mubit-project-id owner/repo
This ensures all team members share the same Mubit project ID regardless of local checkout path.
Codaph tracks contributors via actorId. Resolution order:
  1. --mubit-actor-id flag
  2. CODAPH_ACTOR_ID env var
  3. Global Codaph settings
  4. GitHub user from gh api user (login)
  5. Git config user.name
  6. Shell USER or USERNAME
Set actor ID:
codaph setup --mubit-actor-id alice

# Or environment variable
export CODAPH_ACTOR_ID=alice
Actor ID is attached to all events, enabling contributor filtering in TUI and queries.

Run ID Patterns

From src/lib/memory-mubit.ts:391-425:
// Session scope
mubitRunIdForSession(repoId, sessionId, runIdPrefix = "codaph")
// Returns: "codaph:<repoId>:<sessionId>"

// Project scope (recommended for teams)
mubitRunIdForProject(repoId, runIdPrefix = "codaph")
// Returns: "codaph:<repoId>"

// Specialized runs
mubitPromptRunIdForProject(repoId, runIdPrefix = "codaph-prompts")
// Returns: "codaph-prompts:<repoId>"

mubitSessionSummaryRunIdForProject(repoId, runIdPrefix = "codaph-sessions")
// Returns: "codaph-sessions:<repoId>"

mubitDiffRunIdForProject(repoId, runIdPrefix = "codaph-diffs")
// Returns: "codaph-diffs:<repoId>"
Specialized runs store compacted artifacts:
  • codaph-prompts:<repoId>: Prompt-only timeline for quick browsing
  • codaph-sessions:<repoId>: Session summary snapshots
  • codaph-diffs:<repoId>: Chunked diff data for large sessions

Collaboration Workflows

1
Team Setup
2
  • One team member initializes the project:
  • 3
    cd /path/to/shared-repo
    codaph init \
      --mubit-api-key <team-key> \
      --mubit-project-id owner/repo \
      --mubit-run-scope project \
      --mubit-actor-id alice
    
    4
  • Share settings (optional):
  • 5
    Commit .codaph/project.json (created by codaph init) to git:
    6
    {
      "schema": "codaph.project.v2",
      "mubitProjectId": "owner/repo",
      "mubitRunScope": "project",
      "agentProviders": ["codex", "claude-code"],
      "syncAutomation": {
        "enabled": true,
        "gitPostCommit": true,
        "agentComplete": true
      }
    }
    
    7
  • Other team members run:
  • 8
    cd /path/to/shared-repo
    codaph init --mubit-actor-id bob
    # Codaph detects mubitProjectId from git remote or project.json
    
    9
  • Sync and collaborate:
  • 10
    # Each contributor syncs their work
    codaph sync
    
    # Pull shared context
    codaph pull
    
    # Browse all contributors' sessions
    codaph tui
    

    Multi-Contributor Session Review

    From TUI inspect view (src/index.ts:6394-6471):
    1. Open TUI: codaph tui
    2. Select a session (may contain multiple contributors)
    3. Press c to open Contributors overlay
    4. Navigate with / to see:
      • Actor ID
      • Prompt count per contributor
      • Thought count
      • File count
      • Trace (prompt → thoughts → files)
    5. Press Enter on a contributor to filter prompts to only that actor
    6. Press f to cycle through actor filters
    7. Review contributor-specific changes in Diff pane

    Semantic Query Across Contributors

    Mubit’s semantic query works across all contributors in project scope:
    # Query recent shared context
    codaph mubit query "what changed in auth?" \
      --run codaph:owner/repo \
      --limit 10
    
    # Query specific session (any contributor)
    codaph mubit query "why did we refactor this?" \
      --session <session-id>
    
    # From TUI:
    # 1. Open session
    # 2. Press 'm' for Mubit chat
    # 3. Type question
    # 4. Get answer with cross-contributor context
    

    Sync Automation for Teams

    From src/sync-automation.ts and src/index.ts:1983-2076:

    Git Hooks

    Install team-wide automation:
    codaph sync setup
    # Installs:
    # - .git/hooks/post-commit (auto-sync after commits)
    # - .git/hooks/post-push (optional)
    # - .codex/hooks/agent-complete.sh (Codex)
    # - .claude/hooks/agent-complete (Claude Code)
    # - .gemini/hooks/agent-complete (Gemini CLI)
    
    Hook behavior:
    • Triggers codaph sync after relevant events
    • Respects cooldown (default: 600s between auto-pulls)
    • Marks pending triggers if lock is busy
    • Logs to .codaph/sync-automation-log.jsonl

    Sync Lock

    From src/sync-automation.ts:acquireSyncLock:
    // Only one sync per repo at a time
    const lockPath = ".codaph/sync-lock.json";
    
    // Hook mode: no wait, mark pending
    acquireSyncLock(lockPath, { waitMs: 0 });
    
    // Manual sync: wait up to 30s
    acquireSyncLock(lockPath, { waitMs: 30_000 });
    
    Pending trigger state (.codaph/mubit-remote-sync-state.json):
    {
      "pendingTrigger": {
        "pending": true,
        "source": "hook-post-commit",
        "markedAt": "2026-03-03T10:00:00.000Z"
      }
    }
    
    Next manual sync or hook run will clear pending state.

    Cooldown Configuration

    Prevent excessive cloud pulls:
    # Check current cooldown
    codaph status
    # Output: cooldown=600s
    
    # Update in ~/.codaph/settings.json
    {
      "projects": {
        "/path/to/repo": {
          "syncAutomation": {
            "remotePullCooldownSec": 300  // 5 minutes
          }
        }
      }
    }
    
    Cooldown logic (from src/sync-automation.ts:shouldRunRemotePullNow):
    function shouldRunRemotePullNow(
      lastRunAt: string | null,
      cooldownSec: number
    ): boolean {
      if (!lastRunAt) return true;
      const elapsed = Date.now() - Date.parse(lastRunAt);
      return elapsed >= cooldownSec * 1000;
    }
    
    Triggers that respect cooldown:
    • hook-agent-complete
    • hook-post-commit
    • hook-post-push
    • tui-startup (when autoWarmTuiOnOpen enabled)
    Manual codaph pull ignores cooldown.

    Mubit API Integration

    From src/lib/memory-mubit.ts:427-830:

    Event Ingestion

    class MubitMemoryEngine {
      async writeEvent(event: CapturedEventEnvelope): Promise<MemoryWriteResult> {
        const runId = this.runIdForEvent(event);
        const result = await this.ingestEvents(runId, [event]);
        await this.appendActivitiesForEvent(event, runId);
        return result;
      }
    
      async writeEventsBatch(events: CapturedEventEnvelope[]): Promise<void> {
        // Groups events by runId
        const byRun = new Map<string, CapturedEventEnvelope[]>();
        for (const event of events) {
          const runId = this.runIdForEvent(event);
          // ...
        }
        // Batch ingest per run
        for (const [runId, group] of byRun.entries()) {
          await this.ingestEvents(runId, group);
        }
        await this.appendActivitiesForBatch(events, 4);
      }
    }
    
    Payload compaction (from src/lib/memory-mubit.ts:308-385): To reduce bandwidth, Codaph compacts event payloads before sending to Mubit:
    function compactActivityEnvelope(event: CapturedEventEnvelope) {
      return {
        schema: "codaph_event.v2",
        event: {
          eventId: event.eventId,
          source: event.source,
          repoId: event.repoId,
          actorId: event.actorId,
          sessionId: event.sessionId,
          ts: event.ts,
          eventType: event.eventType,
          payload: compactPayload(event),  // Truncates text to 2000 chars
        },
      };
    }
    

    Semantic Query

    interface MubitSemanticQueryOptions {
      runId: string;
      query: string;
      limit?: number;  // default 8
      includeLinkedRuns?: boolean;
      directLane?: "semantic_search" | "hdql_query";  // default hdql_query
      mode?: "agent_routed" | "direct_bypass";  // default direct_bypass
    }
    
    async querySemanticContext(options: MubitSemanticQueryOptions) {
      const payload = {
        run_id: options.runId,
        query: options.query,
        mode: options.mode ?? "direct_bypass",
        include_linked_runs: options.includeLinkedRuns ?? false,
        limit: options.limit ?? 8,
        direct_lane: options.directLane ?? "hdql_query",
      };
      return await this.client.control.query(payload);
    }
    
    Usage:
    codaph mubit query "recent auth changes" \
      --run codaph:owner/repo \
      --limit 10 \
      --mode direct_bypass \
      --lane hdql_query
    

    Context Snapshot

    Fetch full run context snapshot:
    interface MubitContextSnapshotOptions {
      runId: string;
      timelineLimit?: number;  // default 500
      refresh?: boolean;  // default false
    }
    
    async fetchContextSnapshot(options: MubitContextSnapshotOptions) {
      const payload = {
        run_id: options.runId,
        timeline_limit: options.timelineLimit ?? 500,
        refresh: Boolean(options.refresh),
      };
      return await this.client.control.contextSnapshot(payload);
    }
    
    Usage:
    codaph mubit snapshot \
      --run codaph:owner/repo \
      --timeline-limit 1000 \
      --refresh
    

    Best Practices

    Recommended:
    codaph init --mubit-run-scope project
    
    Why?
    • All contributors see each other’s sessions
    • Semantic queries work across the team
    • Shared context for onboarding
    • Cross-contributor file change tracking
    When to use session scope?
    • Personal projects
    • Experimentation
    • Isolated feature development
    Ensure all team members use the same project ID:
    1. Auto-detect from GitHub: Use consistent remote URL
      git remote -v
      # origin [email protected]:owner/repo.git
      
    2. Or set explicitly:
      codaph setup --mubit-project-id owner/repo
      
    3. Commit project.json (created by codaph init):
      git add .codaph/project.json
      git commit -m "Add Codaph project config"
      
    Each team member should have a unique actor ID:
    # Alice
    codaph setup --mubit-actor-id alice
    
    # Bob
    codaph setup --mubit-actor-id bob
    
    Why?
    • Enables contributor filtering in TUI
    • Tracks who made which changes
    • Supports audit trails
    • Powers semantic queries like “what did alice change?”
    Set up automation to keep shared context fresh:
    # One-time setup
    codaph sync setup
    
    # Verify automation is working
    codaph status
    # Output: Auto-sync: enabled (post-commit:on, agent-complete:on)
    
    # Manual sync when needed
    codaph sync  # or just `codaph pull`
    
    Tip: Enable autoPullOnSync for automatic cloud pulls:
    // ~/.codaph/settings.json
    {
      "projects": {
        "/path/to/repo": {
          "syncAutomation": {
            "autoPullOnSync": true
          }
        }
      }
    }
    
    Regularly check sync health:
    codaph status
    
    Look for:
    • Recent lastSuccessAt timestamps
    • Non-zero receivedTimelineCount
    • Low consecutiveSameSnapshotCount (< 10)
    • No lastError messages
    If sync is stale:
    # Force refresh
    codaph pull --refresh
    
    # Check for errors
    cat .codaph/sync-automation-log.jsonl | tail -20
    

    Troubleshooting

    Issue: TUI contributors overlay is empty or missing teammates.Causes:
    1. Different project IDs across team members
    2. Session scope instead of project scope
    3. Sync not running
    Fix:
    # Check project ID matches
    codaph status | grep repoId
    # Should match across all team members
    
    # Check run scope
    codaph status | grep "run scope"
    # Should be "project" for teams
    
    # Sync to pull teammates' work
    codaph pull
    
    Issue: codaph mubit query returns no results despite activity.Causes:
    1. Wrong run ID
    2. Mubit API key not set
    3. Events not synced to cloud
    Fix:
    # Verify Mubit is enabled
    codaph doctor
    # Check: Mubit runtime: enabled
    
    # Check run ID
    codaph status
    # Note the project ID, construct run ID: codaph:<projectId>
    
    # Force sync to cloud
    codaph push
    
    # Query with correct run ID
    codaph mubit query "recent changes" --run codaph:owner/repo
    
    Issue: codaph sync fails with “Another Codaph sync is already running”.Cause: Concurrent sync attempt or stale lock file.Fix:
    # Wait a few seconds and retry
    sleep 5 && codaph sync
    
    # Or force release (only if sure no sync is running)
    rm .codaph/sync-lock.json
    codaph sync
    
    Issue: codaph status shows high dedup count, low import count.Cause: Events already exist in cloud (normal after backfill).Fix: This is expected behavior. Deduplication prevents duplicate events.If concerned:
    # Check if new events are being imported
    codaph pull --refresh
    codaph status
    # lastImported should increase for new activity
    

    Build docs developers (and LLMs) love