---
name: site-lookahead
description: AM look-ahead and EOD site summary for LFCS. Reads programme, RFIs, subbie schedule, and job tracker; outputs a tight 4-6 line Telegram summary. Runs as a daily cron job.
category: ops
tags: [lfcs, daily, telegram, opsman]
recurrence: daily cron
---

# Site Look-Ahead / EOD Summary Skill

Covers two cron-triggered outputs:
1. **AM look-ahead** — 6am daily, before Rocky's day starts
2. **EOD summary** — end of day, after site finishes

Both use the same file inputs. The distinction is in the output format and focus: AM = what's planned; EOD = what actually happened + what's missing.

## Trigger
- Scheduled cron job (daily ~6am)
- Rocky asks for "AM look-ahead", "morning summary", or "what's happening today"

## Input files (in order)

Always read from the **Ongoing Jobs** folder first. If files are sparse/empty, check the **Upcoming Jobs** folder — a job that just mobilised often has richer docs there still.

| File | Purpose |
|---|---|
| `00-brief.md` | Job type, client, start date, contract value |
| `04 - Programme and Scheduling/programme-24wk.md` | Today's week, tasks, pour areas |
| `06 - Correspondence/06b - RFIs/_REGISTER.md` | Cat A blockers still Pending |
| `10 - Subcontractors/_subbie-schedule.md` | Subbies booked for today |
| `Job-Tracker.md` | Planned activity, materials, hours |

**Note:** The subfolder is `Programme and Scheduling` (with "and"), not `Programme`. Watch for this when doing path completions.

## Two-folder lookup rule

This is not optional — it is the first check before any job is reported on.

**Canonical decision tree (always run this first):**

```
1. List /home/ccuser/opsman-work/Jobs/Ongoing Jobs/  → jobs present
2. List /home/ccuser/opsman-work/Jobs/Upcoming/       → jobs present

For EACH job that appears in BOTH Ongoing AND Upcoming:
   → Compare programme-24wk.md line count in both copies
   → If Ongoing < 10 lines AND Upcoming ≥ 10 lines: USE UPCOMING COPY
   → Apply the same check to RFI _REGISTER.md and Job-Tracker.md
   → Log the cross-reference in the vault diary entry
```

**Critical note on folder naming — do NOT require exact name match:**
- `Ongoing Jobs/` folder name: typically short form — e.g. `01-2275 - HPE Field of Play Hornsby`
- `Upcoming/` folder name: typically includes client — e.g. `01-2275 - Solutions Plus - HPE Field of Play Hornsby`
- These never match exactly. Use `ls` + grep/partial match, not exact string comparison.
- Hornsby confirmed: `ls "/home/ccuser/opsman-work/Jobs/Ongoing Jobs/"` ≠ `ls "/home/ccuser/opsman-work/Jobs/Upcoming/"` — different names for the same job. Always list both, then match by job number (01-2275).

A job is considered "sparse" (and Upcoming should be checked) when:
- `programme-24wk.md` in Ongoing has **< 10 lines** (header + empty table = 4 lines)
- RFI `_REGISTER.md` in Ongoing has **< 10 lines** (header only = 4 lines)
- Job-Tracker hours block is **completely blank**

**Live example (2026-05-23):** Hornsby (01-2275) appears in both folders. Ongoing programme = 4 lines; Upcoming programme = 57 lines with full 24-week table. Without the cross-reference, the AM look-ahead would report "programme not yet loaded" and miss Wk 4 tasks, Claim 1 due 25 May, and 10 Cat A RFIs still Pending.

**Correct AM output (with cross-reference applied):**
```
Today (Wk 4, Sat): EA Zone 1 pour — 5 pours × 160 m²
Blockers: 10 Cat A RFIs Pending (sub-base handover, pump, batching plant, steel)
RFIs to chase: A1 sub-base overdue 15 May, A3 concrete pump, A4 batching plant
Deliveries: steel + EA zone prep expected
⚠️ Claim 1 due 25 May — CC to start drafting
```

### Two-folder lookup for dockets and diary

## Output: Telegram Summary

**Plain text only — no markdown, no tables.** Rocky reads on phone. .md format in Telegram is unreadable.

### AM format:
```
Today (Wk N, DOW): [one line — key task]
Blockers: [Cat A RFIs still Pending, or "none"]
RFIs to chase: [top 2-3 by critical path — RFI number + one-liner]
Deliveries: [confirmed deliveries, steel, materials — or "none booked"]
[CLAIM prep note if within 2 weeks of due date]
```

### EOD format (5–7 lines max):
```
[CRITICAL 🔴 if docket missing] — flag in red text, prepend "🔴 CRITICAL: "
Hours today: [total hours logged, or "none — confirm with Rocky"]
Photos saved: [count or "none"]
Docket: [photographed Y/N — if NO: "MISSING — no signed docket = no invoice line"]
Materials in: [anything delivered, or "none"]
Tomorrow: [next planned activity — confirm with Rocky]
Action required: [the one thing Rocky must do tomorrow]
```

**Rules for both:**
- Cat A RFIs are mobilisation blockers — always flag if any are still `Pending`
- Chase list (AM): max 3, prioritised by what is blocking today's work
- If nothing notable (AM): one line only
- **Never use bullets, tables, or markdown in Telegram output**
- Plain sentences, short — that's what fits in a phone message
- Docket status is CRITICAL for dayworks/hybrid jobs — missing docket = no invoice line, flag prominently

## Active job — dynamic, not hardcoded

The active job is whatever Rocky is on TODAY. Do NOT assume it's the last job from a previous session or a hardcoded reference. The job may be:

- **In the vault/Airtable** — check `opsman-work/Jobs/Ongoing Jobs/` and Airtable Jobs table
- **Not yet in the system** — new LFCS job, no folder, no Airtable record. Rocky will give the job name/contact. Create a pending job entry in `hermes-work/` and flag for Airtable sync later
- **On the shared Drive** — not yet synced to VPS vault

When Rocky says "we're on [job name]" — that IS the active job for the session, regardless of what's in the vault.

For new/off-system jobs, capture:
- Client name and contact
- Job type (dayworks / price / hybrid)
- Contract basis (verbal or written)
- Any RFIs, variations, or delay events

### Multiple active jobs in Ongoing Jobs

**Ongoing Jobs/ can contain more than one active job simultaneously.** Do not stop after finding the first job — list all jobs in the folder and decide which to report on using this priority:

1. Job with a diary entry for today's date (most reliable signal of active work today)
2. Job with the most recent diary entry (if no entry today yet)
3. Job that Rocky is physically on (if mentioned in any capture log)
4. If in doubt: report on the job with the earliest start_date in its `00-brief.md`

For AM look-ahead, run the drill-down on the highest-priority job. If multiple jobs are active, summarise the top one and note the others in one line: e.g. "Also active: Bingara Gorge (strip day 2)."

Example directory with two active jobs:
```
/Jobs/Ongoing Jobs/
  01-2275 - HPE Field of Play Hornsby/     ← Hornsby
  TBC - Solutions Plus - Bingara Gorge Homestead Park/  ← Bingara Gorge
```

Both may have `start_date` in the past. Both may have diary entries. Apply the priority rules above.

## EOD Daily Report — Google Docs output (when Google Workspace is connected)

Rocky requests EOD compile to a Google Doc. When Google Workspace OAuth is active:

1. Search Drive for existing job folder — `drive search "TCE Liverpool"` or similar
2. If no folder exists, create one: `GAPI drive create --name "TCE Liverpool - YYYY-MM-DD"`
3. Compile all captured entries (timestamps, delays, variations, crew hours, photos) into a plain text report
4. Create a Google Doc: `GAPI docs create --title "TCE Liverpool - Daily Report - YYYY-MM-DD" --body REPORT_TEXT`
5. Insert photos via Drive upload + link in doc, or embed inline if supported

**Report structure:**
```
TCE Liverpool — Daily Site Report
Date: YYYY-MM-DD
Crew: HH:MM–HH:MM (X hrs)

Timeline:
07:00 — event
etc.

Flags for variation/delay recovery:
- item
- item

Photos: N sent via Telegram
```

## Voice transcription — Australian accent workaround

**Naming mismatch is common.** The same job often has slightly different folder names in Ongoing vs Upcoming (e.g. "01-2275 - HPE Field of Play Hornsby" vs "01-2275 - Solutions Plus - HPE Field of Play Hornsby"). Do NOT require an exact name match — grep or partial match to find the equivalent Upcoming folder when Ongoing is sparse.

## Pitfalls

1. **Ongoing folder is sparse or EMPTY** — `ls` the job directory, not just the parent. The key test:
   - `ls "/home/ccuser/opsman-work/Jobs/Ongoing Jobs/01-2275 - HPE Field of Play Hornsby/"` — if this returns nothing (exit 2), the folder is EMPTY, not just sparse
   - EMPTY Ongoing + populated Upcoming = always use Upcoming for programme, RFIs, subbie schedule, job tracker
   - A completely empty Ongoing folder means the job was never properly set up in Ongoing — don't assume the data is elsewhere in Ongoing; go directly to Upcoming
2. **Empty programme table** — if `programme-24wk.md` has 0 rows (only header), the job has no loaded programme yet. Don't fabricate a plan — report "programme not yet loaded" in the AM summary. HOWEVER: if the header has a title line and the table is otherwise empty (3–4 lines total), check the **Upcoming** folder — the full programme may live there while the Ongoing copy is sparse. Hornsby (2026-05-19) is a live example: Ongoing version is 4 lines (header only); Upcoming version has 57 lines with full 24-week table including today's Week 4 row. Always cross-reference before concluding "no programme."
3. **Subbie schedule naming varies** — `_subbie-schedule.md` in the job root (e.g. `Jobs/Ongoing Jobs/TBC - Solutions Plus - Bingara Gorge Homestead Park/_subbie-schedule.md`) is the correct path, NOT inside `10 - Subcontractors/`. Check job root first.
2. **Programme table is empty** — flag in summary; job may not have a loaded programme yet.
7. **Empty programme file vs missing programme — distinguish these.** A file that exists at `<job>/04 - Programme and Scheduling/programme-24wk.md` with only 4 lines (header + empty table) is NOT a ghost job indicator — the programme has been loaded. The file just has no rows yet. A genuinely missing programme would have `exit code 2` from `ls`. The ghost-job signal (Ongoing <10 lines) only fires when the file is present but sparse — but if the Upcoming copy is equally sparse, the job may simply not have a programme committed yet (normal for pre-mobilisation). Only flag ghost-job when Ongoing is sparse AND Upcoming is also sparse or absent. See 2026-05-28 audit: Hornsby Ongoing programme has 4 lines (sparse) but the file exists at the correct path and is readable — not a ghost job.

10. **Ghost job — Variant B** (Upcoming but active): Job sits in `Upcoming/` but has a past `start_date`, HammerTech sent, RFIs logged, programme loaded. The job is contractually and operationally active but was never moved to `Ongoing Jobs/`. Check `Upcoming/<job>/` when `Ongoing Jobs/` yields nothing. Hornsby is in both — `Ongoing Jobs/01-2275 - HPE Field of Play Hornsby/` and `Upcoming/01-2275 - Solutions Plus - HPE Field of Play Hornsby/`. The Ongoing copy is sparse; the Upcoming copy may have richer docs. Always cross-reference both when in doubt.
4. **No RFI responses** — this is the normal state before mobilisation. 10 Cat A RFIs pending is expected. Only escalate if today's planned activity is blocked by a missing answer.
5. **Claim date proximity** — if within 2 weeks of a claim due date, remind Rocky that CC needs to start drafting. Claim dates: 25 May, 25 Jun, 25 Jul, 25 Aug, 25 Sep.
5. **Job Tracker empty + no diary = operational gap, flag it explicitly.** Five weeks into a hybrid contract (30 Apr start), zero hours logged and no diary entries is a serious administrative lapse. Claims will be indefensible without this data. In the AM look-ahead: report "Job Tracker empty — X weeks in, no hours captured" as a standalone finding. Do not soft-pedal it. In the EOD: same — if hours block is blank and no diary, write "⚠️ No capture for [N] weeks — confirm with Rocky."
6. **Programme week row vs actual site activity — distinguish these.** The programme row tells you what *should* be happening this week. It does NOT confirm work is actually happening on the ground. If the programme shows Week N but the Job Tracker has zero hours and no diary entries, the job is behind on capture, not necessarily behind on programme. Report the discrepancy explicitly: "Programme Wk N (EA Zone 3 pours) but no hours/diary captured this week."

## Logging

After AM look-ahead output:
8. **Docket directory missing vs empty — distinct cases** — for dayworks/hybrid jobs, `07a - Dockets/` may not exist at all OR it may exist but contain zero files. Both cases require the same flag but different root-cause language:
   - Exit code 2 (No such file or directory): directory was never created — scaffold is incomplete. Flag: "🔴 CRITICAL: Docket folder not created yet — no signed docket = no invoice line."
   - Exit code 0 (directory exists, empty): docket simply not photographed yet today. Flag: "🔴 CRITICAL: Docket missing — no signed docket = no invoice line."
9. **Two-folder lookup for dockets and diary** — both `Ongoing Jobs/<job>/07 - Site Documents/` AND `Upcoming/<job>/07 - Site Documents/` may have `07a/` and `07d/` subdirs. Check Ongoing first. If empty, check Upcoming. Jobs just starting often have richer docs still in Upcoming. The job may also still be in `Upcoming/` if it hasn't been formally promoted — do not assume the absence from `Ongoing Jobs/` means no active work.
10. **Job still in Upcoming/ — don't skip it** — some jobs show activity (programme loaded, RFIs sent, diary entries) even while sitting in `Upcoming/` because they haven't been formally moved to `Ongoing Jobs/`. Hornsby is a known example: active contract, HammerTech sent, but folder still in `Upcoming/`. Always check `Upcoming/<job>/` when `Ongoing Jobs/` yields nothing.
11. **Job Tracker may be .xlsx not .md** — if `Job Tracker - <job>.xlsx` exists (Excel format) instead of `Job-Tracker.md`, the hours data is in the `Labor Hours` sheet. Python 3.12 at `/usr/local/lib/python3.12/dist-packages/openpyxl` can read it; the cron venv uses Python 3.11 which lacks openpyxl. Use: `python3.12 -c "import openpyxl; ..."` to read it. Check sheet names first — typically `Labor Hours`, `Materials Receipts`, `Cost`. All-hours-zero in the Excel is a real finding, not a read error.
9. **Ghost job in Upcoming/** — job has a past `start_date`, a populated brief, HammerTech sent, RFIs logged, but zero diary entries after start_date and all-tracker-hours = 0. This means the job is contractually active but operations have not started capturing. Flag explicitly in EOD: "Job in system since [start_date] — no hours, diary, or docket captured. Confirm with Rocky." Do not suppress this — write to vault so CC can follow up.
10. **Ongoing is sparse — always cross-reference Upcoming.** When `programme-24wk.md` in Ongoing has <10 lines (header only, empty table), immediately check the Upcoming copy. Hornsby is a live case: Ongoing programme = 4 lines; Upcoming programme = 57 lines with full 24-week table. The job was recently moved but the Ongoing copy was not updated with the full programme. Same pattern applies to RFI register (Ongoing: 4 lines blank header; Upcoming: 160+ lines with all 33 RFIs). Always check Upcoming when Ongoing is sparse — do not assume the copy is complete.
11. **Subbie schedule — check two paths.** The file lives at `10 - Subcontractors/_subbie-schedule.md` (confirmed in both Ongoing and Upcoming Hornsby copies). The skill previously noted `_subbie-schedule.md` at job root — that path was wrong for Hornsby. Check both `<job>/_subbie-schedule.md` AND `<job>/10 - Subcontractors/_subbie-schedule.md`. The skill's earlier pitfall #3 (subbie schedule in job root) was inaccurate — correct location is inside `10 - Subcontractors/`.
12. **Job Tracker may be .xlsx not .md**

## Required commands / environment

- `python3.12` (system Python, not cron venv) — for reading `.xlsx` Job Trackers. The cron venv uses Python 3.11 which lacks `openpyxl`. System Python 3.12 has it at `/usr/local/lib/python3.12/dist-packages/openpyxl`. Read with:
  ```bash
  python3.12 -c "import openpyxl; wb = openpyxl.load_workbook('Job Tracker - HPE Field of Play Hornsby.xlsx', data_only=True) ..."
  ```

The FOIL (`ops/foil-management`) is the daily status board — read it at session start for the full picture across all projects and modes.

`site-lookahead` is the job-specific drill-down when Rocky is in **site mode** — it reads programme, RFIs, and subbie schedule for the active job only.

**Layer order:** FOIL (broad) → site-lookahead (job-specific) → per-job artefacts.

## Voice transcription — Australian accent workaround

Rocky's accent is regularly mangled by the STT pipeline into Welsh-like character sequences. Signs of this: strings like "Ysio'r Eid", "yn yimportuthiad", "Grifodd yn Gwyrddai". Rocky denies having a Welsh keyboard — the issue is in the transcription chain.

**When you see garbled output from Rocky:**
- 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"

## Logging

After AM look-ahead output:
- Append AM look-ahead note to the daily diary at `07 - Site Documents/07d - Daily Diary/YYYY-MM-DD.md`
- Mark as `hermes - AM look-ahead` entry
- Update `11_OpsMan_LFCS/FOIL.md` — set `## Mode:` to `site`, update `## Active work:` with today's site activity

After EOD summary output:
- Append EOD note to the daily diary at `07 - Site Documents/07d - Daily Diary/YYYY-MM-DD.md`
- Mark as `hermes - EOD summary` entry
- If docket was missing, additionally write a note to the vault at `11_OpsMan_LFCS/_AgentLog/hermes-daily/YYYY-MM-DD-docket-alert.md` so CC can follow up on Monday morning
- Update `11_OpsMan_LFCS/FOIL.md` — set `## Mode:` back to `desk` or `site-closed`, update notes
