# Cron Job: morning-digest

**Job ID:** 5fd07b6b83f2
**Run Time:** 2026-06-15 05:00:47
**Schedule:** 0 5 * * *

## Prompt

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

---
name: lfcs
description: LF Construction Services site operations — job tracking, correspondence, inspections, RFIs, dockets, EOD summaries, and pre-pour checks. All skills share the same job folder structure under lfcs.jobs_root.
version: 1.0.0
category: lfcs
tags: [lfcs, construction, site-operations, job-tracking, admin]
metadata:
  hermes:
    requires_toolsets: [terminal]
    config:
      - key: lfcs.jobs_root
        description: Root directory holding active LFCS job folders. Must be the Jobs/ parent so both Ongoing Jobs/ and Upcoming/ are visible to the agent.
        default: "C:\\Users\\mclou\\rateright-growth-deploy\\HQ-Vault\\11_OpsMan_LFCS\\Operations\\Jobs"
        note: "Laptop path — use SSH to mclou@laptop-cd8df7fs to read. VPS vault at /home/ccuser/rateright-growth/HQ-Vault/11_OpsMan_LFCS/Operations/Jobs mirrors this but may lag."
      - key: lfcs.vps_jobs_root
        description: VPS mirror of the job vault. Use when laptop is unreachable.
        default: "/home/ccuser/rateright-growth/HQ-Vault/11_OpsMan_LFCS/Operations/Jobs"
---

# LF Construction Services — Site Operations

All LFCS skills share the same job folder conventions. Every path below is relative to `<job>` = `<lfcs.jobs_root>/<Job>/`.

## Standard Job Folder Structure

```
<job>/
  00-brief.md                          ← contract price, type, start_date, target_completion
  _strategy.md                         ← profit-lever flags (optional)
  Job-Tracker.md                       ← hours table, materials table, costs-to-date
  Job Tracker - <job>.xlsx             ← alternative Excel format; check both if .md absent
  04 - Programme and Scheduling/
    programme-24wk.md                  ← 24-week programme, current week number
  06 - Correspondence/
    06a - Emails/                      ← drafted emails (EML-YYYY-MM-DD-description.md)
    06b - RFIs/_REGISTER.md            ← RFI register (RFI-A1, VO-001 numbering)
  05 - Safety and Compliance/
      05b - JSAs/_toolbox-talks.md      ← TBT running log (weekly entry pattern)
      05c - Inductions and Tickets/_inductions-checklist.md  ← crew tickets, HammerTech expiry dates
    06 - Correspondence/
      06a - Emails/                      ← drafted emails (EML-YYYY-MM-DD-description.md)
      06b - RFIs/_REGISTER.md            ← RFI register (RFI-A1, VO-001 numbering)
    07 - Site Documents/
      _inspection-log.md              ← inspection register
    07d - Daily Diary/                  ← directory containing `YYYY-MM-DD.md` entry files
  08 - Variations/_REGISTER.md         ← variation register (VO-001 upwards, status + client approval)
```

## Standard Job Fields (00-brief.md)

| Field | Values | Notes |
|-------|--------|-------|
| `type:` | `price`, `dayworks`, `hybrid` | Determines docket/photo requirements |
| `contract_price:` | dollars | Used for budget variance in job-tracker |
| `start_date:` | YYYY-MM-DD | Jobs in Upcoming/ with start_date within ±7 days are active |
| `target_completion:` | date | |

## Skill Map

| Skill | When to use | Key file |
|-------|-------------|---------|
| `lfcs-job-tracker` | "where's [job] at", "am I over budget" | Job-Tracker.md |
| `lfcs-eod-summary` | "EOD summary", "wrap up", 17:00 cron | Daily Diary + Job-Tracker |
| `lfcs-docket-check` | "docket check", EOD cron auto-check | 07a - Dockets/ |
| `lfcs-pre-pour-check` | "pre-pour check", "ready to pour" | 07c - Inspection Records/ |
| `lfcs-rfi-status` | "RFI status", "open RFIs", "what to chase" | 06b - RFIs/_REGISTER.md |
| `lfcs-client-correspondence` | Formal emails, letters, RFIs, variations | 06 - Correspondence/ |
| `lfcs-tool-tracker` | Tool/vehicle register, WhatsApp check-in/out | Google Sheets + Transfer Log |
| `timesheet` | "do my timesheet", weekly payroll submission | Job-Tracker.md hours table |
| `lfcs-weekly-snapshot` | Monday morning programme update, float ledger, RFI/VO/TBT/induction status | programme-24wk.md, _REGISTER.md files, _toolbox-talks.md, _inductions-checklist.md |
| `lfcs-am-lookahead` | AM morning briefing, 06:00 cron, today's plan + blockers + RFIs + deliveries | programme-24wk.md, RFI register, subbie schedule, Job-Tracker |

## Active Job Identification

Check BOTH `<lfcs.jobs_root>/Ongoing Jobs/` AND `<lfcs.jobs_root>/Upcoming/`.
- A job in `Upcoming/` that has started site work is still active — do not skip it.
- Jobs with `start_date` within ±7 days of today are active.
- If there are multiple active jobs, handle each separately.

## Finding Full Job Data When Ongoing Jobs/ Is a Skeleton

**Failure mode:** A job shows in `Ongoing Jobs/` as active (site work started), but its folder there contains only skeleton files (`00-brief.md`, `Job-Tracker.md`, minimal subfolders). The rich data — full programme, RFI register, subbie schedule, variation register — still lives in the `Upcoming/` folder for that same job. This commonly happens when the job was set up for tender/pre-start but the move to "ongoing" was never completed by creating a full folder copy.

**Diagnosis checklist (run in order):**
1. `ls <job>/04 - Programme and Scheduling/` — if `programme-24wk.md` is empty (0 lines) or missing, the real data is elsewhere.
2. `ls <job>/06 - Correspondence/06b - RFIs/` — if `_REGISTER.md` has no rows or shows only a header, the real register is in `Upcoming/`.
3. `ls <job>/10 - Subcontractors/` — if the directory doesn't exist, the subbie schedule is in `Upcoming/`.

**Resolution:** When the Ongoing Jobs/ skeleton is empty, read the corresponding file from `<lfcs.jobs_root>/Upcoming/<job name>/`. Compare by checking `total_lines` — the non-empty file is the authoritative one. Log the discrepancy in today's diary entry: note which files had to be pulled from Upcoming/ vs Ongoing Jobs/.

**Why this matters:** The AM look-ahead and EOD summary will silently produce wrong outputs (empty programme, no RFI data) if they read the skeleton instead of the full folder. Always verify file content size before using a path — don't assume the path existence means the data is there.

## Handling Untracked Jobs

- RFI numbering: `RFI-A1`, `RFI-A3`, etc. (Cat A = mobilisation blockers, Cat B = scope, Cat C = commercial)
- Variation numbering: `VO-001` upwards
- Email drafts: `EML-YYYY-MM-DD-short-description.md`
- Diary entries: `YYYY-MM-DD.md`
- Docket files: `YYYY-MM-DD-signed.*` (strict pattern — no false positives)

## Handling Untracked Jobs (Job Referenced But Not in Vault)

When Rocky mentions a job that doesn't appear in any search result:

**Failure mode:** Job exists in Rocky's mental model and on his local machine or Google Drive, but has no VPS vault folder and no Airtable record. Searching by location name, client name, or job number returns nothing.

**Immediate protocol:**
1. Acknowledge the gap — "I can't find [job name] in the vault yet"
2. Ask Rocky for the **client name** and **job number** (even partial helps — e.g., "TCE job", "Liverpool job")
3. Check Rocky's **local machine** at `C:\Users\mclou\lfcs-inbound-artefacts\` via SSH (`ssh -i /root/.ssh/id_hermes_ed25519 mclou@laptop-cd8df7fs`)
4. If artefacts found locally, create the vault folder structure and 00-brief.md before proceeding
5. If no artefacts found locally, create a skeleton job folder with `00-brief.md` (type, client, notes) and flag for Rocky to supply artefacts
6. Never assume the job doesn't exist — it may simply not have been synced to the VPS vault yet

**Never say:** "That job doesn't exist." Say: "I don't have it in the vault yet — what's the client name?"

## Shared Pitfalls

Rocky's accent is regularly mangled by the STT pipeline into Welsh-like character sequences. Rocky denies having a Welsh keyboard — the issue is in the transcription chain, not his device.

**Signs of garbled output:** strings like "Ysio'r Eid", "yn yimportuthiad", "Grifodd yn Gwyrddai", "Darn, itgen un anen ing nes i sfywad".

**When you see garbled output:**
- Do NOT say "Welsh keyboard" — it frustrates him
- Say: "Can you rephrase that?"
- Capture what you understood from context shape, confirm before logging
- If genuinely unintelligible: "I didn't catch that — try again"
- Log the corrected content once confirmed

**When Rocky uses a name that doesn't match the vault:**
- He may use a short name, suburb alias, or contact name instead of the official job title
- This is NOT transcription garble — it's him using a different identifier
- Failure mode: search returns nothing even though the job exists
- Workaround: check the Ongoing Jobs directory by scanning all names, or ask for clarification
- Known cases:
  - "Gleeb" = "Solutions Plus - Jubilee Oval Upgrade" (Glebe is the suburb, not the job name)
  - Job names in vault may not match what contacts call them
- When in doubt: list the Ongoing Jobs directory and confirm against what he described

**This applies to all LFCS skills that receive voice input.** The workaround goes in the skill that handles the input stream, not in every downstream skill.

## Shared Pitfalls

1. Job-Tracker.md tables sometimes use `$` and `,` — strip both before parsing numbers.
2. For `dayworks`/`hybrid` jobs: dockets required. For `price` jobs: no docket needed.
3. Use Australia/Sydney timezone for all date comparisons.
4. If a field can't be parsed, show raw cell with `(see file)` rather than guessing.
5. Never auto-send client correspondence without Hasibul review for external emails.
- **VPS path for job folders:** On the VPS (hermes-gateway), job folders live at `/home/ccuser/opsman-work/Jobs/Ongoing Jobs/` (note the space in "Ongoing Jobs"). This is the `lfcs.vps_jobs_root` location. Do not assume the path from `lfcs.jobs_root` (which defaults to a laptop path). Always `ls /home/ccuser/opsman-work/Jobs/` first to discover the correct subfolder name.
- **Always `ls` a directory before reading files inside it.** On the VPS vault, `07d - Daily Diary/` and `07a - Dockets/` are **directories** (not files). Attempting to `read_file` on a directory path will fail. Use `ls <dir>/` to confirm contents, then read the specific file.
- **Job folder names have spaces:** "Ongoing Jobs" and "01-2275 - HPE Field of Play Hornsby" both have spaces. Always quote paths in shell commands when they contain spaces.
- **Job-Tracker.md is empty (zero rows):** This is a real finding — report "no entries logged today" explicitly. Do not skip the hours/materials section or imply a diary exists when it doesn't.
- **Docket: report MISSING explicitly.** Do not say "not yet" or "not received". Use "❌ MISSING" and state "⚠️ No signed docket = no invoice line." This is always CRITICAL — use red/bold text in Telegram.

- [Cron Job Setup — lfcs](./references/cron-jobs-lfcs.md) — full registry of LFCS cron jobs with job IDs, schedule, status, deliver target. Update this file when jobs are created/paused/resumed/deleted so future sessions have the current state without needing to call cronjob list.
- [VPS Cron Script Traps](./references/vps-cron-traps.md) — hermes-health-snapshot.sh fixes: flock guard, safe .env parse (no set -a), correct MiniMax endpoint (api.minimax.io), pm2 jlist vs pm2 list hang, heredoc mv-before-chown pattern. Also: Google OAuth token expiry check and re-auth command. Dashboard auth verify pattern (curl -I → 302 /login). Cron job registry (5 jobs, IDs, statuses, next runs).
- [Weekly Programme Snapshot](./references/weekly-programme-snapshot.md) — Monday cron: week N, float, work due, claims due, RFIs to chase, VOs pending, TBT due, induction expiry check

- [Local Artefact Recovery](./references/local-artefact-recovery.md) — SSH retrieval from laptop when job not in vault
- [TCE Liverpool Recovery](./references/tce-liverpool-recovery.md) — worked example of laptop-closed gap protocol, identifier mismatch, questions to ask
- [EOD Google Doc Report](./references/eod-google-doc-report.md) — compile daily report with photos, upload to Google Drive, insert inline images, share link
- [Stormwater Pit Rates](./references/stormwater-pit-rates.md) — benchmark breakdown, TCE Livingstone actuals, 2626 Mack Civil, 2626 Hasibul full-scope ($107k), adjustment rules
- [Site Visit Prep](./references/site-visit-prep.md) — address, contact, key risks, questions to ask. Load before Rocky heads to a job site.
- [Docket Check](./references/docket-check.md) — signed docket verification, at-risk revenue flags
- [EOD Summary](./references/eod-summary.md) — end-of-day report procedure and format
- [Job Tracker](./references/job-tracker.md) — mid-day status, budget variance
- [Pre-Pour Checklist](./references/pre-pour-check.md) — 8-item concrete pour walkthrough
- [Concrete Pricing](./references/concrete-pricing.md) — 40MPa/80mm slump/20mm agg specs, Sydney readymix rates, short load surcharge rules, plant call checklist
- [RFI Status](./references/rfi-status.md) — open RFI register, priority sorting, chase actions
- [Client Correspondence](./references/client-correspondence.md) — email/letter drafting templates, tone rules, filing
- [Tool & Equipment Tracker](./references/tool-tracker.md) — system design, WhatsApp workflow, Google Sheets schema
- [Weekly Timesheet](./references/timesheet.md) — hours compilation and submission workflow

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 Hermes, Rocky's site ops agent. Sydney timezone. Deliver a morning intelligence digest to Rocky's Telegram (chat 7377499346) via send_message.

Gmail + Google Calendar only (do NOT check LFCS email — that's handled separately). Use the Google OAuth token at ~/.hermes/google_token.json. If the token is expired or invalid, send this message instead: "⚠️ Morning digest unavailable — Google OAuth token needs re-auth. Run: python3 /usr/local/lib/hermes-agent/setup.py --auth-url"

**Format — tight bullets, deadlines first, no filler:**

```
🗓 [DAY] Morning Digest — [DD MMM]

## Deadlines today
(bullets)

## Action items
(bullets)

## Gmail — top 5 unread
(bullets: sender, subject, snippet, time)

## Calendar — next 7 days
(bullets: time, event, location if any)

## Waiting on (3+ days, from open follow-ups in memory)
(read ~/.hermes/memories/MEMORY.md, find open follow-ups, include items with no reply/activity in 3+ days — if none, write "Nothing stale")
```

Scopes to request: gmail.readonly + calendar (already in token). Max 5 email items. Keep snippets to 1 line each. If token is valid, actually fetch real data — do not fabricate emails or calendar events.

## Response

⚠️ Morning digest unavailable — Google OAuth token needs re-auth. Run: python3 /usr/local/lib/hermes-agent/setup.py --auth-url
