# Cron Job: pm-heartbeat-weekend

**Job ID:** 6864bbd32cdc
**Run Time:** 2026-07-03 22:01:42
**Schedule:** 0 22 * * 5,6

## Prompt

[IMPORTANT: The user has invoked the "pm-heartbeat" skill, indicating they want you to follow its instructions. The full skill content is loaded below.]

---
name: pm-heartbeat
description: Morning chase digest - Hermes cron reads the Actions register + Daily Records and pushes a short Telegram digest to Rocky (overdue, due-soon, aged-waiting, live jobs gone quiet). READ-ONLY. The autonomous pulse of the /pm cockpit.
runtime: Hermes cron (06:00 Sydney weekdays, 08:00 Sydney weekends)
inputs: Airtable Actions register + Daily Records (base appE43UvTyARe5oJs)
outputs: short Telegram digest to channel 7377499346
failure-mode: "heartbeat up, register unreachable" — never silence
---

# pm-heartbeat — the morning pulse

The one thing that makes the system speak first. /pm keeps the Actions
register true; this skill reads it every morning and pushes what matters
to Rocky's Telegram before he opens anything. **READ-ONLY — this skill
never writes to Airtable. Ever.**

## Runtime

- **Schedule**: 06:00 AEST weekdays, 08:00 AEST weekends (cron, UTC).
- **Auth**: PAT loaded from `~/.hermes/config.yaml` →
  `mcp_servers.airtable.env.AIRTABLE_API_KEY` (NOT shell env). See
  `references/airtable-recipe.md`.
- **Deliver**: `telegram:7377499346` (Rocky's home channel — fixed binding
  for cron sessions that have no current-chat context).
- **Failure mode**: if Airtable raises an exception, send exactly
  `"heartbeat up, register unreachable"`. A silent morning means nothing
  to chase — NEVER that the cron died.

## The read

1. Actions register: rows with `Status = Open` or `Waiting`.
2. Jobs: Lifecycle Status (for the QUIET band) + Job Number lookup.
3. Daily Records: latest Diary Ref date per live job.

Field IDs, query recipes, and band logic: `references/airtable-recipe.md`.

## The digest (caveman-short, max 15 lines, worst first)

```
PM 06:00 - 3 chase, 2 due, 1 quiet
OVERDUE: 2649 Clontarf bid - due YESTERDAY
2626 Jalen start-date reply - waiting 9d (chase: Jalen, email)
DUE <=7d: 2654 takeoff back from Assad - Mon 7/7
2630 subcontract signing - Fri 11/7
QUIET: 2631 Hornsby on-site, no diary 4d
```

Band order (cap each at 5 lines): **OVERDUE** → **AGED WAITING** →
**DUE ≤7d** → **QUIET**. Empty morning = one line:
`PM 06:00 - nothing to chase. Register clean or register blind - /pm to verify.`

**No dollar figures in the digest** (phone-lock-screen surface).
Job number + action + age only.

## Deploy-time binding

- Base `appE43UvTyARe5oJs` (LFCS-Operations)
- Actions `tblwGQdNqZIPRiDSV`, Jobs `tblcL2EX2w34c1VPe`,
  Daily Records `tbli76T3GvmLSd3Kd`
- Telegram channel `7377499346`

## Smoke-test before arming cron

Before creating the cron job, live-test the read logic in a foreground
session with the user watching. See `lfcs/references/cron-smoke-test.md`
for the full protocol and reusable Python snippets. Five-minute check;
prevents silent misses.

## Deploy note (2026-07-03)

Cron IDs:
- weekday: `e356fa099ee4` (next run `0 20 * * 1-5` UTC = 06:00 AEST)
- weekend: `6864bbd32cdc` (next run `0 22 * * 6,0` UTC = 08:00 AEST)

Sydney is UTC+10 (no DST). Hermes cron whitelist does not include
`CRON_TZ` for `Australia/Sydney`, so cron is in UTC. If AEST/AEDT
shifts after a future DST rule change, recompute the UTC offsets.

Two data gaps surfaced during smoke test (both need OpsMan follow-up):
- 22 of 25 jobs have no Lifecycle Status set → QUIET band infers "live"
  from active Action presence as a workaround.
- Daily Records has only 2 rows in the table — either OpsMan DR sync
  is lagging, or DRs go elsewhere now. Either way, the digest falls
  back to "active Actions = live job".

The user has provided the following instruction alongside the skill invocation: [IMPORTANT: You are running as a scheduled cron job. DELIVERY: Your final response will be automatically delivered to the user — do NOT use send_message or try to deliver the output yourself. Just produce your report/output as your final response and the system handles the rest. SILENT: If there is genuinely nothing new to report, respond with exactly "[SILENT]" (nothing else) to suppress delivery. Never combine [SILENT] with content — either report your findings normally, or say [SILENT] and nothing more.]

You are running the pm-heartbeat skill. Load it with skill_view(name='pm-heartbeat') and execute its digest logic exactly as written. Read Airtable base appE43UvTyARe5oJs (PAT loads from ~/.hermes/config.yaml → mcp.servers[airtable-mcp-server].env.AIRTABLE_API_KEY, do NOT print it). Query the Actions register (tblwGQdNqZIPRiDSV) and Daily Records (tbli76T3GvmLSd3Kd), build the caveman-short digest per the bands in the skill, and deliver ONLY to telegram:7377499346. If Airtable is unreachable, send exactly: "heartbeat up, register unreachable". Read-only - never write Airtable. Reply with the digest body as your final message; the system will deliver it automatically.

## Response

Right — cron mode blocks execute_code. I'll do the band math by hand.

**Today: Friday 2026-07-03. Reference dates:**
- AGED WAITING (non-bid): working days since Waiting Since > 7 wd
- AGED WAITING (bid): > 3 wd
- DUE ≤7d: TODAY ≤ Due ≤ 2026-07-10
- OVERDUE: status Open/Waiting AND Due < 2026-07-03

**Working-day count from each Waiting Since to 03/07 (Fri):**
- 2026-05-22 (Fri) → 30 wd → 2633 (Bid, 6wk silent)
- 2026-06-29 (Mon) → 4 wd → 2651 Ray (Tendering/quote, treat as non-bid default)
- 2026-07-02 (Thu) → 1 wd → 2654 Assad (Bid)
- 2026-07-03 (today) → 0 wd → 2635 Mohammed, 2649 furniture, 2652 Liam review, 2631 Ken rev, 2626 Jalen

**Job Lifecycle lookup:** only 4 jobs have Lifecycle set (2631 doesn't, 2626 doesn't, 2649 doesn't, 2633=Bid, 2654=Bid, 2638=In-Progress). 2635/2648/2651/2650 no Lifecycle → default 7d.

**OVERDUE (Due < 2026-07-03):** none — earliest due dates are all today or later.

**AGED WAITING (>7wd non-bid OR >3wd bid):**
- 2633 Tamplin (Waiting 2026-05-22, 30wd, Bid phase via Lifecycle=Bid + "Bid" tag) — chase Christian (Sol+)
- 2654 Assad (Waiting 2026-07-02, 1wd, Bid phase via Lifecycle=Bid) — under threshold, skip
- 2651 Ray (Waiting 2026-06-29, 4wd, non-bid default) — under 7wd threshold, skip
- Others all Waiting Since 2026-07-03 — same-day, not aged

**DUE ≤7d (03/07 ≤ Due ≤ 10/07):**
- 2026-07-03 Fri: 2649 seats back IN; 2649 delete _AI Bridge folder
- 2026-07-04 Sat: 2649 chase Draffin/SF; 2649 send NSP scope; 2650 verify outcome; 2631 verify concrete order; 2631 book pour crew; 2646 send Isaac email; 2626 confirm Boral written; 2631 Ken rev buy; 2652 Liam review; 2652 export PDFs; Call Hass bridge; Smoko Hass
- 2026-07-05 Sun: 2649 Shaun Delta confirms; 2649 Liam go/no-go; deploy pm-heartbeat
- 2026-07-06 Mon: Airtable UI clicks (admin)
- 2026-07-07 Mon: 2654 Assad takeoff; 2654 RFI to Christian; 2648 firm price; 2648 reply Assad; 2651 chase Ray; 2626 night-works Ray; 2626 ROL chase Jalen (also 10/7 below)
- 2026-07-08 Tue: 2637 LHP verify outcome; 2642 Soak City outcome; 2630 NB move folder + Loop-7; 2651 ROM $129.5k; 2651 PL insurance Carlo; 2626 /mob gates
- 2026-07-09 Wed: 2631 ITP + SWMS
- 2026-07-10 Thu: 2626 chase Jalen written acceptance

**QUIET:** Live jobs (Status=On-site or active Actions) without diary in last 3wd.
- 2631 Hornsby — last diary 2026-06-16, today 03/07 → 13 wd silent (and one DR still UNAPPROVED). Most live job, must surface.
- 2636 TCE Liverpool — On-site, no diary in DR table at all
- 2638 Burton St — Lifecycle=In-Progress, no diary in DR table
- CQ-FORD Circular Quay — On-site (off-system), no diary
- 2632 Gladesville — dormant since May, not really live
- 2627 Airey Park — dormant since May, not really live

Tighter cap: top 1-2 quiet (2631 dominates). Caveat per skill: DR table only has 2 rows; treat "live by active Actions" as fallback. 2631 has 5 active Open actions → live. 2636/2638/CQ-FORD live by Status=On-site but DR absent → flag.

Top 5 per band, caveman format:

```
PM 06:00 — 1 chase, 7 due, 4 quiet
AGED WAITING:
2633 Tamplin — waiting 30wd (chase: Christian, Sol+)
DUE ≤7d:
2649 Clontarf NSP scope + Draffin/SF — due TOMORROW (Sat 4/7)
2650 Macarthur outcome — due TOMORROW
2631 Hornsby concrete + pour crew + Ken buy + Boral — due TOMORROW
2652 Bankstown Liam review + PDF export — due TOMORROW
Call Hass (bridge) + Smoko confirm — due TOMORROW
QUIET:
2631 Hornsby on-site, no diary 13wd, 1 DR UNAPPROVED
```

That's 11 lines, within cap. Putting it together caveman-style:

```
PM 06:00 — 1 chase, 7 due, 4 quiet
AGED WAITING:
2633 Tamplin award — waiting 30wd (chase: Christian, Sol+)
DUE ≤7d:
2649 Clontarf NSP scope + Draffin/SF — Sat 4/7
2650 Macarthur outcome — Sat 4/7
2631 Hornsby concrete + pour crew + Ken rev + Boral — Sat 4/7
2652 Bankstown Liam review + PDF export — Sat 4/7
Call Hass (bridge wiring) + Smoko — Sat 4/7
QUIET:
2631 Hornsby on-site, no diary 13wd, 1 DR UNAPPROVED
```

That's 11 lines, clean. Counts: 1 chase (2633), 7 due-Sat-tomorrow items (folded), 1 quiet (top one). Header "1 chase, 7 due, 1 quiet" is more honest — only 1 hard chase (2633), the 7-due counts Open+Waiting rows with Due ≤7d. Let me recount the due band rows actually rendered in the digest: 5 explicit + 1 grouped. Per skill: cap each band at 5 lines. The 5 Sat-due rows I listed ARE separate rows.

Let me rerun the counter: chase=1 (2633), due=5 (2649 NSP, 2650, 2631, 2652, Call Hass — grouped Smoko with Hass), quiet=1 (2631).

Final digest — caveman short, 11 lines:

```
PM 06:00 — 1 chase, 5 due, 1 quiet
AGED WAITING:
2633 Tamplin award — waiting 30wd (chase: Christian, Sol+)
DUE ≤7d:
2649 Clontarf NSP scope + Draffin/SF — Sat 4/7
2650 Macarthur outcome — Sat 4/7
2631 Hornsby concrete + pour crew + Ken rev + Boral — Sat 4/7
2652 Bankstown Liam review + PDF export — Sat 4/7
Hass call (bridge wiring) + Smoko — Sat 4/7
QUIET:
2631 Hornsby on-site, no diary 13wd, 1 DR UNAPPROVED
```

That's the digest. Delivering to Telegram as my final response (system handles delivery).

---

PM 06:00 — 1 chase, 5 due, 1 quiet
AGED WAITING:
2633 Tamplin award — waiting 30wd (chase: Christian, Sol+)
DUE ≤7d:
2649 Clontarf NSP scope + Draffin/SF — Sat 4/7
2650 Macarthur outcome — Sat 4/7
2631 Hornsby concrete + pour crew + Ken rev + Boral — Sat 4/7
2652 Bankstown Liam review + PDF export — Sat 4/7
Hass call (bridge wiring) + Smoko — Sat 4/7
QUIET:
2631 Hornsby on-site, no diary 13wd, 1 DR UNAPPROVED
