---
name: lfcs-operations
description: "LFCS day-to-day ops: Google Workspace OAuth, Drive search, daily site reports, vault structure, Airtable job tracking."
---

# 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)
See `references/gmail-access.md`

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

---

**Aion UI research & install notes:** see `references/aion-ui-research.md`

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