Skip to content

vt-c-project-status-sync

Multi-source project status sync. Aggregates project signals from Open Brain (Sally.io meeting transcripts uploaded throughout the week) and an optional PO-Weekly transcript, then writes confirmed status updates to Asana.

Plugin: project-updates
Category: Other
Command: /vt-c-project-status-sync


Project Status Sync

Aggregate project status signals from multiple sources (Open Brain, Granola, PO-Weekly transcript) and write confirmed updates to Asana. Open Brain is the primary source — Sally.io meeting transcripts are uploaded via webhook throughout the week. Granola provides direct access to meeting transcripts not yet captured by Open Brain. The PO-Weekly transcript is the final confirmation/override layer.

Signal Model

All sources produce normalized ProjectSignals:

{
  project_name: str,          # spoken or referenced name
  content: str,               # relevant text (quote, summary, observation)
  source_type: str,           # "po-weekly" | "open-brain" | "granola" | "granola-transcript"
  source_detail: str,         # e.g. "Sally.io meeting 2026-03-24" or "Open Brain thought #42"
  signal_strength: str,       # "strong" (PO-Weekly) | "medium" (other sources)
  date: str                   # ISO date of the source
}

Prerequisites

  1. Load project config from TOOLKIT_ROOT/configs/project-config.yaml:

    python3 TOOLKIT_ROOT/scripts/seed_config.py --config "TOOLKIT_ROOT/configs/project-config.yaml"
    
    If config does not exist, this seeds it from Asana API (requires ASANA_PAT).

  2. Build the project lookup list from config: for each project, collect asana_gid, asana_name, short_name, and all aliases.

  3. Resolve optional PO-Weekly transcript argument:

  4. If a path was provided as argument: validate it exists
  5. If no argument: that's fine — Open Brain signals are the primary source
  6. If the user explicitly wants transcript-only mode: suggest /vt-c-project-updates instead

  7. Staleness guard: For each project in config, check last_updated date.

  8. If a project was updated within the last 3 days:
    • PO-Weekly signals (strong) still apply — they always override
    • Open Brain / Granola signals (medium) are skipped for this project — prevents duplicate updates from overlapping runs
  9. Track update timestamps: after a successful Asana write, update last_updated in the config YAML

Stage 1: Gather Open Brain Signals

Query Open Brain for project-relevant observations from the past 7 days.

For each project in the config:

  1. Search using project name and aliases:

    search_thoughts(query: "{short_name}")
    search_thoughts(query: "{alias}") — for each alias
    
    Filter results to the last 7 days.

  2. For each relevant thought returned:

  3. Create a ProjectSignal:

    project_name: {short_name}
    content: {thought text}
    source_type: "open-brain"
    source_detail: "Open Brain thought — {thought date}"
    signal_strength: "medium"
    date: {thought date}
    

  4. Deduplicate: if the same thought matches multiple aliases for the same project, keep only one signal.

If Open Brain MCP is not available (tools not loaded): display a warning and continue — the skill degrades to transcript-only mode (same as /vt-c-project-updates).

Expected output: List of ProjectSignals from Open Brain, grouped by project.

Stage 1b: Gather Granola Meeting Signals

Query Granola for project-relevant meeting content from the past 7 days. This catches meetings not yet uploaded to Open Brain via webhook.

  1. List recent meetings:

    list_meetings(time_range: "this_week")
    list_meetings(time_range: "last_week")
    

  2. Filter out PO-Weekly meetings (handled separately in Stage 2). Keep all other meetings (SELI, ad-hoc project discussions, client calls, etc.).

  3. For each non-PO-Weekly meeting, query for project-relevant content:

    query_granola_meetings(
      query: "project status updates, progress, blockers, decisions",
      document_ids: ["{meeting_id}"]
    )
    

  4. For each project mentioned in a Granola meeting response:

  5. Create a ProjectSignal:

    project_name: {mentioned project name}
    content: {relevant excerpt from Granola response — preserve citation links}
    source_type: "granola"
    source_detail: "Granola: {meeting title} ({meeting date})"
    signal_strength: "medium"
    date: {meeting date}
    

  6. Deduplicate against Open Brain signals (Stage 1): if the same meeting content appears in both Open Brain thoughts and Granola results (because Sally.io already uploaded it), keep only one signal — prefer the Open Brain version (it's already in persistent memory).

If Granola MCP is not available (tools not loaded): skip silently. Open Brain signals are the primary source; Granola is supplementary.

Expected output: List of ProjectSignals from Granola meetings, grouped by project.

Stage 2: Parse PO-Weekly Transcript (optional)

If a PO-Weekly transcript was provided:

  1. Parse the transcript:

    python3 TOOLKIT_ROOT/scripts/parse_transcript.py "<transcript_path>"
    

  2. Segment by project:

    python3 TOOLKIT_ROOT/scripts/segment_projects.py "<parsed_segments_json>"
    

  3. For each project segment, create a ProjectSignal:

    project_name: {project_name_spoken}
    content: {full segment text}
    source_type: "po-weekly"
    source_detail: "PO-Weekly transcript {date}"
    signal_strength: "strong"
    date: {today}
    

  4. Detect "no change" segments: if the segment text contains only phrases like "kein Update", "nichts Neues", "keine Veränderung", "no change", or the segment is very short (< 50 characters) with no status-indicating words — mark the signal as:

    signal_strength: "neutral"   # explicitly "no change"
    

If no transcript provided: skip this stage entirely. All status determination will be based on Open Brain signals (Stage 1).

Stage 3: Match Signals to Asana Projects

Match all collected ProjectSignals to Asana projects using the existing matching logic:

python3 TOOLKIT_ROOT/scripts/match_projects.py "<all_signals_json>" "TOOLKIT_ROOT/configs/project-config.yaml"

For Open Brain signals that don't match via the script (free-text content rather than structured project blocks), use inline fuzzy matching against the project lookup list built in Stage 0.

Group matched signals by asana_gid. Each project now has 0-N signals from various sources.

Stage 4: Determine Status per Project

For each project with matched signals, determine the status update:

Decision matrix:

PO-Weekly signal Open Brain / Granola signals Action
Strong (explicit status) Any Use PO-Weekly status. konfidenz: hoch. Other signals enrich was_passiert.
Neutral ("no change") None Skip — no update needed.
Neutral ("no change") Has signals Review — PO-Weekly says no change but week had activity. konfidenz: niedrig.
Not mentioned Has signals Use accumulated signals. konfidenz: mittel. Always goes to review.
Not mentioned None Skip — project not active this week.
No transcript provided Has signals Use accumulated signals. konfidenz: mittel. Always goes to review.
No transcript provided None Skip.
Any Staleness guard triggered (updated < 3 days ago) Skip medium signals — only PO-Weekly (strong) can override recent updates.

For each project that needs an update:

Use LLM analysis to produce: - was_passiert: Synthesize what happened from all signals. Cite sources. - was_kommt: What's coming next (extract from signals or mark "Nicht besprochen"). - status_farbe: One of planmaessig, gefaehrdet, unplanmaessig, zurueckgestellt, erledigt, verworfen - konfidenz: hoch, mittel, or niedrig (per decision matrix above) - konfidenz_grund: Why this confidence level — which sources contributed - quellen: List of source attributions ("PO-Weekly 2026-03-27", "Sally.io meeting 2026-03-24", etc.)

Confidence cap: Non-PO-Weekly signals can never exceed konfidenz: mittel. Only PO-Weekly with explicit status discussion can reach konfidenz: hoch.

Stage 5: Interactive Review

Present status updates for user review, grouped by confidence:

5a. Auto-apply high-confidence updates

Display count: "N projects with high-confidence updates (PO-Weekly confirmed) — auto-applying." List project names and proposed status colors for transparency.

5b. Review medium/low-confidence updates

For each konfidenz: mittel or niedrig update, present via AskUserQuestion:

Project: {project_name}
Status:  {status_farbe} ({mapped Asana color})
Sources: {quellen list}

Was passiert:
{was_passiert}

Was kommt:
{was_kommt}

Confidence: {konfidenz} — {konfidenz_grund}

Options: Confirm / Edit / Skip

  • Confirm: Accept as-is
  • Edit: User provides corrected status, text, or both
  • Skip: Don't create status update for this project

5c. Skipped projects summary

Display: "N projects skipped (no change / not discussed)." Ask: "Want to manually add a status update for any of these?" with project list.

Stage 6: Write to Asana

Write confirmed status updates to Asana.

Preferred method: Use asana_create_project_status MCP tool if available:

asana_create_project_status(
  project_gid: "{asana_gid}",
  text: "{was_passiert}\n\nNächste Schritte:\n{was_kommt}",
  color: "{mapped_asana_color}",
  title: "Status Update {today}"
)

Fallback: If MCP tool is not available, use the Python script:

python3 TOOLKIT_ROOT/scripts/write_asana_updates.py "<confirmed_updates_json>"
This requires ASANA_PAT environment variable.

Status color mapping (German → Asana MCP color / REST API status_type): - planmaessiggreen / on_track - gefaehrdetyellow / at_risk - unplanmaessigred / off_track - zurueckgestelltblue / on_hold - erledigtcomplete / complete - verworfenred / missed

After each successful write, update last_updated for that project in TOOLKIT_ROOT/configs/project-config.yaml to today's date (staleness guard for future runs).

Stage 7: Report

Display summary report:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Project Status Sync — {date}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Sources:
  Open Brain signals:  {N} thoughts across {M} projects
  Granola meetings:    {G} meetings queried, {H} with project signals
  PO-Weekly transcript: {provided/not provided}

Results:
  {X} updates auto-applied (high confidence, PO-Weekly confirmed)
  {Y} updates reviewed and confirmed
  {Z} updates skipped by user
  {W} projects not discussed (no signals)
  {E} updates failed (see errors below)
  {S} projects skipped by staleness guard (updated < 3 days ago)

Signal sources per project:
  {project_name}: PO-Weekly + Sally.io 3/24, 3/26
  {project_name}: Open Brain (2 thoughts) + Granola (SELI 3/25)
  {project_name}: Granola only (client call 3/23)
  ...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

If a PO-Weekly transcript was provided: archive it to 02-Knowledge/04-Dokumente/ with date prefix.

Open Brain capture (optional)

If capture_thought MCP tool is available:

capture_thought(
  thought: "Project status sync {date}: {X} auto-applied, {Y} reviewed, {W} unchanged. Key changes: {top 3 project status changes}."
)