{
  "jobs": [
    {
      "id": "8b2ae3275ce1",
      "name": "linkedin-content-batch",
      "prompt": "You are the Social Media Agent. Your persona and brand rules live at /root/.hermes/profiles/social-media-agent/SOUL.md \u2014 read it first and follow it. The attached skill (social-media-content-pipeline) is the authoritative runbook; its hard rules override anything in this prompt where they conflict.\n\nSchedule the weekly Pipeline Layer LinkedIn batch in GHL Social Planner. The DRAFTS were generated yesterday (Saturday) by the linkedin-weekly-drafts job and delivered to Rob for review; if Rob requested changes in-session, they were already applied to the files below. Your job: schedule exactly what's in the batch, then verify.\n\n## Inputs\n1. Find the batch JSON for the coming week: `/root/.hermes/work/linkedin/YYYY-MM-DD_batch.json` where YYYY-MM-DD = TOMORROW's date (the coming Monday). Array of {date, pillar, hook, source, summary, image_filename}.\n2. The human-readable batch file lives at `/root/.hermes/vault/LinkedIn Posting System/YYYY-MM-DD_linkedin-batch.md` \u2014 the JSON is authoritative for text; use the .md only for context.\n3. If neither file exists, or the JSON is missing days, or any post is still a flagged SHELL: do NOT schedule the missing/shell days and do NOT fabricate content. Schedule only complete days, and make the delivery report explicit about which days were skipped and why (Rob fills shells manually via LinkedIn native scheduling).\n\n## Pre-flight\n4. `curl -s http://127.0.0.1:9120/ghl/health` \u2014 confirm the agency token is live (positive expires_in_s). Fetch the token via `curl -s 'http://127.0.0.1:9120/ghl/token?locationId=agency:e4QT67gW4Bz6yO6r7Teu'`.\n5. LinkedIn account: `GET /social-media-posting/k6PTFz0gXAdNJLDT1aji/accounts` \u2014 use the Rob Blake profile account id and its `oauthId` as `userId`. Confirm `isExpired` is false; if the LinkedIn connection is expired, STOP, do not schedule, and deliver a report telling Rob to reconnect via GHL UI (Marketing \u2192 Social Planner \u2192 Settings).\n6. For each post: confirm the image serves publicly \u2014 `curl -s -o /dev/null -w \"%{http_code}\" https://robblake.cloud/ghl/images/linkedin/{image_filename}` must be 200. If a file is missing from `/root/.hermes/images/linkedin/` but exists in the vault's LinkedIn Posting System folder, copy it over and chmod 644 first.\n\n## Content-image match check (non-negotiable)\n7. Before scheduling, vision_analyze each staged image and confirm its topic matches the post's summary. If any card mismatches its post text (e.g. text was swapped after cards were made), STOP for that day, flag it loudly in the report, and do not schedule the mismatch.\n\n## Schedule\n8. For each day, POST to `/social-media-posting/k6PTFz0gXAdNJLDT1aji/posts` with: accountIds=[the profile account id], summary (exact text from JSON), type=\"post\", status=\"scheduled\", scheduleDate = that date at 09:00 MDT (\"YYYY-MM-DDT09:00:00-06:00\"), userId=oauthId, media=[{url: the robblake.cloud image URL, type:\"image/png\"}]. Use curl for all leadconnectorhq.com calls (Python urllib/requests get Cloudflare-blocked).\n9. Enforce the 3,000-char ceiling before every POST; if any summary exceeds it, do not silently trim mid-sentence \u2014 skip that post and flag it loudly in the report.\n\n## Verify (non-negotiable)\n10. After all creates, re-list posts (`POST .../posts/list`, limit 15) and for each newly created post confirm: status=scheduled, correct scheduleDate, media non-empty, saved summary length equals sent length, and the final hashtag block is present in the tail. Report any mismatch as a failure, not a footnote.\n11. Update the batch .md file: set the top STATUS line to \"SCHEDULED <today's date>\" with per-day post IDs and verification results (keep it short). If any post's text was swapped or held at schedule time, write the FINAL text back into the batch .md \u2014 the vault is the audit trail and must reflect what actually went out.\n\n## Delivery (final response \u2014 goes straight to Rob)\nOne compact report: per-day lines (date, pillar, post ID, scheduled time, image \u2713, tail \u2713), any skips/failures called out in bold at the top, and nothing else. No re-printing full post bodies \u2014 Rob already reviewed them Saturday.",
      "skills": [
        "social-media-content-pipeline"
      ],
      "skill": "social-media-content-pipeline",
      "model": null,
      "provider": null,
      "provider_snapshot": "nous",
      "model_snapshot": "moonshotai/kimi-k3",
      "base_url": null,
      "script": null,
      "no_agent": false,
      "context_from": null,
      "schedule": {
        "kind": "cron",
        "expr": "0 15 * * 0",
        "display": "0 15 * * 0"
      },
      "schedule_display": "0 15 * * 0",
      "repeat": {
        "times": null,
        "completed": 1
      },
      "enabled": true,
      "state": "scheduled",
      "paused_at": null,
      "paused_reason": null,
      "created_at": "2026-08-03T13:30:08.870269+00:00",
      "next_run_at": "2026-08-16T15:00:00+00:00",
      "last_run_at": "2026-08-09T06:14:13.910533+00:00",
      "last_status": "ok",
      "last_error": null,
      "last_delivery_error": null,
      "deliver": "origin",
      "origin": null,
      "enabled_toolsets": [
        "terminal",
        "file",
        "web"
      ],
      "workdir": null,
      "fire_claim": null
    },
    {
      "id": "de54c923184b",
      "name": "linkedin-weekly-drafts",
      "prompt": "You are the Social Media Agent. Your persona and brand rules live at /root/.hermes/profiles/social-media-agent/SOUL.md \u2014 read it first and follow it. Your only skills are the ones in that profile (social-media-content-pipeline, gohighlevel-ops, brand-asset-generation, ghl-api-integration); the attached skill below is the authoritative runbook and its hard rules override anything in this prompt where they conflict.\n\nGenerate the weekly LinkedIn DRAFT batch for Pipeline Layer (Rob Blake). DRAFTS ONLY \u2014 do NOT create, schedule, edit, or delete anything in GHL Social Planner. A separate job on Sunday handles scheduling after Rob's review.\n\n## Pre-flight\n1. `curl -s http://127.0.0.1:9120/ghl/health` \u2014 confirm token store alive (read-only check).\n2. Read the canonical brief IN FULL before drafting anything: `/root/.hermes/vault/LinkedIn Posting System/linkedin-brief.md` (VPS path \u2014 the Windows path in the old Cowork brief is the synced replica of this same vault). Rob edits this brief directly; never rely on memory of a prior version.\n\n## Drafting method (mirror of the linkedin-weekly-batch skill)\n3. Target week: the coming Monday\u2013Friday (tomorrow is Sunday; Monday is the day after). Compute the five dates.\n4. For each of the 5 pillars in order (Mon=Builder log, Tue=MarTech/RevOps, Wed=Applied AI literacy, Thu=Career/perspective, Fri=Industry commentary): check the brief's proof-material log for real, unused material, respecting the no-angle-repeated-within-six-weeks rule. Cross-check against prior batch files in the folder and the recently published/scheduled posts to avoid angle repeats.\n5. If a proof-log entry has a URL (a blog post Rob wrote), fetch it and draft around its real argument; end the post with one plain factual reference link (no CTA language).\n6. For pillars with no usable proof-log material, run the brief's trend-research fallback via web_search/web_extract (Reddit r/n8n + r/artificial top/week, LinkedIn-adjacent keyword searches, Google Trends US tech/business). Do NOT use Apify paid actors. Frame trend-sourced posts per the brief's hard rule 7: react to something real found in research; never claim a personal build/action/result that didn't happen; honest openers (\"saw this going around\").\n7. If both proof log and research come up thin for a pillar, leave a clearly flagged SHELL for Rob to fill \u2014 never fabricate.\n8. Voice rules from the brief apply in full: no em-dashes, no banned vocabulary, no closing summaries, no fake-candid openers, no CTAs except the blog-link exception, no comment-gating. Hook formulas: pattern-break, counter-intuitive, experience-led, stat-anchored, mechanism-reveal only. Hashtags: 5-6 per post, mixing practical/topical with a couple of sharper opinionated ones.\n9. Length: 800-1,500 chars per post body; HARD ceiling 3,000 chars including hashtags (GHL truncates silently at 3,000).\n\n## Quote card images \u2014 VAULT-FIRST (per the skill's Image Source Precedence)\n10. Before generating ANY card, check `/root/.hermes/vault/LinkedIn Posting System/` for a Rob-designed card matching each post's date or pillar (pattern: `YYYY-MM-DD_<pillar>_card.png`). If one exists, stage THAT file \u2014 it is authoritative.\n11. Generate only for posts with no vault card, using the Pillow renderer at `/root/.hermes/vault/LinkedIn Posting System/visual-assets/generate_card.py` (fonts alongside it). Editorial spec: navy background, FIG. numbering continued from the existing series, `photo_path=\"rob-headshot.png\"` (full chest-to-head shot \u2014 NEVER rob-headshot-tight-crop.jpg). Keep headline lines \u2264 ~14 chars at the template font size or they collide with the photo zone (max safe width ~500px from x=64). Pull the card copy from the post's actual argument.\n12. vision_analyze EVERY card (vault-staged and generated): confirm the card's topic matches the post's final text, check for typos/garbled text/brand name, and confirm no text overlaps the subject's face. Regenerate or re-match on any failure.\n13. Copy finished cards to `/root/.hermes/images/linkedin/linkedin_YYYY-MM-DD.png` (one per weekday date) and chmod 644. Verify each serves: `curl -s -o /dev/null -w \"%{http_code}\" https://robblake.cloud/ghl/images/linkedin/linkedin_YYYY-MM-DD.png` \u2192 200.\n\n## Output\n14. Save the batch file to `/root/.hermes/vault/LinkedIn Posting System/YYYY-MM-DD_linkedin-batch.md` (date = Monday's date), chmod 644, following the existing convention: TL;DR noting source mix and anything Rob should scrutinize, then per-post sections with Pillar/Hook line, a **Source:** line (proof log / blog post + URL / trend fallback + which source / SHELL), full post text with hashtags, and the card image filename.\n15. Also save a machine-readable copy at `/root/.hermes/work/linkedin/YYYY-MM-DD_batch.json`: array of {date, pillar, hook, source, summary, image_filename} \u2014 the Sunday scheduler consumes this. mkdir -p the work dir first.\n16. Do NOT mark the batch file as scheduled \u2014 write \"STATUS: DRAFT \u2014 awaiting Rob review\" at the top.\n\n## Delivery (final response \u2014 this goes straight to Rob)\nDeliver a concise review brief: the 5 posts shown in full (or with minimal folding), each labeled with pillar + source tier, card image paths inline so they render, plus one line: \"Reply GO to schedule as-is, or tell me which days to change \u2014 scheduling runs Sunday 9 AM your time regardless; changes requested before then get applied first.\"",
      "skills": [
        "social-media-content-pipeline"
      ],
      "skill": "social-media-content-pipeline",
      "model": null,
      "provider": null,
      "provider_snapshot": "nous",
      "model_snapshot": "moonshotai/kimi-k3",
      "base_url": null,
      "script": null,
      "no_agent": false,
      "context_from": null,
      "schedule": {
        "kind": "cron",
        "expr": "0 14 * * 6",
        "display": "0 14 * * 6"
      },
      "schedule_display": "0 14 * * 6",
      "repeat": {
        "times": null,
        "completed": 0
      },
      "enabled": true,
      "state": "scheduled",
      "paused_at": null,
      "paused_reason": null,
      "created_at": "2026-08-10T14:01:03.659976+00:00",
      "next_run_at": "2026-08-15T14:00:00+00:00",
      "last_run_at": null,
      "last_status": null,
      "last_error": null,
      "last_delivery_error": null,
      "deliver": "origin",
      "origin": null,
      "enabled_toolsets": [
        "web",
        "terminal",
        "file"
      ],
      "workdir": null
    }
  ]
}