# Cron Job: rateright-weekly

**Job ID:** f481ba22c69c
**Run Time:** 2026-07-11 08:01:01
**Schedule:** 0 8 * * 6

## Prompt

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

---
name: chief-of-staff
description: Hermes as Layer 2 Chief of Staff to Rocky. Manages the operational picture across LFCS, RateRight, and Personal. Owns FOIL files, daily digests, crew coordination, and vault write authority. Boots from _System/ChiefOfStaff.md.
version: 1.0.0
category: ops
tags: [hermes, chief-of-staff, vault, crew-management, foil]
recurrence: always-on
---

# Chief of Staff — Hermes

## Role definition

Hermes is Rocky's always-on chief of staff. Layer 2 in the four-layer team structure (above Execution and Operations). The role is defined in `_System/ChiefOfStaff.md` — that file is the canonical source of truth for authority and protocol.

**What this means in practice:**
- Always on Telegram, always reachable
- Manages the operational picture (FOIL files, daily digests)
- Coordinates the crew (Claude Code on laptop) — queues tasks, briefs sessions
- Has full vault read/write — can update FOIL, daily notes, _Log.md without asking
- Escalates to Rocky only for decisions, not routine work

## Team structure

| Layer | Agent | Role |
|---|---|---|
| Layer 0 | Rocky | Operator, owner, non-delegable decisions |
| Layer 1 | Rocky + Claude (chat) | Strategy |
| Layer 2 | Hermes (Chief of Staff) | Operational picture, crew management |
| Layer 3 | Claude Code (laptop) | Execution, heavy lifting |
| Layer 4 | OpenClaw (VPS) | Dormant / deprioritized |

## Boot sequence (Hermes — every session)

1. `_System/CLAUDE.md` (global AI Team Protocol)
2. `_System/ChiefOfStaff.md` (this role's authority)
3. Relevant project `_Brain/CLAUDE.md` (default: `11_OpsMan_LFCS`)
4. `_Brain/_Log.md` (last 20 entries)
5. Relevant FOIL.md (default: `11_OpsMan_LFCS/FOIL.md`)
6. `01_Daily/` (last 3 days)
7. `_AgentLog/` (last 7 days)

## Crew coordination

**Claude Code on laptop** — Rocky opens laptop → I queue tasks via vault + Telegram.
- Read the FOIL before each session to know what's active
- Report completions back into the daily note
- If Rocky is unavailable, I can SSH into the laptop directly and run commands

## OpenClaw fleet (historical — 2026-03)

Rocky ran 8 OpenClaw agents on the VPS before consolidating. All still exist as configs on VPS at `~/.clawdbot*/`. Active as of 2026-05-10:

| Agent | Role | Config path | Port |
|---|---|---|---|
| **Rivet** | Orchestrator (COO) | `.clawdbot/` | ~18789 |
| **Sentinel** | DevSecOps | `.clawdbot-sentinel/` | 18800 |
| **Radar** | Research & Intel | `.clawdbot-radar/` | 18804 |
| **Herald** | Communications (Gemini 3.1 Pro, Google direct) | `.clawdbot-herald/` | 18708 |
| **Cog** | Operations | `.clawdbot-cog/` | 18812 |
| **Harper** | Finance & Legal | `.clawdbot-harper/` | - |
| **Susan** | ? (deepseek-chat → MiniMax → kimi, ElevenLabs voice, workspace `/home/ccuser/susan`) | `.clawdbot-susan/` | 18792 |
| **Builder** | ? | `.clawdbot-builder/` | - |

All have `clawdbot-backup-20260305.json` — wound back around March 2026. OpenClaw updates broke them every time (full day to fix per update), and API costs were high with 8 agents running. Rocky consolidated to Hermes + Claude Code on laptop + OpsMan.

**2026-05-10: Rocky wants Susan and Herald resurrected on Hermes instead of OpenClaw.**
- See `references/susan-herald-rebuild.md` for rebuild plan — Susan (deepseek-chat, ElevenLabs voice, workspace `/home/ccuser/susan/`) and Herald (Gemini 3.1 Pro, communications). Each needs: system prompt (persona), workspace, skills, cron schedule, Telegram integration, MCP connections.

**Laptop SSH access (confirmed working 2026-05-10):**
```
ssh -o StrictHostKeyChecking=no -i /root/.ssh/id_hermes_ed25519 mclou@laptop-cd8df7fs "<command>"
```
- User: `mclou` (Windows Administrator account, NOT root)
- Key: `/root/.ssh/id_hermes_ed25519`
- Hostname: `laptop-cd8df7fs` — NOT IP `100.108.207.15` (known_hosts has hostname entry, not IP)
- `tailscale ssh` does NOT work on Windows — returns "502 Bad Gateway". Use plain SSH.
- `-o StrictHostKeyChecking=no` suppresses host key prompts and handles transient handshake issues.

**When port 22 times out:** laptop shows `active` on tailnet but SSH times out. `tailscale ssh` fails with 502. Plain SSH via hostname often still works. If all fail: Rocky runs `Restart-Service sshd` on the laptop.

- See `references/laptop-dispatch.md` for Windows-specific gotchas (.env config, how to run claude -p)
- See `references/ssh-access.md` for Windows SSH PowerShell path patterns and loop detection
- See `references/agent-dashboards-github.md` for AI agent observability dashboard options (lazyagent, AgentMonitor, _y)
- See `references/lfcs-scope-benchmarks.md` for canonical pit rates, stair rates, footpath rates, and Rate Card V1.7 — loaded from Husse's vault 2026-05-10
- See `references/agent-dashboards-github.md` for AI agent observability tools — **AionUi (24.3k stars, Apache 2.0, supports Hermes + Claude Code + OpenClaw, cross-platform, Team Mode + cron, remote access)** is the top candidate. Parked pending code audit + foundation work completion. Rocky wants to fork under Apache 2.0.
- See `references/aion-ui-audit.md` for full AionUi research: features, risks, setup options (laptop vs VPS), 20-min install.
- See `references/paperclip-research.md` for Paperclip: 63.9k stars (paperclipai/paperclip), NousResearch/hermes-paperclip-adapter (1.1k stars), zero-human company framework. Setup ~2-3 hrs. Parked.
- See `references/hermes-web-dashboard.md` for browser access options: `hermes dashboard` (port 9119, --insecure for Tailscale), AionUi, and Electron desktop app — with security tradeoffs and startup sequence.
- See `references/lfcs-scope-benchmarks.md` for canonical pit rates, stair rates, footpath rates, and Rate Card V1.7 — loaded from Husse's vault 2026-05-10

## FOIL files (operational picture)

Maintain FOIL.md in each project folder:
- `HQ-Vault/11_OpsMan_LFCS/FOIL.md` — LFCS
- `HQ-Vault/10_RateRight/FOIL.md` — RateRight
- `HQ-Vault/12_SiteOps/FOIL.md` — SiteOps
- `HQ-Vault/20_Personal/FOIL.md` — Personal

Update on mode switch, major completion, or blocker. See `foil-management` skill for format and workflow.

## Mode detection

Rocky's context is detected from trigger words:

| Trigger | Mode |
|---|---|
| "site", "on site" | site |
| "office", "project manager", "pricing" | office |
| "personal", "home", "family" | personal |
| (none) | office (default) |

Update `## Mode:` in the FOIL on every switch.

## Daily rhythm

- **8am AEST:** Morning digest — read all FOILs, send Telegram summary to Rocky
  - **Telegram output: plain text only, short sentences, no markdown bullets, no tables, no code blocks** — Rocky reads on phone, formatted messages are unreadable on mobile Telegram
  - If digest needs to show structured data (job status, rates), use labeled key:value pairs or short bullet lines — but never markdown tables or fenced code
  - Markor (Android) or Obsidian (desktop) can render `.md` files for reading, but Telegram messages must be plain text
  - If in doubt about format, make it shorter and more human-readable rather than structured
- **Morning:** update FOILs from overnight events
- **Ongoing:** capture decisions, completions, blockers as they happen
- **EOD:** final FOIL update, daily note append, commit vault

## Escalation rule

Only bother Rocky with:
- Decisions requiring his judgment
- Things that went wrong and need action
- Information he explicitly asked for
- Opportunities needing his sign-off

Routine: handle it, log it, move on.

## Cross-project scope

Chief of Staff spans LFCS + RateRight + Personal. The daily digest covers all three. When Rocky switches context, the FOIL and mode label update accordingly.

> Absorbed from `foil-management` (absorbed_into=chief-of-staff). FOIL format, mode detection, vault path conventions, and morning digest protocol moved here as canonical sections.

## FOIL Format & Protocol

FOIL = Focus, Operational status, Issues, Log. A daily status board that lives in the vault so every agent (Hermes, OpsMan, Claude Code sessions) can see what's active without asking Rocky.

### Trigger

- **Session start:** read the relevant FOIL before doing any work
- **Morning cron (8am AEST):** send digest of all FOIL files to Rocky via Telegram
- **Rocky says "update FOIL" / "log" / "what's happening":** capture and update
- **Mode switch detected:** update FOIL when Rocky changes context (site → office → personal)

### FOIL.md template

```markdown
# {PROJECT} FOIL — {YYYY-MM-DD}

## Mode
[site | office | personal]

## Focus
What Rocky is working on right now — one line.

## Active work
- Bullet list of current tasks, jobs, or activities
- Include time spent if known (e.g. "~2hrs CloudCode work")

## Blockers
- Anything stopping progress
- Include who's responsible if known

## Skills to check
- List any LFCS skills that ran or need testing
- e.g. boq-parse — tested / not tested this week

## Just Done
- What was completed in this session
- Include duration if known

## Next
- What comes after current activity
- Include timing if known (e.g. "7pm: site visit with Liam")

## Evening / Tomorrow
- Planned activities for later today or next day
```

### Daily capture workflow

1. Rocky voices what he's doing → capture into the relevant FOIL under `Active work` or `Just Done`
2. If a blocker is hit → log under `Blockers` with what would unblock it
3. If a skill runs → note under `Skills to check` with `ok/fail/warn`
4. At session end → append a timestamped entry to `01_Daily/YYYY-MM-DD.md`
5. Commit vault changes with `hermes: FOIL update {YYYY-MM-DD}`

### Mode detection

Rocky's current context is detected from trigger words in his messages:

| Trigger word(s) | Mode | What to log |
|---|---|---|
| "site", "on site", "site manager" | `site` | Field work, superintendents, physical progress, deliveries |
| "office", "project manager", "pricing", "admin" | `office` | Bids, RFIs, variations, Liam coordination, spreadsheet work |
| "personal", "home", "family", "sauna" | `personal` | Non-work items, house, youth, health, social |
| No trigger | `office` | Default — assume LFCS work unless context says otherwise |

Mode labels go in the FOIL under `## Mode:` so it's always clear what's active.

### Morning digest (8am AEST cron)

**CRITICAL: Send as inline Telegram text only — NEVER as a .md attachment or file.**

Rocky cannot read .md files in Telegram on his phone. The digest must be plain text pasted directly into the chat, max ~6 lines per project section. No attachments, no tables, no markdown bullets.

If the digest is too long for one message, send LFCS section first, then RateRight, then Personal — as separate messages. Never compress into an attached file.

Format each section:

```
LFCS — {today}
Mode: {mode} | Focus: {one line}
Active: {active work — 1-2 items max}
Blockers: {blockers or "none"}
Next: {next activity with time if known}
```

Plain sentences only. No bullet characters, no dashes as list markers. Max 6 lines per project.

### Vault path conventions

- LFCS: `HQ-Vault/11_OpsMan_LFCS/FOIL.md`
- RateRight: `HQ-Vault/10_RateRight/FOIL.md` (create if missing)
- SiteOps: `HQ-Vault/12_SiteOps/FOIL.md` (create if missing)
- Personal: `HQ-Vault/20_Personal/FOIL.md` (create if missing)
- Daily notes: `HQ-Vault/01_Daily/YYYY-MM-DD.md`

### Response style

Rocky prefers bulleted, tight responses — not paragraphs of explanation. Log in bullets; confirm in 1–2 lines.

### FOIL pitfalls

1. **FOIL file missing** — create from template immediately, don't skip. Empty FOIL is worse than no FOIL.
2. **Mode not updated** — if Rocky is clearly in a different mode than the FOIL shows, update the `## Mode:` line and note the switch.
3. **Personal FOIL neglected** — Rocky's personal life (house move, family, health) matters to him and gets logged. Create `20_Personal/FOIL.md` if it doesn't exist.
4. **Morning digest too long** — max 6 lines per project section. If there's too much content, summarise harder rather than cut.

---

> Absorbed from `lfcs-operations` (absorbed_into=chief-of-staff). Google Workspace OAuth, Drive search, daily site reports, job tracking, Airtable ops, laptop SSH, Windows dispatch.

## LFCS Operations

### Google Workspace OAuth — Troubleshooting & Recovery

### The project/token mismatch problem
When the Drive/Gmail/Sheets API returns:
```
Caller does not have required permission to use project opsman-lfcs-27665
```
This means the access token in `google_token.json` is still bound to an **old OAuth client/project**, not the current one. Simply downloading a new `client_secret.json` does NOT fix this — the old refresh token is still valid and still routes to the wrong project.

### Fix: Re-authenticate to get a fresh project-bound token

```bash
# 1. Generate a new auth URL (run on VPS)
python /root/.hermes/skills/productivity/google-workspace/scripts/setup.py --auth-url

# 2. User pastes URL into browser, authorizes, pastes the redirect URL back
# 3. Exchange the code for a token
python /root/.hermes/skills/productivity/google-workspace/scripts/setup.py --auth-code "CODE_FROM_REDIRECT_URL"
```

The new token will be bound to whichever project the OAuth client was created in (e.g. `hermes-lfcs`).

### Verify Drive access directly (bypasses script caching issues)
If `google_api.py` still fails after re-auth, try a direct Python call:
```python
from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials

creds = Credentials.from_authorized_user_file('/root/.hermes/google_token.json',
    ['https://www.googleapis.com/auth/drive.readonly'])
service = build('drive', 'v3', credentials=creds)
results = service.files().list(
    q='fullText contains "TCE" and fullText contains "Liverpool"',
    pageSize=10,
    fields='files(id, name, mimeType, webViewLink)'
).execute()
```

### Enable required APIs in Google Cloud Console
If API returns "not enabled", go to:
`https://console.cloud.google.com/apis/library/{api-name}.googleapis.com?project={project-name}`

LFCS project: `hermes-lfcs`
Required APIs: `drive.googleapis.com`, `sheets.googleapis.com`, `gmail.googleapis.com`, `docs.googleapis.com`, `calendar-json.googleapis.com`

**Full end-to-end workflow (OAuth setup, file creation, email):** see `references/google-workspace-end-to-end.md`
### gmail-access — Gmail search, read, forward patterns (verified 2026-05-12; sent-mail + job-briefing added 2026-07-06)
See `references/gmail-access.md`. Covers:
- Token manual-refresh bypass when `Credentials` lib errors on bad `expiry`
- `in:inbox` vs `in:sent` scoping — "did I send X" questions need the SENT folder
- Inbox digest categorisation (Money / Tenders / Active ops / OOO / Decisions)
- Job-status briefing shape (Done / Waiting / Queue / Watching) for "investigate job X" questions
- HTML-only body extraction
- ThreadId vs messageId reconstruction

### Email forwarding via Gmail API — verified working pattern
See `references/gmail-forward-via-api.md`
Runnable script: `scripts/fwd_emails.py` — takes message IDs as args

---

### Daily Site Report — LFCS

#### Filing path
- Vault: `/home/ccuser/rateright-growth/HQ-Vault/11_OpsMan_LFCS/daily/{YYYY-MM-DD}.md`
- Auto-syncs to GitHub every 30 min via `auto-sync.sh`

#### Format
```markdown
## HH:MM — hermes — short title

- Bullet 1
- Bullet 2
```

Append only. Never edit history. Corrections go as a new entry with `[[link to original]]`.

#### Report content from site (standard sections)
- Time on/off, crew size
- Work completed (bullets, specific to trade)
- Delays — who caused them, duration, whether client/TCE foreman was notified
- Variations flagged (clearance issues, extra labour, scope gaps)
- Photos: save to `/home/ccuser/opsman-work/daily/{YYYY-MM-DD}_{job}_{seq}.jpg`
- Dayworks: note if confirmed by client/foreman verbally

#### Photos via Telegram — CAPTURE LIVE, NOT END OF DAY
Context compaction eats photo captions mid-session. Workflow:
1. Photo arrives → save to `/home/ccuser/opsman-work/daily/` **immediately** with descriptive name
2. Ask Rocky to caption it right then — append caption to daily note
3. End of day: compile all into the Google Doc report
4. If caption lost to compaction: Rocky forwards original Telegram messages and I reconstruct from there

Save: `cp /root/.hermes/image_cache/{img_id}.jpg /home/ccuser/opsman-work/daily/{YYYY-MM-DD}_{job}_{seq}.jpg`
EXIF unreliable via Telegram — user caption is the source of truth.

#### Photos in Google Docs — SKIP API EMBEDDING
Inline image embedding via Docs API is clunky and produces poor results (thumbnails, not full images). Photo links in docs are clickable but not inline.
**Skip it.** Instead:
- Upload photos to Drive (use `/root/.hermes/embed_photos.py`)
- Put Drive links in the doc
- Or better: save photos to `/home/ccuser/opsman-work/daily/`, email report to admin with a Drive folder link, do photo embedding manually on desktop if needed

#### Daily site report — content sections
Always include:
- **Crew & hours** — on/off time, crew composition
- **Dayworks status** — if TCE foreman confirmed dayworks verbally, note the exact time it was confirmed (e.g. 11:01am) and by whom
- **Delays** — cause, duration, who caused it, whether client/foreman was notified
- **P&L flags** — recoverable (dayworks/delay claim) vs. not yet agreed (clearance variation, extra timber)
- **Actions** — anything that needs formal written follow-up (variation claims, delay notices)
- **No job number** — just use job name; email to admin@lfcs.com.au for filing

---

### Job Number / Folder Lookup

#### No job number
Some LFCS jobs don't have a job number yet. In that case:
- File by job name only (e.g. "TCE Liverpool")
- Email the report to admin@lfcs.com.au — Huss will file it correctly
- Do NOT wait for a job number to create the report

#### Drive search (VPS)
```python
service.files().list(
    q='fullText contains "TCE" and fullText contains "Liverpool"',
    pageSize=10,
    fields='files(id, name, mimeType, webViewLink)'
)
```

Drive returns results with `webViewLink` — pass to user for clicking.

#### Airtable
Base: `LFCS-Operations` (appE43UvTyARe5oJs)
Table: Jobs
Filter by: client name, site suburb

---

### Hermes vs OpsMan — Lane Rules
- **OpsMan** (`clawdbot-opsman`): owns `/home/ccuser/opsman-work/` — writes Job Trackers, RFI registers, variation registers, dockets, daily diaries, email drafts.
- **Hermes** (this agent): reads `/home/ccuser/opsman-work/` for context; writes to `/home/ccuser/hermes-work/` for its own state.
- **Do NOT write to `/home/ccuser/opsman-work/`** — that's OpsMan's territory.
- Both agents share the same Airtable base via MCP.

---

### Remote Terminal Access — Windows Laptop via Tailscale

SSH key: `/root/.ssh/id_hermes_ed25519`
User: `mclou` (Windows Administrator account)
Tailscale hostname: `laptop-cd8df7fs` (100.108.207.15)

**This is a Windows 10+ machine, not a VPS.** SSH goes to the laptop directly.

Commands via `ssh -i /root/.ssh/id_hermes_ed25519 mclou@100.108.207.15 "<cmd>"`

#### Windows command quirks
- `uname` not available — use `cmd /c ver` to get Windows version
- Chain multiple cmds with `;` or separate SSH calls — `&` backgrounding causes errors in this tool
- `dir` with spaces in paths: use `\"path\"` or PowerShell (`powershell -Command "..."`)
- Finding installed apps: `powershell -Command "Get-ChildItem -Path \"C:\Users\mclou\AppData\Local\Programs\" -Recurse -Filter \"*.exe\" -EA SilentlyContinue | Select -Expand FullName"`
- Start GUI apps: `powershell -Command "Start-Process -FilePath \"...\"`" — runs visible on desktop

#### Windows remote install — verified 2026-05-12 (Aion UI v1.9.25)
Full sequence worked end-to-end:
1. `ssh mclou@laptop-cd8df7fs "cmd /c ver"` → Windows 10.0.26200.8246 ✅
2. Download `.exe` to `/tmp/` on VPS, then `scp -i /root/.ssh/id_hermes_ed25519 /tmp/AionUi-1.9.25-win-x64.exe mclou@laptop-cd8df7fs:C:/Users/mclou/Downloads/"`
3. Install silently: `ssh mclou@laptop-cd8df7fs "cmd /c start /wait C:\\Users\\mclou\\Downloads\\AionUi-1.9.25-win-x64.exe /S"`
4. Find install path: `powershell -Command "Get-ChildItem -Path \"C:\\Users\\mclou\\AppData\\Local\\Programs\\AionUi\" -Recurse -Filter \"AionUi.exe\" | Select -Expand FullName"`
5. Launch visible: `powershell -Command "Start-Process -FilePath \"C:\\Users\\mclou\\AppData\\Local\\Programs\\AionUi\\AionUi.exe\" -PassThru | Format-Table -AutoSize Id,ProcessName"`

Executable landed at: `C:\Users\mclou\AppData\Local\Programs\AionUi\AionUi.exe`
Backup copy at: `C:\Users\mclou\Downloads\AionUi-1.9.25-win-x64.exe`

#### Site photo + task capture (live, via Telegram)
Rocky sends photos from site with task orders. Capture workflow:
1. Photo arrives → immediately save: `cp /root/.hermes/image_cache/{img_id}.jpg /home/ccuser/opsman-work/daily/{YYYY-MM-DD}_{job}_{seq}.jpg`
2. Log task to `/home/ccuser/opsman-work/daily/{YYYY-MM-DD}.md`:
   ```markdown
   ## HH:MM — hermes — Job name
   ![Caption](photo file)
   Task description — N lads × N hrs
   ```
3. Append tasks in order received — no editing of prior entries
4. EXIF from Telegram is unreliable — user caption is the source of truth
5. End of day: compile all entries into site report

**IMPORTANT:** `/home/ccuser/opsman-work/daily/` is the shared vault daily log (symlinked to `HQ-Vault/11_OpsMan_LFCS/daily/`). Both Hermes and OpsMan write here. Entries are append-only. Auto-sync to GitHub runs every 30 min.

---

### LFCS Site Ops Sub-skills

> Absorbed from archived `lfcs-docket-check`, `lfcs-eod-summary`, `lfcs-job-tracker`, `lfcs-pre-pour-check`, `lfcs-rfi-status`, `lfcs-tool-tracker`, `lfcs-client-correspondence`, `timesheet-from-lfcs-nested`.

#### lfcs-docket-check — Signed Docket Verification
**Trigger:** Rocky says "docket check", "did I sign the docket", `/lfcs-docket-check`.
Also fires automatically from the EOD cron at 17:00 if there are dayworks or hybrid jobs ongoing.

**Procedure:**
1. Identify the active job. Read its `00-brief.md` to confirm `type:` is dayworks or hybrid.
2. Check `<job>/07 - Site Documents/07a - Dockets/` for a file matching `YYYY-MM-DD-signed.*` for today's date.
3. Reply with one of:
   - **Found** → "✅ Docket signed today (07a/2026-MM-DD-signed.jpg). Day billable." (one line)
   - **Missing AND it's before EOD** → "⚠ Docket not yet received. Get signed before leaving site or revenue at risk."
   - **Missing AND past EOD** → "🚨 AT-RISK REVENUE: today's docket missing. Last day's = [last seen date]. Chase tomorrow morning."
   - **Missing >48h** → "🚨🚨 AT-RISK REVENUE 48hr+: today's and yesterday's dockets missing. Escalate to admin (Hasibul)."

**Pitfalls:**
- Match strict `YYYY-MM-DD-signed.*` pattern — don't false-positive on unrelated files with today's date.
- If type is `price` (not hybrid/dayworks) → reply: "n/a — price contract, no docket needed."
- If multiple jobs ongoing, check each and reply per-job.
- Use Australia/Sydney timezone for date.

#### lfcs-pre-pour-check — 8-Item Pre-Pour Checklist
**Trigger:** Rocky says "pre-pour check" or `/lfcs-pre-pour-check`.

Walk Rocky through the 8-item pre-pour checklist for LFCS concrete pours. Log result to the inspection register.

#### lfcs-rfi-status — Open RFI Report
**Trigger:** Rocky says "RFI status" or `/lfcs-rfi-status`.

List open RFIs across active LFCS jobs, sorted by priority and days pending.

#### lfcs-job-tracker — Mid-Day Job Status
**Trigger:** Rocky says "job tracker" or `/lfcs-job-tracker`.

Mid-day status check — hours, costs, variance vs quote.

#### lfcs-eod-summary — End-of-Day Summary
**Trigger:** Rocky says "EOD summary" or `/lfcs-eod-summary`.

Generate end-of-day summary for active LFCS job(s). On-demand version of the 17:00 cron.

#### lfcs-client-correspondence — Draft Emails & Letters
**Trigger:** Rocky asks to draft client-facing emails or formal letters (pre-start RFIs, commercial clarification, variation requests, payment claim covers). Collates from job brief, RFI register, and variation register.

#### lfcs-tool-tracker — Tool & Equipment Register
**Trigger:** Rocky says "tool tracker" or `/lfcs-tool-tracker`.

Design and implement a tool and equipment register — WhatsApp photo check-in/out, Google Sheets backend, multi-vehicle manifests, floater management, service reminders.

#### timesheet — Weekly Timesheet Compilation
**Trigger:** Rocky says "timesheet" or `/timesheet`.

Compile Rocky's weekly timesheet from logged hours and draft submission email/PDF.

---

### Active Jobs (2026-05-12)
- 2602 Munmorah Bridge
- 2626 Mack Civil (repriced rev6, pending AfC drawings + RFI36 sig)
- 2629 TCE WWTP Erskine Park (bid submitted)
- 2630 TCE North Beach Wollongong (WON)
- Winswell (Homebush Solutions) — footpath, Forest Rd & Boundary Rd, Peakhurst NSW 2210. Started 2026-05-12, 3 lads, tasks: bench setup, edge boards, mesh + chair up, control joints, other footpath. 7am–3:30pm. Contact: Nihar Reddy. Superintendent meeting done (outcome TBC).

---

## RateRight Operations

> Absorbed from `rateright-operations` (archived 2026-06-26). All RateRight-specific operational knowledge consolidated here. See `references/facebook-pages.md` for the Facebook scrape regression fixture.

### Vault anatomy

```
HQ-Vault/10_RateRight/
├── _Brain/
│   ├── CLAUDE.md         ← start here: what RateRight is, strategy, hard rules
│   ├── _Log.md           ← append-only one-liners, chronological
│   ├── Open-Threads.md   ← dormant decisions waiting Rocky's call
│   ├── Decision-Log.md
│   └── README.md
├── Business/
│   └── Marketing/        ← hostel campaign, cold email, facebook strategy
├── Skills/
│   ├── hunt/             ← hunt infrastructure (aggregator, outreach, matchmaker)
│   ├── matchmaker/      ← conversion engine design
│   └── pulse/            ← telemetry / monitoring
├── ROADMAP.md            ← historical baseline (stale, see Active.md)
└── Operations/           ← deliverability fixes, technical ops docs
```

### PM2 fleet reference (updated 2026-05-26)

```
hunt-aggregator   — stopped (scrapers down — selector breakage, not just key exhaustion)
hunt-contractors — stopped (scrapers down)
hunt-matchmaker  — stopped (not built)
hunt-monitor     — online but idle
hunt-outreach    — online but starving (no queue)
hunt-workers     — stopped (scrapers down)
growth-engine    — online
rateright-app    — systemctl (not PM2)
```

**Critical new facts (Graphify proof, 2026-05-26):**
- Gumtree scrapers: 0 contacts extracted since 2026-04-28. Root cause: HTML structure change broke selectors. ScrapingBee fallback wired but `SCRAPINGBEE_API_KEY` not provisioned. Running the scrape produces nothing actionable until selectors are remapped AND key is provisioned.
- Hunt → Marketplace: ZERO connecting edges. Hunt writes to `growth-engine` Supabase (`memscjotxrzqnhrvnnkc`), marketplace reads from `marketplace` Supabase (`eciepjpcyfurbkfzekok`). No shared module, no API call, no DB reference. Hunt is a parallel pipeline producing nothing for marketplace supply.
- Marketplace Supabase: auto-paused 2026-05-26 after 7 days idle on free tier. Restoration in progress. Data preserved.

**Fix path before any hunt scrape:**
1. Remap Gumtree selectors (HTML change broke them)
2. Provision `SCRAPINGBEE_API_KEY`
3. Restore marketplace Supabase
4. Re-architect hunt→marketplace pipeline (structural, not a bug fix)

### Current known state (as of 2026-05-09)

| System | Status | Note |
|--------|--------|------|
| Site (rateright.com.au) | ✅ Live | Fixed 2026-05-03 |
| Stripe | ⚠️ Untested end-to-end | Key valid, no browser checkout confirmed |
| Supabase | ✅ Working | Service-role key valid |
| Gumtree scraper | 🔴 Stopped | ScrapingBee exhausted until 2026-05-13 |
| Hunt queue | ⚠️ Stalled | 359 leads, 1 approved/sent |
| Matchmaker | 🔴 Not built | Design exists, implementation needed |
| rr profile | 🔴 Dead | DeepSeek 402, k2.6 model never started |
| Pawel (AWX Labour) | 🟡 Cold | 80 days since contact |
| Susan's 17 leads | 🟡 Stale | ~75 days old, too cold to send as-is |

### Hunt — Marketplace Lead Scraper

#### Invocation

| Command | Script |
|---|---|
| `/hunt` | Run all: job-aggregator.js + worker-hunter.js + contractor-hunter.js |
| `/hunt workers` | Run worker-hunter.js only |
| `/hunt contractors` | Run contractor-hunter.js only |
| `/hunt workers sydney` | Run worker-hunter.js --city sydney |
| `/hunt contractors melbourne` | Run contractor-hunter.js --city melbourne |
| `/hunt jobs` | Run job-aggregator.js only |
| `/hunt --dry-run` | Add --dry-run flag to all scripts |
| `/hunt --status` | Run notion-summary.js for today's stats |
| `/hunt facebook-workers` | Scrape Facebook construction worker pages (Australia-wide) |
| `/hunt facebook-jobs` | Scrape Facebook job posting pages (Australia-wide) |

#### Script paths

- **VPS:** `/home/ccuser/rateright-growth/scripts`
- **Laptop deploy:** `C:\Users\mclou\rateright-growth-deploy`
- **Vault mirror:** `HQ-Vault/10_RateRight/Skills/hunt/`

#### Facebook hunt (Wave 2.2 — 237 pages)

**Browser approach (VPS):**
```
browser_navigate(url="https://www.facebook.com/[page-slug]")
# screenshot → vision AI extract → parse → upsert CRM → digest
```

**Workflow per page:**
1. Navigate to the Facebook page (public posts don't need login)
2. Screenshot the post list
3. Vision AI extracts: name, location, job type, contact, post date
4. Upsert to `hunt-leads` table with `source='facebook-workers'` or `source='facebook-jobs'`
5. After full run: output daily digest of new contacts

**Scope:** 237 pages across all Australian states/territories. Run in batches of 20-30 per session to avoid rate limiting.

**Do NOT:** try to auto-send messages as Rocky — this risks account ban and is explicitly against the Facebook strategy in the vault.

#### Regression fixture

See `references/facebook-pages.md` for expected output structure.

### chat-bootstrap — Vault Snapshot for Chat Handoff

**Trigger:** When Rocky starts a new session and needs the RateRight operational picture quickly. "Bootstrap chat", "read the vault", " what's happening with RateRight."

**What it does:** Reads key RateRight vault files and outputs a single mobile-friendly markdown snapshot. One copy-paste = full context for the chat.

#### Source files

| Data | File |
|---|---|
| Strategic priority | `HQ-Vault/10_RateRight/ROADMAP.md` |
| Open decisions | `HQ-Vault/10_RateRight/_Brain/Open-Threads.md` |
| VPS / agent state | `HQ-Vault/11_OpsMan_LFCS/_Brain/Agent-Stack-MOC.md` |
| VPS detailed state | `HQ-Vault/11_OpsMan_LFCS/_Brain/VPS-State-2026-05-01.md` |
| Recent decisions | `HQ-Vault/10_RateRight/_Brain/Decision-Log.md` |
| Today's daily notes | `HQ-Vault/01_Daily/YYYY-MM-DD.md` (current date) |
| RateRight activity log | `HQ-Vault/10_RateRight/_Brain/_Log.md` |

#### Process

1. Determine today's date in `YYYY-MM-DD` format.
2. Read each source file.
3. If any file is missing or returns an error, note it inline as `<!-- FILE: path — NOT FOUND -->`.
4. Build a single fenced markdown block (triple-backtick + `markdown`).
5. Output nothing else — no preamble, no commentary, no follow-up questions.
6. End with a blank `Session intent:` line.

#### Output format

```markdown
VAULT SNAPSHOT [timestamp]

## Strategic Priority
[Extract top 3 🚨 CRITICAL items from ROADMAP. If stale, prepend "(⚠️ ROADMAP stale — YYYY-MM-DD)".]

## Open Decisions — Waiting on You
[Summarise each 🔴/🟡 thread from Open-Threads. Max 2 sentences each.]

## What's Running Right Now
[Agent-Stack + VPS-State: bullet list of live services, their status. Max 1 line per service.]

## Recent Decisions
[Last 5 entries from Decision-Log or today's daily.]

## Today So Far
[Last 20 entries from today's daily note — bullets only.]

## Live Numbers
[Try to read Dashboard.md. If stale or missing:]
> ⚠️ Live numbers unavailable. Supabase service-role key is invalid (needs replacing). Dashboard is stale. Don't guess — say "unknown" until the key is fixed.

## Session intent:

```

#### Key rules

- **Never invent numbers.** If the vault doesn't have them, say so explicitly.
- **Never output outside the fence.** Chat will parse the fence as the signal-to-noise boundary.
- **If today's daily doesn't exist**, use the most recent daily note and note the date.
- **Timestamps** in the header use UTC.

### Hard rules (from vault CLAUDE.md)

- Worker supply is the #1 priority
- Never re-enable SMS without explicit Rocky approval
- Never propose deletions of legacy repos without flagging first
- Never pretend to know user counts until Supabase service-role key is confirmed valid
- The $50 flat fee is the model — no percentage, no ongoing commission

---

## Pitfalls

1. **OpsMan interference** — OpenClaw (clawdbot-opsman) is unstable and deprioritized. Do not route LFCS work through it. Use Hermes directly.
2. **Mode staleness** — If Rocky has been in "site" mode for hours but the FOIL still says "office", update it immediately.
3. **Crew silence** — If Claude Code hasn't reported back and Rocky is unreachable, SSH to the laptop directly and check what's running.
4. **Personal FOIL skipped** — Rocky's personal life (house, family, health) matters. Update `20_Personal/FOIL.md` even when work is busy.
7. **Laptop SSH silent timeout** — laptop shows as `active` on tailnet but port 22 times out. `tailscale ssh` returns "502 Bad Gateway" on Windows specifically. Plain SSH via hostname (`mclou@laptop-cd8df7fs`) still works. Retry with that form before declaring laptop unreachable.
8. **Windows SSH PowerShell path failures — SSH command loop pattern** — When running PowerShell commands over SSH to the laptop, if 3 consecutive attempts fail (terminal exits 255), the issue is path escaping in PowerShell context, NOT network. Pattern that works: `ssh mclou@laptop-cd8df7fs "cmd /c dir C:\\\\path\\\\to\\\\dir"`. Do NOT use bare PowerShell `Get-ChildItem 'C:\path'` over SSH — it silently returns nothing (exit 0 but no output). Always wrap PowerShell in `cmd /c powershell -Command "..."` for complex commands.
9. **Gmail/Drive multi-account** — VPS uses `gws` CLI. Rocky does OAuth flow in browser per account. Laptop CC uses OAuth via Max subscription (no key needed). Multiple accounts supported.
10. **Gmail `format=metadata` returns empty headers in practice** — The pattern `?format=metadata&metadataHeaders=From&metadataHeaders=Subject&metadataHeaders=Date` is documented as correct but on this VPS (2026-06-30) the response came back with `headers: []` for every message. `format=full` works reliably — payload is larger but you can pull the same `payload.headers` array. For any cron-driven digest, default to `format=full` and ignore the `parts` body unless needed. If `format=metadata` is mandatory, the headers come back fine — the bug is intermittent and the safer path is `format=full`.
11. **Cron-mode tool restrictions** — When running as a scheduled cron job, `execute_code` is blocked by the `cron_mode` approval policy. The Tirith security scanner also blocks heredoc (`python3 << EOF`) and pipe-to-interpreter (`cat | python3`) patterns, plus `python3 -c` snippets that touch credentials. The working pattern for multi-step Python in a cron: `write_file(path='/tmp/script.py', content=...)` then `python3 /tmp/script.py`. Plain `terminal` calls with single-line commands are fine. Plan scripts as files, not inline.

9. **Gmail/Drive multi-account** — VPS uses `gws` CLI. Rocky does OAuth flow in browser per account. Laptop CC uses OAuth via Max subscription (no key needed). Multiple accounts supported.

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 RateRight platform agent. Sydney timezone. Deliver a weekly RateRight health check to Rocky's Telegram (chat 7377499346) via send_message.

**Check:**
1. Is the RateRight app reachable? (curl http://localhost:3000 or check if pm2 process is running)
2. Anything in memory (~/hermes/memories/MEMORY.md) flagged as queued for RateRight action?
3. If web access is available, scan for relevant civil construction tech news (1-2 items max).

**Format — tight bullets:**
```
📊 RateRight Weekly — [DD MMM]

## App status
[reachable / down / unknown]

## Queued items
(bullets from memory — RateRight-specific items. If none, write "Nothing queued.")

## News/competitive (if web available)
(bullets or "No news available")
```

Do NOT make changes — report only.

## Response

Good. App is up, no queued items, found one relevant competitor (EstimateOne) for the news line.

📊 RateRight Weekly — 11 Jul 2026

## App status
reachable (HTTP 200, systemctl active)

## Queued items
Nothing queued. No RateRight-specific items in memory.

## News/competitive
- EstimateOne (E1) expanding tender aggregation with WA-focused listings — $10m+ projects visible (Marangaroo Golf Course, Mandurah Baptist College Stage 5). They're a direct aggregator competitor to RateRight's tender-discovery lane.
- IBISWorld Construction in Australia 2026 report flags continued demand underpinned by population growth — tailwind for the platform but paywalled detail.
