Skip to content

Feature: Real-Time Project Metadata & Status Sync #54

@EricGrill

Description

@EricGrill

Feature: Real-Time Project Metadata & Status Sync

Problem

Project metadata — tags, favorites, groups, colors, notes, "in-progress" status — is currently trapped in globalState on a single machine. Eric has no visibility into which projects he was actively working on when he switches from Ventress to Snoke. The mental overhead of reconstructing context ("what was I doing yesterday on the other machine?") slows down multi-device workflows.

Proposed Solution

Extend the File Location Index (FLI) with a Project Metadata Layer that syncs lightweight status updates across all connected devices in near-real-time.

Data Model

type ProjectMetadata = {
  projectId: string;
  version: number;          // monotonic counter for conflict resolution
  updatedAt: number;
  updatedBy: MachineId;

  // User-facing fields
  status: 'active' | 'archived' | 'idea' | 'blocked' | 'review';
  priority: 'low' | 'medium' | 'high' | 'urgent';
  note?: string;            // free-text scratchpad (max 500 chars)
  lastBranch?: string;      // last git branch checked out (auto-captured)
  lastOpenedAt?: number;    // timestamp of last open

  // Structural fields (synced)
  tags: string[];
  color?: number;
  favoriteGroup?: string;
};

type MetadataIndex = {
  schemaVersion: 1;
  machines: MachineHeartbeat[];
  projects: Record<string, ProjectMetadata>;
};

type MachineHeartbeat = {
  machineId: MachineId;
  lastSeen: number;
  vscodeVersion: string;
  extensionVersion: string;
};

Sync Strategy: Event-Stream over Gist Polling

Because Gists don't support push/webhooks, we use short-poll + diff:

  1. On activation, pull the Gist and merge metadata into local state.
  2. Every 60s (configurable), poll for Gist updates.
  3. On local change (tag added, color changed, note edited), increment version, update updatedAt, and push immediately.
  4. On pull, if remote version > local version, merge using last-writer-wins per field (fine-grained because each field is independent).

For users who opt into the self-hosted backend, upgrade to WebSocket/SSE for true real-time.

Implementation Plan

  • Phase 1: Metadata State Refactor
    • Create src/states/MetadataState.ts that mirrors the existing TagsState/FavoritesState APIs but reads from the merged metadata index instead of raw globalState.
    • Existing globalState becomes a local cache; MetadataState is the source of truth.
  • Phase 2: Auto-Capture Events
    • Hook into onDidChangeWorkspace to auto-update lastOpenedAt and lastBranch.
    • Hook into git extension API (vscode.git) to capture current branch on workspace switch.
  • Phase 3: Status UI
    • Add a new sidebar view: Project Status.
    • Shows a compact list: Project Name | Status | Last Branch | Note.
    • Inline editing: click status → quick-pick; click note → input box.
    • Color-code tree items by priority (red = urgent, gray = archived).
  • Phase 4: Conflict Resolution UI
    • If two machines edit the same note within the same sync window, show a diff dialog:
      • Projects: Resolve Sync Conflict — side-by-side local vs. remote, pick one or merge.
  • Phase 5: Activity Feed
    • Add a new output channel: Projects Sync.
    • Log: "[ventress] updated 'cafe-justo' status → active (2 min ago)".

Acceptance Criteria

  • Changing a project's color on Snoke reflects on Ventress within 2 minutes.
  • Opening a workspace auto-updates lastOpenedAt and lastBranch in the synced metadata.
  • Status and notes are editable directly from the sidebar without opening the project.
  • If a machine hasn't synced in >7 days, its heartbeat is shown as "stale" in the activity feed.

Open Questions

  • Should we support custom status values (user-defined strings) or keep a fixed enum?
  • Is 60s polling too aggressive for Gist rate limits? (Gist unauthenticated reads are generous; with a PAT, essentially unlimited.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions