name: Project Metrics Tracker

on:
  schedule:
    # Offset to :05 to avoid overlapping with daily (07:00) and weekly (07:00)
    - cron: '5,20,35,50 * * * *' # every 15 min — collect metrics + push to analytics
    - cron: '0 7 * * *'           # daily — 7am UTC (9am CEST), digest (only if activity)
    - cron: '0 7 * * 1'           # weekly — Monday 7am UTC (9am CEST), Slack digest
  workflow_dispatch:
    inputs:
      force_digest:
        description: 'Send weekly digest now'
        type: boolean
        default: false

# Prevent overlapping runs from racing on the shared cache
concurrency:
  group: project-metrics
  cancel-in-progress: false

permissions:
  contents: read   # repo metadata + traffic API

jobs:
  track:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      # ── Restore cached metrics ──────────────────────────────
      # Uses run_id-based key rotation to avoid needing actions:write for cache delete.
      # Save always creates a new key; restore-keys finds the latest.
      - name: Restore metrics cache
        id: cache-restore
        uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4
        with:
          path: metrics.json
          key: project-metrics-${{ github.run_id }}
          restore-keys: |
            project-metrics-

      - name: Load previous metrics
        id: prev
        run: |
          if [ -f metrics.json ]; then
            cat metrics.json
            echo "docker_pulls=$(jq -r '.docker_pulls // 0' metrics.json)" >> "$GITHUB_OUTPUT"
            echo "stars=$(jq -r '.stars // 0' metrics.json)" >> "$GITHUB_OUTPUT"
            echo "forks=$(jq -r '.forks // 0' metrics.json)" >> "$GITHUB_OUTPUT"
            echo "week_docker=$(jq -r '.week_docker // 0' metrics.json)" >> "$GITHUB_OUTPUT"
            echo "week_stars=$(jq -r '.week_stars // 0' metrics.json)" >> "$GITHUB_OUTPUT"
            echo "week_forks=$(jq -r '.week_forks // 0' metrics.json)" >> "$GITHUB_OUTPUT"
            echo "week_views=$(jq -r '.week_views // 0' metrics.json)" >> "$GITHUB_OUTPUT"
            echo "week_clones=$(jq -r '.week_clones // 0' metrics.json)" >> "$GITHUB_OUTPUT"
            echo "day_docker=$(jq -r '.day_docker // 0' metrics.json)" >> "$GITHUB_OUTPUT"
            echo "day_stars=$(jq -r '.day_stars // 0' metrics.json)" >> "$GITHUB_OUTPUT"
            echo "day_forks=$(jq -r '.day_forks // 0' metrics.json)" >> "$GITHUB_OUTPUT"
            echo "day_views=$(jq -r '.day_views // 0' metrics.json)" >> "$GITHUB_OUTPUT"
            echo "day_clones=$(jq -r '.day_clones // 0' metrics.json)" >> "$GITHUB_OUTPUT"
            echo "last_milestone=$(jq -r '.last_milestone // 0' metrics.json)" >> "$GITHUB_OUTPUT"
            echo "has_data=true" >> "$GITHUB_OUTPUT"

            # Detect whether day/week baseline fields actually exist in the cached JSON.
            # Legacy caches from before these fields were added will be missing them,
            # and jq defaults them to 0 above — but 0 can also be a legitimate value.
            # Field-presence is the only reliable way to distinguish "missing" from "zero".
            DAY_PRESENT=$(jq 'has("day_docker") and has("day_stars") and has("day_forks")' metrics.json)
            WEEK_PRESENT=$(jq 'has("week_docker") and has("week_stars") and has("week_forks")' metrics.json)
            echo "day_fields_present=$DAY_PRESENT" >> "$GITHUB_OUTPUT"
            echo "week_fields_present=$WEEK_PRESENT" >> "$GITHUB_OUTPUT"
          else
            echo "has_data=false" >> "$GITHUB_OUTPUT"
            echo "day_fields_present=false" >> "$GITHUB_OUTPUT"
            echo "week_fields_present=false" >> "$GITHUB_OUTPUT"
            for key in docker_pulls stars forks week_docker week_stars week_forks week_views week_clones day_docker day_stars day_forks day_views day_clones last_milestone; do
              echo "${key}=0" >> "$GITHUB_OUTPUT"
            done
          fi

      # ── Fetch current metrics ───────────────────────────────
      - name: Fetch Docker Hub pulls
        id: docker
        run: |
          PULLS=$(curl -sf https://hub.docker.com/v2/repositories/labsai/eddi/ | jq '.pull_count')
          echo "pulls=$PULLS" >> "$GITHUB_OUTPUT"
          echo "Docker pulls: $PULLS"

      - name: Fetch GitHub stats
        id: github
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          REPO_DATA=$(curl -sf -H "Authorization: Bearer $GH_TOKEN" \
            https://api.github.com/repos/labsai/EDDI)
          STARS=$(echo "$REPO_DATA" | jq '.stargazers_count')
          FORKS=$(echo "$REPO_DATA" | jq '.forks_count')
          echo "stars=$STARS" >> "$GITHUB_OUTPUT"
          echo "forks=$FORKS" >> "$GITHUB_OUTPUT"
          echo "Stars: $STARS, Forks: $FORKS"

      - name: Fetch GitHub traffic (views + clones)
        id: traffic
        env:
          # METRICS_PAT has admin:read scope for traffic API; falls back to GITHUB_TOKEN
          GH_TOKEN: ${{ secrets.METRICS_PAT || github.token }}
        run: |
          VIEWS_DATA=$(curl -s -H "Authorization: Bearer $GH_TOKEN" \
            https://api.github.com/repos/labsai/EDDI/traffic/views)
          if echo "$VIEWS_DATA" | jq -e '.count' > /dev/null 2>&1; then
            VIEWS=$(echo "$VIEWS_DATA" | jq '.count // 0')
            UNIQUES=$(echo "$VIEWS_DATA" | jq '.uniques // 0')
          else
            echo "⚠️  Traffic API unavailable — set METRICS_PAT secret for traffic data"
            VIEWS=0; UNIQUES=0
          fi

          CLONES_DATA=$(curl -s -H "Authorization: Bearer $GH_TOKEN" \
            https://api.github.com/repos/labsai/EDDI/traffic/clones)
          if echo "$CLONES_DATA" | jq -e '.count' > /dev/null 2>&1; then
            CLONES=$(echo "$CLONES_DATA" | jq '.count // 0')
          else
            CLONES=0
          fi

          echo "views=$VIEWS" >> "$GITHUB_OUTPUT"
          echo "uniques=$UNIQUES" >> "$GITHUB_OUTPUT"
          echo "clones=$CLONES" >> "$GITHUB_OUTPUT"
          echo "Views: $VIEWS (uniques: $UNIQUES), Clones: $CLONES"

      # ── Detect trigger type ──────────────────────────────────
      - name: Detect schedule type
        id: schedule
        run: |
          SCHEDULE="${{ github.event.schedule }}"
          FORCE="${{ github.event.inputs.force_digest }}"

          # Determine which schedule triggered this run
          if [ "$SCHEDULE" = "0 7 * * *" ]; then
            TYPE="daily"
          elif [ "$SCHEDULE" = "0 7 * * 1" ] || [ "$FORCE" = "true" ]; then
            TYPE="weekly"
          else
            TYPE="collect"
          fi
          echo "type=$TYPE" >> "$GITHUB_OUTPUT"
          echo "Schedule: '$SCHEDULE' → type=$TYPE"

      # ── Compute deltas ──────────────────────────────────────
      - name: Compute deltas
        id: delta
        run: |
          PULL_DIFF=$(( ${{ steps.docker.outputs.pulls }} - ${{ steps.prev.outputs.docker_pulls }} ))
          STAR_DIFF=$(( ${{ steps.github.outputs.stars }} - ${{ steps.prev.outputs.stars }} ))
          FORK_DIFF=$(( ${{ steps.github.outputs.forks }} - ${{ steps.prev.outputs.forks }} ))
          echo "pull_diff=$PULL_DIFF" >> "$GITHUB_OUTPUT"
          echo "star_diff=$STAR_DIFF" >> "$GITHUB_OUTPUT"
          echo "fork_diff=$FORK_DIFF" >> "$GITHUB_OUTPUT"

          # Check for changes
          if [ "$PULL_DIFF" -ne 0 ] || [ "$STAR_DIFF" -ne 0 ] || [ "$FORK_DIFF" -ne 0 ]; then
            echo "changed=true" >> "$GITHUB_OUTPUT"
          else
            echo "changed=false" >> "$GITHUB_OUTPUT"
          fi

          # Milestone check (every 10,000 pulls)
          CURRENT=${{ steps.docker.outputs.pulls }}
          LAST_MS=${{ steps.prev.outputs.last_milestone }}
          CURRENT_MS=$(( CURRENT / 10000 * 10000 ))
          if [ "$CURRENT_MS" -gt "$LAST_MS" ] && [ "$LAST_MS" -ne 0 ]; then
            echo "milestone=$CURRENT_MS" >> "$GITHUB_OUTPUT"
          else
            echo "milestone=0" >> "$GITHUB_OUTPUT"
          fi

      # ── Validate baselines ──────────────────────────────────
      # A baseline is valid when: (1) the cache existed, (2) the fields are present,
      # AND (3) the Docker pulls baseline is non-zero. Docker pulls only ever increase,
      # so a zero baseline is always a cold-start artifact (cache eviction/rebuild).
      # Stars and forks CAN legitimately be 0 for new repos, but Docker pulls cannot
      # be 0 for an established project with 300k+ pulls.
      - name: Validate baselines
        id: baselines
        run: |
          HAS_DATA="${{ steps.prev.outputs.has_data }}"
          DAY_PRESENT="${{ steps.prev.outputs.day_fields_present }}"
          WEEK_PRESENT="${{ steps.prev.outputs.week_fields_present }}"
          DAY_DOCKER="${{ steps.prev.outputs.day_docker }}"
          WEEK_DOCKER="${{ steps.prev.outputs.week_docker }}"

          # Day baselines: fields must exist AND docker baseline must be non-zero
          if [ "$HAS_DATA" = "true" ] && [ "$DAY_PRESENT" = "true" ] && [ "$DAY_DOCKER" -gt 0 ]; then
            echo "day_valid=true" >> "$GITHUB_OUTPUT"
          else
            echo "day_valid=false" >> "$GITHUB_OUTPUT"
            echo "⚠️  Day baselines invalid (has_data=$HAS_DATA, day_present=$DAY_PRESENT, day_docker=$DAY_DOCKER)"
          fi

          # Week baselines: fields must exist AND docker baseline must be non-zero
          if [ "$HAS_DATA" = "true" ] && [ "$WEEK_PRESENT" = "true" ] && [ "$WEEK_DOCKER" -gt 0 ]; then
            echo "week_valid=true" >> "$GITHUB_OUTPUT"
          else
            echo "week_valid=false" >> "$GITHUB_OUTPUT"
            echo "⚠️  Week baselines invalid (has_data=$HAS_DATA, week_present=$WEEK_PRESENT, week_docker=$WEEK_DOCKER)"
          fi

      # ── Push to analytics (always, if data changed) ─────────
      - name: Send to Google Analytics 4
        if: steps.delta.outputs.changed == 'true' && steps.prev.outputs.has_data == 'true'
        continue-on-error: true
        env:
          GA4_SECRET: ${{ secrets.GA4_API_SECRET }}
        run: |
          curl -sf -X POST \
            "https://www.google-analytics.com/mp/collect?measurement_id=G-L1011GL1PY&api_secret=$GA4_SECRET" \
            -H 'Content-Type: application/json' \
            -d "{
              \"client_id\": \"github-actions.metrics\",
              \"events\": [{
                \"name\": \"project_metrics\",
                \"params\": {
                  \"docker_pulls\": ${{ steps.docker.outputs.pulls }},
                  \"docker_delta\": ${{ steps.delta.outputs.pull_diff }},
                  \"github_stars\": ${{ steps.github.outputs.stars }},
                  \"github_forks\": ${{ steps.github.outputs.forks }},
                  \"github_views\": ${{ steps.traffic.outputs.views }},
                  \"github_clones\": ${{ steps.traffic.outputs.clones }},
                  \"engagement_time_msec\": 1
                }
              }]
            }"

      - name: Send to Umami Cloud
        if: steps.delta.outputs.changed == 'true' && steps.prev.outputs.has_data == 'true'
        continue-on-error: true
        run: |
          curl -sf -X POST "https://cloud.umami.is/api/send" \
            -H 'Content-Type: application/json' \
            -H 'User-Agent: EDDI-GitHub-Actions/1.0' \
            -d "{
              \"type\": \"event\",
              \"payload\": {
                \"hostname\": \"eddi.labs.ai\",
                \"language\": \"en-US\",
                \"url\": \"/project-metrics\",
                \"website\": \"06a369b1-4a64-4262-9f6e-0a987ca7122a\",
                \"name\": \"project_metrics\",
                \"data\": {
                  \"docker_pulls\": ${{ steps.docker.outputs.pulls }},
                  \"docker_delta\": ${{ steps.delta.outputs.pull_diff }},
                  \"github_stars\": ${{ steps.github.outputs.stars }},
                  \"github_forks\": ${{ steps.github.outputs.forks }},
                  \"github_views\": ${{ steps.traffic.outputs.views }},
                  \"github_clones\": ${{ steps.traffic.outputs.clones }}
                }
              }
            }"

      # ── Milestone alert (immediate) ─────────────────────────
      - name: Milestone celebration
        if: steps.delta.outputs.milestone != '0'
        continue-on-error: true
        env:
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        run: |
          MS=$(printf "%'d" ${{ steps.delta.outputs.milestone }})
          curl -sf -X POST "$SLACK_WEBHOOK" \
            -H 'Content-type: application/json' \
            -d "{
              \"blocks\": [
                {
                  \"type\": \"header\",
                  \"text\": { \"type\": \"plain_text\", \"text\": \"🎉  EDDI hit ${MS} Docker pulls!\", \"emoji\": true }
                },
                {
                  \"type\": \"section\",
                  \"text\": {
                    \"type\": \"mrkdwn\",
                    \"text\": \"⭐ Stars: ${{ steps.github.outputs.stars }}  ·  🍴 Forks: ${{ steps.github.outputs.forks }}\"
                  }
                }
              ]
            }"

      # ── Weekly digest (Monday or manual) ────────────────────
      - name: Weekly digest
        if: >-
          steps.schedule.outputs.type == 'weekly'
          && steps.baselines.outputs.week_valid == 'true'
        continue-on-error: true
        env:
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        run: |
          if [ -z "$SLACK_WEBHOOK" ]; then
            echo "No SLACK_WEBHOOK_URL configured, skipping"
            exit 0
          fi

          PULLS=${{ steps.docker.outputs.pulls }}
          STARS=${{ steps.github.outputs.stars }}
          FORKS=${{ steps.github.outputs.forks }}
          VIEWS=${{ steps.traffic.outputs.views }}
          UNIQUES=${{ steps.traffic.outputs.uniques }}
          CLONES=${{ steps.traffic.outputs.clones }}

          WEEK_PULL_DIFF=$(( PULLS - ${{ steps.prev.outputs.week_docker }} ))
          WEEK_STAR_DIFF=$(( STARS - ${{ steps.prev.outputs.week_stars }} ))
          WEEK_FORK_DIFF=$(( FORKS - ${{ steps.prev.outputs.week_forks }} ))
          WEEK_VIEW_DIFF=$(( VIEWS - ${{ steps.prev.outputs.week_views }} ))
          WEEK_CLONE_DIFF=$(( CLONES - ${{ steps.prev.outputs.week_clones }} ))

          # Sanity check: if delta equals current, baseline was 0 (cold-start artifact)
          if [ "$WEEK_PULL_DIFF" -eq "$PULLS" ] && [ "$PULLS" -gt 0 ]; then
            echo "⚠️  Weekly deltas are bogus (baseline was 0) — skipping digest"
            exit 0
          fi

          PULLS_FMT=$(printf "%'d" "$PULLS")

          # Build sign prefixes (pulls/stars/forks only increase; views/clones can decrease)
          pull_sign=""; [ "$WEEK_PULL_DIFF" -gt 0 ] && pull_sign="+"
          star_sign=""; [ "$WEEK_STAR_DIFF" -gt 0 ] && star_sign="+"
          fork_sign=""; [ "$WEEK_FORK_DIFF" -gt 0 ] && fork_sign="+"

          # Views/clones: suppress delta when baseline was 0 (field missing from old cache)
          view_delta=""; if [ "${{ steps.prev.outputs.week_views }}" -ne 0 ] || [ "$VIEWS" -eq 0 ]; then
            vs=""; [ "$WEEK_VIEW_DIFF" -gt 0 ] && vs="+"; view_delta=", ${vs}${WEEK_VIEW_DIFF}"
          fi
          clone_delta=""; if [ "${{ steps.prev.outputs.week_clones }}" -ne 0 ] || [ "$CLONES" -eq 0 ]; then
            cs=""; [ "$WEEK_CLONE_DIFF" -gt 0 ] && cs="+"; clone_delta=" (${cs}${WEEK_CLONE_DIFF})"
          fi

          curl -sf -X POST "$SLACK_WEBHOOK" \
            -H 'Content-type: application/json' \
            -d "{
              \"blocks\": [
                {
                  \"type\": \"header\",
                  \"text\": { \"type\": \"plain_text\", \"text\": \"📊  EDDI Weekly Digest\", \"emoji\": true }
                },
                {
                  \"type\": \"section\",
                  \"fields\": [
                    { \"type\": \"mrkdwn\", \"text\": \"*🐳 Docker Pulls*\n${PULLS_FMT} (${pull_sign}${WEEK_PULL_DIFF})\" },
                    { \"type\": \"mrkdwn\", \"text\": \"*⭐ GitHub Stars*\n${STARS} (${star_sign}${WEEK_STAR_DIFF})\" },
                    { \"type\": \"mrkdwn\", \"text\": \"*🍴 Forks*\n${FORKS} (${fork_sign}${WEEK_FORK_DIFF})\" },
                    { \"type\": \"mrkdwn\", \"text\": \"*👀 Repo Views (14d)*\n${VIEWS} (${UNIQUES} unique${view_delta})\" },
                    { \"type\": \"mrkdwn\", \"text\": \"*📥 Git Clones (14d)*\n${CLONES}${clone_delta}\" }
                  ]
                },
                {
                  \"type\": \"context\",
                  \"elements\": [
                    { \"type\": \"mrkdwn\", \"text\": \"<https://hub.docker.com/r/labsai/eddi|Docker Hub> · <https://github.com/labsai/EDDI|GitHub> · <https://eddi.labs.ai|Website>\" }
                  ]
                }
              ]
            }"

      # ── Daily digest (only if activity) ──────────────────────
      - name: Daily digest
        if: >-
          steps.schedule.outputs.type == 'daily'
          && steps.baselines.outputs.day_valid == 'true'
        continue-on-error: true
        env:
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        run: |
          if [ -z "$SLACK_WEBHOOK" ]; then
            echo "No SLACK_WEBHOOK_URL configured, skipping"
            exit 0
          fi

          PULLS=${{ steps.docker.outputs.pulls }}
          STARS=${{ steps.github.outputs.stars }}
          FORKS=${{ steps.github.outputs.forks }}
          VIEWS=${{ steps.traffic.outputs.views }}
          CLONES=${{ steps.traffic.outputs.clones }}

          DAY_PULL_DIFF=$(( PULLS - ${{ steps.prev.outputs.day_docker }} ))
          DAY_STAR_DIFF=$(( STARS - ${{ steps.prev.outputs.day_stars }} ))
          DAY_FORK_DIFF=$(( FORKS - ${{ steps.prev.outputs.day_forks }} ))
          DAY_VIEW_DIFF=$(( VIEWS - ${{ steps.prev.outputs.day_views }} ))
          DAY_CLONE_DIFF=$(( CLONES - ${{ steps.prev.outputs.day_clones }} ))

          # Sanity check: if delta equals current, baseline was 0 (cold-start artifact)
          if [ "$DAY_PULL_DIFF" -eq "$PULLS" ] && [ "$PULLS" -gt 0 ]; then
            echo "⚠️  Daily deltas are bogus (baseline was 0) — skipping digest"
            exit 0
          fi

          # Only send if something actually changed today
          if [ "$DAY_PULL_DIFF" -eq 0 ] && [ "$DAY_STAR_DIFF" -eq 0 ] && [ "$DAY_FORK_DIFF" -eq 0 ]; then
            echo "No daily activity — skipping digest"
            exit 0
          fi

          # Build sign prefixes (pulls/stars/forks only increase; views/clones can decrease)
          pull_sign=""; [ "$DAY_PULL_DIFF" -gt 0 ] && pull_sign="+"
          star_sign=""; [ "$DAY_STAR_DIFF" -gt 0 ] && star_sign="+"
          fork_sign=""; [ "$DAY_FORK_DIFF" -gt 0 ] && fork_sign="+"

          # Views/clones: suppress delta when baseline was 0 (field missing from old cache)
          view_delta=""; if [ "${{ steps.prev.outputs.day_views }}" -ne 0 ] || [ "$VIEWS" -eq 0 ]; then
            vs=""; [ "$DAY_VIEW_DIFF" -gt 0 ] && vs="+"; view_delta=" (${vs}${DAY_VIEW_DIFF})"
          fi
          clone_delta=""; if [ "${{ steps.prev.outputs.day_clones }}" -ne 0 ] || [ "$CLONES" -eq 0 ]; then
            cs=""; [ "$DAY_CLONE_DIFF" -gt 0 ] && cs="+"; clone_delta=" (${cs}${DAY_CLONE_DIFF})"
          fi

          PULLS_FMT=$(printf "%'d" "$PULLS")

          curl -sf -X POST "$SLACK_WEBHOOK" \
            -H 'Content-type: application/json' \
            -d "{
              \"blocks\": [
                {
                  \"type\": \"header\",
                  \"text\": { \"type\": \"plain_text\", \"text\": \"📈  EDDI Daily Update\", \"emoji\": true }
                },
                {
                  \"type\": \"section\",
                  \"text\": {
                    \"type\": \"mrkdwn\",
                    \"text\": \"🐳 *Pulls:* ${PULLS_FMT} (${pull_sign}${DAY_PULL_DIFF})  ·  ⭐ *Stars:* ${STARS} (${star_sign}${DAY_STAR_DIFF})  ·  🍴 *Forks:* ${FORKS} (${fork_sign}${DAY_FORK_DIFF})\"
                  }
                },
                {
                  \"type\": \"context\",
                  \"elements\": [
                    { \"type\": \"mrkdwn\", \"text\": \"👀 Views (14d): ${VIEWS}${view_delta} · 📥 Clones (14d): ${CLONES}${clone_delta} · <https://github.com/labsai/EDDI|GitHub>\" }
                  ]
                }
              ]
            }"

      # ── Save updated metrics ────────────────────────────────
      - name: Build metrics snapshot
        run: |
          SCHED_TYPE="${{ steps.schedule.outputs.type }}"

          CUR_PULLS=${{ steps.docker.outputs.pulls }}
          CUR_STARS=${{ steps.github.outputs.stars }}
          CUR_FORKS=${{ steps.github.outputs.forks }}

          WEEK_VALID="${{ steps.baselines.outputs.week_valid }}"
          DAY_VALID="${{ steps.baselines.outputs.day_valid }}"

          CUR_VIEWS=${{ steps.traffic.outputs.views }}
          CUR_CLONES=${{ steps.traffic.outputs.clones }}

          # ── Determine week baseline ──
          # Uses the same field-presence check as the Validate step
          if [ "$WEEK_VALID" != "true" ]; then
            # No prior data or legacy cache missing week fields — seed from current
            WEEK_DOCKER=$CUR_PULLS
            WEEK_STARS=$CUR_STARS
            WEEK_FORKS=$CUR_FORKS
            WEEK_VIEWS=$CUR_VIEWS
            WEEK_CLONES=$CUR_CLONES
          elif [ "$SCHED_TYPE" = "weekly" ]; then
            # Weekly digest just sent — reset baseline to now
            WEEK_DOCKER=$CUR_PULLS
            WEEK_STARS=$CUR_STARS
            WEEK_FORKS=$CUR_FORKS
            WEEK_VIEWS=$CUR_VIEWS
            WEEK_CLONES=$CUR_CLONES
          else
            # Preserve existing baseline
            WEEK_DOCKER=${{ steps.prev.outputs.week_docker }}
            WEEK_STARS=${{ steps.prev.outputs.week_stars }}
            WEEK_FORKS=${{ steps.prev.outputs.week_forks }}
            WEEK_VIEWS=${{ steps.prev.outputs.week_views }}
            WEEK_CLONES=${{ steps.prev.outputs.week_clones }}
          fi

          # ── Determine day baseline ──
          # Uses the same field-presence check as the Validate step
          if [ "$DAY_VALID" != "true" ]; then
            # No prior data or legacy cache missing day fields — seed from current
            DAY_DOCKER=$CUR_PULLS
            DAY_STARS=$CUR_STARS
            DAY_FORKS=$CUR_FORKS
            DAY_VIEWS=$CUR_VIEWS
            DAY_CLONES=$CUR_CLONES
          elif [ "$SCHED_TYPE" = "daily" ]; then
            # Daily digest just sent — reset baseline to now
            DAY_DOCKER=$CUR_PULLS
            DAY_STARS=$CUR_STARS
            DAY_FORKS=$CUR_FORKS
            DAY_VIEWS=$CUR_VIEWS
            DAY_CLONES=$CUR_CLONES
          else
            # Preserve existing baseline
            DAY_DOCKER=${{ steps.prev.outputs.day_docker }}
            DAY_STARS=${{ steps.prev.outputs.day_stars }}
            DAY_FORKS=${{ steps.prev.outputs.day_forks }}
            DAY_VIEWS=${{ steps.prev.outputs.day_views }}
            DAY_CLONES=${{ steps.prev.outputs.day_clones }}
          fi

          # Current milestone marker
          CURRENT_MS=$(( CUR_PULLS / 10000 * 10000 ))

          jq -n \
            --argjson docker_pulls "$CUR_PULLS" \
            --argjson stars "$CUR_STARS" \
            --argjson forks "$CUR_FORKS" \
            --argjson week_docker "$WEEK_DOCKER" \
            --argjson week_stars "$WEEK_STARS" \
            --argjson week_forks "$WEEK_FORKS" \
            --argjson week_views "$WEEK_VIEWS" \
            --argjson week_clones "$WEEK_CLONES" \
            --argjson day_docker "$DAY_DOCKER" \
            --argjson day_stars "$DAY_STARS" \
            --argjson day_forks "$DAY_FORKS" \
            --argjson day_views "$DAY_VIEWS" \
            --argjson day_clones "$DAY_CLONES" \
            --argjson last_milestone "$CURRENT_MS" \
            '{docker_pulls: $docker_pulls, stars: $stars, forks: $forks,
              week_docker: $week_docker, week_stars: $week_stars, week_forks: $week_forks,
              week_views: $week_views, week_clones: $week_clones,
              day_docker: $day_docker, day_stars: $day_stars, day_forks: $day_forks,
              day_views: $day_views, day_clones: $day_clones,
              last_milestone: $last_milestone}' > metrics.json

          echo "Saved metrics:"
          cat metrics.json

      - name: Save metrics cache
        uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4
        with:
          path: metrics.json
          key: project-metrics-${{ github.run_id }}
