name: daily-briefing
description: Every morning, summarize yesterday's deck activity into a fresh inbox capture
trigger:
  - cron: "0 7 * * *"
  - webhook:
      path: /hooks/daily-briefing-manual
      secret_env: BRIEFING_SECRET
concurrency: skip
timezone: America/Chicago
budget:
  max_duration_secs: 120
  max_llm_cost_usd: 0.05
state:
  declared_keys: [last_briefing_date]
tags: [daily, morning, briefing]

steps:
  - id: should_run
    type: transform
    body: |
      const today = new Date().toISOString().slice(0, 10);
      return context.state.last_briefing_date !== today;

  # The four deck reads below return slim summaries (id, ref, title,
  # stateId, updatedAt for tasks; id, kind, title, source for inbox).
  # No bodies — keeps the agent prompt well under Windows' 32k arg cap.

  - id: fetch_completed
    type: deck
    when: steps.should_run.json === true
    action: list_tasks
    state_ref: done
    since_hours: 24
    limit: 20

  - id: fetch_active
    type: deck
    when: steps.should_run.json === true
    action: list_tasks
    state_ref: active
    limit: 20

  - id: fetch_blocked
    type: deck
    when: steps.should_run.json === true
    action: list_tasks
    state_ref: blocked
    limit: 20

  - id: fetch_inbox
    type: deck
    when: steps.should_run.json === true
    action: list_inbox
    since_hours: 24
    limit: 15

  - id: write_briefing
    type: agent
    when: steps.should_run.json === true
    model: claude-sonnet-4-6
    timeout_secs: 90
    prompt: |
      Generate a morning briefing in markdown. Keep it tight - 6 to 10 bullets total.

      Yesterday's completed ({{ steps.fetch_completed.json.length }}): {{ steps.fetch_completed.json | json }}
      Active right now ({{ steps.fetch_active.json.length }}): {{ steps.fetch_active.json | json }}
      Blocked ({{ steps.fetch_blocked.json.length }}): {{ steps.fetch_blocked.json | json }}
      Fresh inbox ({{ steps.fetch_inbox.json.length }}): {{ steps.fetch_inbox.json | json }}

      Structure exactly:

      ## What shipped yesterday
      <2-3 bullets of meaningful completions; skip section if nothing>

      ## In flight
      <2-3 bullets of active tasks worth surfacing; skip section if none worth flagging>

      ## Needs attention
      <inbox items or blocked tasks worth flagging; skip if empty>

      ## Top focus today
      <exactly 1 bullet - the one thing>

      Tone: direct, no fluff, no preamble. The user re-reads this in 30 seconds and knows what matters.
    on_failure: abort

  - id: write_to_inbox
    type: deck
    action: create_inbox_item
    when: steps.should_run.json === true
    kind: capture
    title: "Morning briefing - {{ run.date }}"
    source: routine:daily-briefing
    body: |
      # Morning briefing - {{ run.date }}

      {{ steps.write_briefing.stdout }}

      ---
      _Generated by routine `daily-briefing` run {{ run.id }}_

  - id: persist_state
    type: set_state
    when: steps.should_run.json === true
    state:
      last_briefing_date: "{{ run.date }}"
