# J2 Client Stage Dashboard — Setup Guide

**Owner:** Jimmie | **Time required:** ~5 minutes | **Frequency:** one-time

The Sheet has been created and seeded with today's snapshot. Follow these steps once and the dashboard will auto-refresh every 15 minutes from Atlas.

## Sheet URL

**https://docs.google.com/spreadsheets/d/152S1dwZb7m3ODPG3Fo2IfYXV5B3EnUxtkw8ZPAn0W9M/edit**

Open it. The first tab shows a snapshot of all 30 active clients. After setup, the layout will reorganize into Exceptions + Full Roster sections.

## Step 1 — Install the Apps Script (~2 min)

1. In the Sheet, click **Extensions → Apps Script**. A new tab opens with the Apps Script editor.
2. You'll see a default `Code.gs` file. **Select all** the placeholder code and delete it.
3. Open `C:\PKA\Team Inbox\Cord\atlas-sheets-sync.gs` in any text editor, **copy the entire contents**, and paste into the empty `Code.gs`.
4. Click the **save icon** (or Ctrl+S). Name the project something like "J2 Client Stage Sync."
5. Keep this Apps Script tab open for the next step.

## Step 2 — Set the bearer token (~1 min)

1. In the Apps Script editor, click the **gear icon** (Project Settings) in the left sidebar.
2. Scroll down to **Script Properties**. Click **Add script property**.
3. **Property name:** `ATLAS_BEARER`
4. **Value:** Get this from `C:\PKA\Atlas\app\.env.local` — the line that starts with `ATLAS_INGEST_TOKEN=`. Copy everything after the `=` sign (no quotes).
5. Click **Save script properties**.

## Step 3 — Install the 15-minute trigger (~1 min)

1. Go back to the **Sheet tab in your browser** (not the Apps Script editor).
2. **Reload the page** (F5) — this loads the new "Atlas" menu.
3. You should see a new menu item at the top: **Atlas**. Click it.
4. Click **Install 15-min refresh**.
5. **First-time only:** Google will pop up an authorization prompt asking permission to run external API calls + edit the Sheet. Click through:
   - "Review permissions" → pick your Google account
   - "Advanced" → "Go to J2 Client Stage Sync (unsafe)" — yes, this is normal for personal Apps Scripts. It's "unsafe" only in the sense that Google hasn't formally verified the project; YOU wrote/own it.
   - Allow.
6. You should see "15-min refresh installed" confirmation. Done.

## Step 4 — Trigger the first refresh

1. **Atlas menu → Refresh now**. The Sheet repopulates with the live data.
2. Verify:
   - Top of Sheet shows "Last refreshed: <today's timestamp>"
   - Section "🚨 EXCEPTIONS" — should say "none — all clear" today
   - Section "📋 ALL ACTIVE CLIENTS (30)" — full roster sorted by stage priority

## Step 5 (optional but recommended) — Add conditional formatting

This is one-time visual polish so stages are color-coded for at-a-glance scanning.

**Apply via Format → Conditional formatting → Add new rule:**

| Range | Rule | Color |
|-------|------|-------|
| Stage column (B) | Text is exactly `paused_internal` | Bg red, white text — most urgent |
| Stage column (B) | Text is exactly `paused_client` | Bg amber/yellow |
| Stage column (B) | Text is exactly `eom_close` | Bg orange |
| Stage column (B) | Text is exactly `eom_review` | Bg orange-light |
| Stage column (B) | Text is exactly `cleanup` | Bg blue (engagement work) |
| Stage column (B) | Text is exactly `onboarding` | Bg teal |
| Stage column (B) | Text is exactly `offboarding` | Bg gray |
| Stage column (B) | Text is exactly `weekly` | Bg green-light (steady state) |
| Flags column (D) | Text contains `stuck` | Bg red bold |
| Flags column (D) | Text contains `chronic_late` | Bg red bold |
| Flags column (D) | Text contains `client_blocking` | Bg amber |
| Days column (C) | Color scale → Green to red, min 0, max 30 | (heatmap) |

You only need to set these once; they persist on the Sheet itself (not in the script), so the auto-refresh doesn't clobber them.

## How to use the dashboard going forward

- **Open the Sheet whenever** — it'll refresh every 15 min automatically
- **Force a refresh** — Atlas menu → Refresh now
- **Check the Exceptions section first** — if it's empty, the day's clear. If something's there, click into the client row for context.
- **On mobile** — Google Sheets app reads it fine. The first column (Client) is wider so names don't truncate.

## Things to know

- **The Sheet is auto-managed.** Don't manually edit data — it'll get overwritten on the next refresh. Stage changes go through Atlas (Forge's REST API) or eventually through Riv's `/stage` Slack command in Phase 2.
- **Adding more columns** — edit the `HEADERS` array and the `clientRow_` function in the Apps Script. Re-save and Refresh.
- **The script polls Atlas via the Tailscale Funnel URL.** If Minas reboots and Tailscale doesn't auto-restart, refreshes will fail silently. (Riv's setup notes cover the daemon auto-start.)
- **Bearer token is in Script Properties** — not in the script source. So sharing the Sheet doesn't leak the token. To share the Sheet with someone else (a bookkeeper), grant them edit access via Share — the script still runs under your account, your token stays private.

## Troubleshooting

| Symptom | Fix |
|---------|-----|
| Refresh shows "ATLAS_BEARER not set" error | Go back to Step 2, verify the property name is exactly `ATLAS_BEARER` and the value has no extra spaces. |
| Refresh shows "Atlas API returned 401" | Token is wrong/expired. Re-check `app/.env.local` and re-paste. |
| Refresh shows "Atlas API returned 5xx" or "DNS_FAIL" | Atlas is down on Minas, or the Tailscale Funnel is offline. Open `https://minas-tirith.taildacccb.ts.net/api/clients` from your browser with the bearer to confirm reach — fix the root cause first. |
| Sheet says it's refreshed but data looks old | Manual refresh: Atlas menu → Refresh now. The 15-min trigger fires on Google's schedule and can drift slightly. |
| Atlas menu doesn't appear | Reload the Sheet (F5). The `onOpen` function only runs at Sheet open time. |

## Phase 2 callouts (for later)

- Once Riv issues per-actor tokens in Phase 2, you can swap your bearer to a Coefficient-or-Apps-Script-specific token instead of the legacy shared token.
- When agents start updating client state, their `agent:tally` / `agent:echo` actor IDs will show up in the "Actor" column with the 🤖 icon — that's the visual signal you can trust them.
- Cord can extend the script to write a second tab ("Stage History") or expose specific filters (e.g., "Just my fractional CFO clients") if you find you want them.
