# Cron Job: self-health

**Job ID:** 74522c9d2bb7
**Run Time:** 2026-06-28 18:01:04
**Schedule:** 0 18 * * 0

## 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 vault root — job folders live here. Use ls first to discover exact subfolder names.
        default: "/home/ccuser/opsman-work/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?"

**Job number mismatch — clarify before assuming it's a new job:**
- Rocky may say "LF 2651" but the job is actually "2626" — a one-digit slip
- Before creating a new job folder, always ask: "Is that 2651 or 2626?" or check the Ongoing Jobs directory listing
- This avoids a duplicate job folder being created for something that already exists
- If searching by job number returns nothing, try: (a) partial substring search in vault filenames, (b) ask for the client name, (c) check Airtable Jobs table

## Role Scope — Foreman's Pocket PM

**Hermes is the foreman's pocket super: PM + estimator + admin + engineer in one.**

When Rocky is on site, I am his daily companion. The full scope:

- **Capture:** site notes, photos, hours, deliveries, docket status, variations, RFI triggers
- **Recall:** answer "what did I do Tuesday?", "what's the rate for X?", "is docket A signed?", "is this in scope?"
- **Remind:** docket-not-signed, RFI-overdue, pour-tomorrow, materials-due, 48-hour variation clock running
- **Route:** PM territory (formal letters, contract interpretation, programme analysis) → tell Rocky "CC will draft this from the desk"
- **Draft:** variations immediately when triggered (48-hour clause is non-negotiable), pre-pour checklists, RFI drafts, EOD summaries
- **Programme:** hold the baseline + progress schedule, tell Rocky how to get back on track when behind
- **Specs:** make the job specs, pre-pour checklists — Rocky prints on site or I file for him
- **Scope answers:** when a foreman asks "is this in my scope?", I answer from the drawings + scope of work — not guess

**NOT my job:** formal commercial letters, contract interpretation calls, strategic programme replanning, long-form reports. Capture the trigger, route to Claude Code on the laptop.

**Coordination with Claude Code:**
- My daily diary must be thorough enough for CC to draft commercial letters from
- Variations I log need: date, trigger, description, dollar estimate, status
- CC reads `daily/{date}.md` + Variations register before drafting anything commercial
- CC handles: formal variation claims, EOT notices, payment claim disputes, programme analysis, contract clause readings

## Communication Mode — TEXT ONLY (HARD RULE)

**WhatsApp groups: READ-ONLY. Telegram is the live channel.**
- Rocky manages LFCS from site via **Telegram** (1:1 with Hermes)
- WhatsApp groups: Hermes receives site updates but **cannot reply** unless @mentioned, in a 1:1 DM, or Rocky/Hasibul/Liam directly ask
- Never reply to onboarding prompts, error messages, or status updates in WhatsApp groups
- All live foreman communications happen in Telegram

**Rocky has explicitly said: "stop giving me voice messages, just do in text."**
- NEVER send voice memos or TTS audio to Rocky via Telegram
- If Rocky asks for a voice explanation → deliver short bulleted text he can read on his phone
- If you accidentally send audio → switch to text immediately, do not repeat the mistake

**Why:** Audio requires download/play on his Telegram — he's on-site, one hand occupied, noisy environment. Text he skims in 5 seconds.

## 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

## Email and Airtable — MUST BE RESTORED

**OpsMan is decommissioned. Hermes is the sole LFCS agent as of 2026-06-27.**

Confirmed missing on this profile (not just uncertain — tested and failed):
- Email: `gws` CLI returns auth errors. `himalaya` not available on laptop (Windows). No email tool is currently working.
- Airtable MCP: not wired for this profile. LFCS-Operations base (appE43UvTyARe5oJs) was on OpsMan's config.

**Email restoration:**
- Check: `python3 /root/.hermes/skills/.archive/google-workspace/scripts/setup.py --check`
- If expired: `python3 /root/.hermes/skills/.archive/google-workspace/scripts/setup.py --auth-url` → send URL to Rocky → `python3 ...setup.py --auth-code <code>`
- Scope: `gmail.send`, `gmail.modify`, `calendar`, `drive.readonly`

**Airtable restoration:**
- Need: Airtable PAT with access to `LFCS-Operations` base (appE43UvTyARe5oJs)
- Ask Rocky to provision or transfer from the decommissioned OpsMan profile config at `/root/.clawdbot-opsman/openclaw.json`

**Both must work:** the 48-hour variation notice rule is meaningless without email. Every other LFCS function depends on having these tools.

## Shared Pitfalls

## Shared Pitfalls

1. **WhatsApp groups are READ-ONLY.** Never reply in groups unless @mentioned, 1:1 DM, or Rocky/Hasibul/Liam directly ask.
2. **Job-Tracker.md tables sometimes use `$` and `,` — strip both before parsing numbers.
3. For `dayworks`/`hybrid` jobs: dockets required. For `price` jobs: no docket needed.
4. Use Australia/Sydney timezone for all date comparisons.
5. If a field can't be parsed, show raw cell with `(see file)` rather than guessing.
6. Never auto-send client correspondence without Hasibul review for external emails.
7. **VPS path for job folders:** Job folders live at `/home/ccuser/opsman-work/Jobs/`. Always `ls /home/ccuser/opsman-work/Jobs/` first — subfolder names contain spaces ("Ongoing Jobs") and must be quoted in all shell commands.
8. **Always `ls` a directory before reading files inside it.** On the VPS vault, `07d - Daily Diary/` and `07a - Dockets/` are **directories** (not files).
9. **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.
10. **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.

- [WhatsApp-Site-Diary Architecture](./references/whatsapp-site-diary-architecture.md) — WhatsApp → Hermes → Airtable diary loop. No Zapier. Pilot: Hornsby HPE Field of Play. Pre-reqs, config file locations, PAT security, approval-gate workflow, open questions.
- [Variation — 48-Hour Rule](./references/variation-48hr-rule.md) — TfNSW/tier-1 VO notice deadlines, workflow advantage with Hermes, what to capture in the VO 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).
- [Role Scope Gap Analysis](./references/role-scope-gap-analysis.md) — what I have vs what's missing for full foreman-PM scope (contract docs, drawings, programme, pre-pour checklists, email auth, Airtable, job status register)
- [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. Sydney timezone. Deliver a self-health report to Rocky's Telegram (chat 7377499346) via send_message. Report only — change nothing.

**Checks:**
1. Stuck/hung Hermes processes: `ps aux | grep hermes | grep -v grep` — any process with etime > 1 hour?
2. Disk: `df -h /` — usage %
3. Systemd: `systemctl is-active hermes-gateway hermes-dashboard` — both should say "active"
4. Vault git sync: check age of last commit in /home/ccuser/rateright-growth/ — `git log -1 --format="%H %ci"` — is it within 1 hour?
5. MEMORY.md budget: `wc -c ~/.hermes/memories/MEMORY.md` — what % of 2200 chars?

**Format — tight bullets, no filler:**
```
🏥 Self Health — [DD MMM]

## Processes
(bullets — any stuck processes, or "All clear")

## Disk /dev/vda1
[usage%]

## Systemd
hermes-gateway: [active/inactive]
hermes-dashboard: [active/inactive]

## Vault sync
[last commit hash + age, or "No recent commit"]

## MEMORY.md
[char count] / 2200 ([%] used)
```

Read ~/.hermes/memories/MEMORY.md to get the char count. Use terminal tool for all system queries.

## Response

🏥 Self Health — 28 Jun

## Processes
- Gateway: uptime 16d, CPU 0.5% — active, not stuck
- Dashboard: uptime 16d, CPU 0.3% — active, not stuck
- All clear

## Disk /dev/vda1
77G total, 61G used, 17G free — **79% used** (⚠️ heading toward 80%)

## Systemd
hermes-gateway: **active**
hermes-dashboard: **active**

## Vault sync
`a2ab7e9` — **21 hours ago** (27 Jun 22:30, now 28 Jun ~19:30) ⚠️ outside 1-hour window, auto-sync may have missed

## MEMORY.md
1991 / 2200 chars (**90% used**) ⚠️ approaching budget ceiling
