Internal — implementation reference · July 2026 · v2

Build sheets: the full technical design of every loop.

Everything an implementer (human or AI agent) needs: verified data sources with exact table and field names, cycle algorithms, thresholds, model routing, and prerequisites. Verified live on 2026-07-07 against the NetSuite warehouse, the Odoo 19 CRM, and the vps2 infrastructure. The plain-language overview stays the front door — this page is the workshop.

01 — How to implement from this page

Four rules before any loop gets built.

  1. Read the loop's sheet + the systems it cites. Do not re-derive the queries — they were verified live on 2026-07-07.
  2. Build on the chassis — never invent new state/delivery/registration mechanics per loop.
  3. Pilot as a local supervised loop for 1–2 weeks with the recipient reading the output, then promote per the runtime on the sheet.
  4. A loop is Done only when: ≥3 real cycles ran, the recipient confirmed the digest is readable and correctly thresholded, the process is registered, and its Healthchecks check has gone green→red→green in a deliberate test.

Model routing (post-Fable, from 2026-07-08)

Stage typeModel
Harness code, verifier skills, judgment calls, high-stakes alert wording, anything customer-facingOpus
Data pulls, SQL, diffing, digest assembly, scraping, log digging — output prefixed [codex]GPT-5.5 via Codex (effectively free)
Glue stages in Workflow scriptsSonnet
Haiku: never

Standing escalation: judge output, not price — redo weak cheap-model output with a smarter model without asking. Effort scale used below: S ≤1 agent-day · M 2–4 days · L needs new data plumbing or app changes first.

Graduation doctrine (applies to every loop)

  1. Audits earn their place — a loop stays read-only until its findings repeatedly match its recipient's own judgment. Compare explicitly during the pilot.
  2. Writes unlock smallest-blast-radius first — start with the action whose worst case is trivially recoverable.
  3. Approve until it's boring — keep per-batch approval until proposals are predictable formalities.
  4. The agent never invents the target — thresholds and goals are always human-set.
02 — The loop chassis (build once, reuse)

Shared machinery every loop runs on.

Basics

  • Timezone: everything schedules and reports in Asia/Dubai (Hermes scheduler and vps2 crontabs already do).
  • State: diff-against-last-run loops keep a JSON state file in their runtime workspace (proven pattern: huxberrypm's workspace/assistant/*-baseline.json + .jsonl ledgers). Loops needing SQL-diffable history get a small table in shared Postgres reporting, schema loops (e.g. loops.inv1_snapshots). Never store state only in agent context.
  • Runtimes: deterministic steps → plain scripts (no tokens). Judgment-light scheduled loops → Hermes profile jobs (full verified reference in the next card — never hand-edit jobs.json, use the CLI). Judgment-heavy / Claude-MCP-dependent → Claude Code /schedule cloud routines, piloted as local /loop.
  • New Hermes profile bizops (Codex provider, Telegram gateway) hosts the CRM/INV/FIN/OPS digests — own HERMES_HOME, .env with Telegram bot token + Fizzy vars (FIZZY_ACCOUNT + FIZZY_API_URL + FIZZY_TOKEN — legacy FIZZY_PROFILE alone fails), a hermes-gateway-bizops.service systemd user unit, restart-notice autoclean on. DB reads via the hermes_ro Postgres role (exists; no live client config yet — wire during build).

Hermes internals — ✅ verified live on vps2, 2026-07-07

The previously-undocumented gap is closed. Everything below was read from the running system.

Creating a job — use the CLI, never hand-edit jobs.json:

sudo -u hermes /home/hermes/.hermes/hermes-agent/venv/bin/python -m hermes_cli.main \
  --profile bizops cron create '0 7 * * *' 'Self-contained job prompt…' \
  --name crm-hygiene-watchdog --deliver telegram \
  --skill some-skill --script pull-data.sh --workdir /opt/some-project
# schedule accepts '30m', 'every 2h', or a 5-field cron expr (Asia/Dubai)
# other subcommands: list · edit · pause · resume · run (fire next tick) · remove · status · tick

Job entry schema (<profile>/cron/jobs.json, verified fields):

{ "jobs": [ {
  "id": "…", "name": "health-probe",
  "prompt": "…" ,                    // agent instruction (or "" for script-only jobs)
  "skills": ["…"], "skill": "…",
  "model": null, "provider": null, "base_url": null,   // PER-JOB model/provider override
  "script": "foo.sh",                // under <profile>/scripts/; default: stdout injected into prompt
  "no_agent": true,                  // script IS the job; stdout delivered verbatim
  "schedule": {"kind":"interval","minutes":240} | {"kind":"cron","expr":"0 8 * * 1"},
  "repeat": {"times": null, "completed": 335},
  "enabled": true, "state": "scheduled",
  "deliver": "telegram" | "origin" | "local" | "platform:chat_id",
  "origin": {"platform":"telegram","chat_id":"…"},     // deliver:"origin" replies into the creating chat
  "workdir": "/abs/path",            // injects that dir's AGENTS.md/CLAUDE.md, cwd for tools
  "next_run_at": "…", "last_run_at": "…", "last_status": "ok", "last_error": null
} ] }
  • ⚠️ --no-agent design note: documented behavior is "empty stdout = silent" — that conflicts with our never-silent promise. Loop scripts must ALWAYS emit (including the all-green line); the Healthchecks ping still catches outright death.
  • Per-job model/provider override means one bizops profile can run jobs on different providers — no profile sprawl needed for model routing.
  • Gateway unit (~hermes/.config/systemd/user/hermes-gateway-<p>.service, verified): ExecStart=<venv>/python -m hermes_cli.main --profile <p> gateway run · WorkingDirectory=/home/hermes/.hermes/profiles/<p> · Environment="HERMES_HOME=…/profiles/<p>" · Restart=always, RestartSec=5, RestartForceExitStatus=75, KillMode=mixed, ExecReload=kill -USR1, ExecStopPost=gateway.cgroup_cleanup, TimeoutStopSec=210, WantedBy=default.target.
  • Profile layout (verified): config.yaml (keys: model, providers, toolsets, agent, terminal, web, browser, checkpoints, compression, prompt_caching…), .env, cron/, scripts/, skills/, workspace/, SOUL.md, state.db. The default profile IS /home/hermes/.hermes/; named profiles live under …/profiles/<p>/.
  • .env keys for a Telegram+Fizzy profile (names verified on huxberrypm): TELEGRAM_BOT_TOKEN, TELEGRAM_ALLOWED_USERS, TELEGRAM_HOME_CHANNEL, TELEGRAM_HOME_CHANNEL_NAME, FIZZY_API_URL, FIZZY_TOKEN, FIZZY_ACCOUNT (+ legacy FIZZY_PROFILE).

Hermes v0.18.0 — new capabilities to design with (updated to upstream 1c473bc6, 2026-07-07; from a 45-day / ~907-feature changelog scan)

  1. Structural approval enforcement: user-defined deny rules that block commands even under yolo; a pre_tool_call plugin action that deterministically escalates any tool call to the human gate; per-tool MCP gating. "Never autopilot" becomes enforced code: Odoo reads open, write-tools gated, deny rules under every loop profile. pre_tool_call is also the upgrade path to deterministic (non-LLM-turn) Telegram approvals.
  2. Event-driven loops: authenticated /api/cron/fire webhook + per-profile /p/<profile>/ inbound routing + at-most-once fire semantics — DATA-0 can fire on ETL completion instead of a clock; HUX-L5's fatigue→creative trigger gets a clean mechanism.
  3. Cost data for IT-3: hermes -z --usage-file emits per-run token/cost JSON; /usage per-category breakdowns; per-profile observability. No per-cron-job rollup exists — IT-3 builds that aggregation itself.
  4. SecretSource is pluggable (1Password + self-hosted Bitwarden shipped): write an Agent Vault source and retire per-profile .env shims.
  5. Email reality check: no native SMTP cron delivery exists — email digests are sent BY the job's script (gws CLI, the proven huxberrypm brief-email pattern) with deliver: local.
  6. Build conveniences: per-channel/per-session model overrides that persist across restarts; Cron Recipes (package our digest/watchdog/goal archetypes as stampable templates); continuable cron delivery (reply to a digest → continues the loop's session — the "tell it what to change" UX free); dashboard profile cloning = fastest bizops setup; /goal completion contracts + /goal wait for INV-2; web_extract truncate-and-store + free Parallel web search cut scraping costs (HUX-L2, CRM-3, INV-4).
  7. Validated paranoia: still no native heartbeat/missed-run handling — the Healthchecks dead-man's-switch stays mandatory. ⚠️ Session auto-reset now defaults to off — check profile expectations after this update.

Delivery surfaces (never build new ones)

  • Email digest — the starting channel (nothing to install for recipients). Sent BY the job's script via the gws CLI with deliver: local — Hermes has no native SMTP cron delivery (verified v0.18.0).
  • Telegram — via a Hermes profile job with deliver: local, the script sends its own message; raw-stdout deliver: telegram is audit-only.
  • Feed card — a PullEntry in config/metabase-pull.ts (repo ~/Documents/Projects/company-feed): questionId, feed slug, title, mode: "upsert"|"on-change", metricColumn, unit, optional tiles[]/chart/sourceUrl. Metabase question must exist first (author via metabase-admin skill, [Card] <Area> — <Metric> naming). Deploy: branch off feat/m1-vertical-slice, typecheck+test, PR, pull + docker compose build app pull + up -d app on vps2, run run-pull.sh, verify status:"published" http:202. Composition is all-or-nothing.
  • Approval gate — Telegram Approve/Reject buttons → Fizzy (huxberrypm pattern). ⚠️ Button taps route through an LLM turn, not a deterministic callback — fine for batches, don't design per-item rapid-fire approvals.

Registration checklist (every loop, no exceptions)

  1. Row in systems-handbook/wiki/processes.md — table Process | Coordinated via | Status | What it does, name Process - Hermes - <Description>.
  2. Healthchecks check via API (healthchecks.huxapps.com, ping URL /ping/<uuid>; cron-type with the loop's schedule + 1h grace). Ping on success only, at the END of the cycle.
  3. Named recipient confirmed they want it, and at what cadence.
  4. Week-1 cost review (/usage for Claude runtimes; token logs for Hermes).

Universal guardrails: read-only vault-managed credentials by default · any system-of-record write or outward message = human approval · "nothing found" is a reported result · every digest ends with a data-freshness stamp (source + as-of date).

03 — Build sheets by domain

Every loop, full detail.

Status pills: Ready data verified, build now · Needs prep small investigation/setup first · Needs plumbing new data connection required.

Data trust — recipient: Nish

DATA-0Pipeline & freshness sentinel
ReadyWave 1
Why
Found 2026-07-07: the NetSuite warehouse ETL had failed daily since 06-27 (raw.itemsduplicate key … items_pkey, 11 consecutive failures, no table synced after the abort) — every inventory dashboard silently ~12 days stale. Fix session run same day; sentinel makes recurrence impossible to miss.
Cadence · runtime
Daily 08:00 (after 04:00 joinery refresh, 07:15 warehouse ETL, 07:30 feed pull) · Hermes vpsops extension.
Data (verified)
netsuite_warehouse.raw.sync_log (table_name, started_at, finished_at, rows_loaded, status, error_message); freshness via max(synced_at) on raw.items/raw.transactions; Company Feed publisher_runs + logs/pull.log; GBrain source timers (netsuite-to-brain was failing in the June audit, odoo-crm-to-brain, calendar-to-brain); gbrain-dream.timer via /opt/gbrain/logs/dream.log; Healthchecks API all-checks status.
Cycle
(1) sync_log last 48h → any status != success or rows_loaded = 0; (2) freshness: warehouse synced_at > 26h ⇒ RED, feed pull missing today's published ⇒ RED; (3) timers failed/stale > 2 days ⇒ AMBER; (4) one status line per pipeline (✅/⚠️/❌ + as-of + error excerpt); (5) on RED, diagnosis paragraph (only judgment step); (6) ping own Healthchecks check.
Output
One message. Green days: single line "All pipelines green (warehouse as-of <date>)". Never skips — silence must mean the sentinel died (caught by its own check).
Build
Codex end-to-end; Opus reviews the diagnosis prompt once. Effort S. Prereq: ETL fix confirmed holding.

Sales & CRM — recipient: Rajiv + BU leads

CRM-1CRM hygiene watchdog
ReadyWave 1
Verified baseline
2026-07-07 live: 1,144 active leads; 1,068 (93.4%) no next activity; 559 of 635 open (non-Won) missing next step; 0 overdue activities exist; lost missing reason 163/236; source_id/medium_id/campaign_id 100% unset; 634 active missing both email+phone.
Field map (Odoo 19)
crm.lead: activity_date_deadline (false = none), activity_state (overdue|today|planned|false), stage_id, team_id, user_id, expected_revenue, won_status, lost_reason_id, active, email_from, phone, country_id, date_last_stage_update, duration_tracking (JSON seconds-per-stage), duplicate_lead_count, rotting_days/is_rotting (native but thresholds unconfigured — 0 records match). Stages (global; crm.stage.team_id does not exist): 1 New · 2 Qualified · 3 Proposition · 5 High probability · 4 Won. Teams: 1 BR (406) · 4 Construction (94) · 5 Hospitality (644) · 6 OEM (0) · 2 Website (0). ⚠️ Default search excludes active=false — lost-lead queries need explicit ["active","=",false].
Checks
  1. No next step: open (stage_id != 4, active) with activity_date_deadline = false — per team + top 10 by expected_revenue (daily).
  2. Overdue: activity_state = 'overdue' — list all (daily).
  3. Stale in stage: date_last_stage_update older than New>14d, Qualified>30d, Proposition>21d, High-prob>14d (daily; tune with Rajiv after week 1).
  4. Lost without reason (weekly). 5. Duplicates duplicate_lead_count > 0 (weekly). 6. Contact gaps (weekly). 7. Dead teams OEM/Website at 0 (weekly line).
  5. Hygiene gates (ex-GTM design): dup rate <5% + normalized-identity check before new records (manufacturing GTM fails at ~23% dup benchmark); personal/free-mail domains (Gmail/Yahoo regex) flagged; LinkedIn URLs normalized (lowercase, strip params) wherever stored.
Fixes (gated)
Proposes batches ("schedule follow-up To-Do in 7 days on these 12 deals") via Approve/Reject → applied through the odoo-write skill only, per-batch approval.
One-time setup
Configure stage/team rot thresholds so is_rotting works natively; add Activity Plans (Odoo CRM NG A5). ~1 hour.
Build
Codex queries + digest; Opus thresholds, proposal prompt, write-gate wiring. Effort S. Zero blockers.
CRM-2Pipeline pulse
ReadyWave 3
Verified baseline
New 297 deals / AED 229.6M (junk-inflated — CRM-1 must run first), Qualified 211 / 70.3M, Proposition 109 / 63.0M, High-prob 18 / 19.4M, Won 509 / 124.9M.
Cycle
Weekly Monday: snapshot to loops.crm2_snapshots(run_date, team_id, stage_id, count, value) → WoW movement per team (new/advanced/won/lost/value deltas; Odoo keeps no aggregate history — we accumulate our own) → biggest 5 moves narrated → per-team one-screen brief. duration_tracking gives stage velocity free.
Output
Four sales-* feed cards (headline: open pipeline value; tiles: WoW delta, won-this-week; stacked-bars chart by stage) + per-lead digest.
Build
Codex; Opus narration template once. Effort S–M (card wiring ×4). Prereqs: ≥2 weeks CRM-1 cleanup + 2 weeks of snapshots.
CRM-3Signal watch & qualification (hospitality first)
Needs prepWave 3
Why hospitality first
Marriott/IHG dropped the Hypnos brand globally — a large share of hospitality revenue needs replacing; Ifham prospects alone. Verified market signal: 717 ME hotel projects / 177,110 rooms, 202 in Early Planning (+36% YoY, Lodging Econometrics Q1-2026).
Sources (two tiers)
Tier 0 (free, ships first): Codex sweeps GCC hospitality-development news, tender portals, permit announcements. Tier 1 (paid, on conversion evidence): TOPHOTELPROJECTS own seat ~€85–250/mo (≈8,500 projects + ~20,000 decision-maker contacts included; per-user license — get our own seat, don't share partner logins); BNC Network pay-as-you-go or Ventures Onsite (KSA/UAE site granularity) for the Construction variant; Lodging Econometrics contact programs as alternative.
Qualification gate
Mandatory before a human sees it: company legal entity verified by the signal source · sector · specific Emirate/GCC country · title mapped to persona (Hospitality: Owner/Operator/GM/Brand Director; Construction: PM/Procurement Director/Lead Architect).
Cycle
Weekly: sweep → gate → Opus qualifies vs ICP → shortlist w/ source links + angle → Ifham → per-lead 👍 → crm.lead (team_id 5) via odoo-write, per-record approval, source stamped. Construction clone for Yasar. Qualified signals feed B2B-L4.
Build
Effort M — qualification prompt needs 2–3 iterations with Ifham.

Inventory & purchasing — recipient: Lerris

INV-1Dead-stock & aging watchdog
ReadyWave 1
Verified finding
Of 1,284 items with on-hand > 0: 898 dead (779 never on any WO/SO/PO line, 119 last moved 6+ months) = AED ~989,340 of 4,151,729 on-hand value (~24%).
Data
raw.item_inventory, raw.items (averagecost, displayname, parent_id), raw.transactions + raw.transaction_lines. Verified base query:
WITH last_txn AS (SELECT tl.item_id, max(t.trandate) AS last_txn_date
  FROM raw.transaction_lines tl JOIN raw.transactions t ON t.id=tl.transaction_id GROUP BY 1)
SELECT i.id, i.displayname, inv.quantityonhand, i.averagecost,
       inv.quantityonhand*coalesce(i.averagecost,0) AS value, lt.last_txn_date
FROM raw.item_inventory inv JOIN raw.items i ON i.id=inv.item_id
LEFT JOIN last_txn lt ON lt.item_id=inv.item_id
WHERE inv.quantityonhand > 0
  AND (lt.last_txn_date IS NULL OR lt.last_txn_date < now() - interval '6 months')
Caveats (state in every digest)
"Movement" = WO/SO/PO line appearance only — fulfillments/receipts/adjustments aren't in the warehouse; WO history starts Nov-2024, so "never transacted" overstates deadness of older stock. Directionally strong, not audit-grade.
Cycle
Weekly Mon 08:30: buckets (never / 6–12m / 12m+) → snapshot loops.inv1_snapshots → diff (newly dead, revived) → top 20 by value grouped by parent_id family → Codex drafts / Opus reviews liquidation shortlist (proposals only) → freshness stamp from DATA-0.
Output
Digest + inventory feed card (headline: dead-stock value; tiles: item count, % of stock value, WoW delta).
Build
Codex SQL/snapshot/diff; Opus shortlist judgment + card wiring. Effort S. Prereqs: ETL fix; Metabase schema re-sync (its metadata only knows 8/16 warehouse tables — GUI questions can't see transaction_lines until re-synced).
INV-2MRP data burn-down (goal-loop)
Needs plumbingWave 3
Blocked because
Verified: raw.items has NO lead-time / reorder-point / safety-stock / preferred-vendor columns — not extracted from NetSuite (where only 9% of items have lead times, 0% reorder points, 35% missing vendor). Fix inside the same ETL extension as FIN-1.
Design
Repeated goal-runs, not a watchdog: "propose MRP values for the next 50 items by usage value; stop when all 50 have a proposal + evidence." Evidence: PO-interval history from mart.purchase_orders + lines, vendor from last POs, demand variability from WO/SO lines. Output = xlsx review batch for Lerris; approved values load into Odoo during migration data-prep (no NetSuite write path — values would die with NetSuite anyway). Coverage dashboard: % items with approved lead time / reorder point.
Build
Codex derivations; Opus sanity-judges per batch. Effort M. This loop IS Odoo Phase-1 MRP data-prep.
INV-3Reorder-risk watchdog
Needs plumbingWave 4
Design
Daily, once INV-2 coverage >60%: quantityavailable + quantityonorder vs approved lead time + demand rate → "order by <date>" for long-lead items (fabric 40–95 days first); open-PO netting via mart.purchase_orders. Recipient Lerris/Ajeesh. Codex. Effort S after INV-2.
INV-4Supplier & commodity intelligence
Needs prepWave 3
Design
Rebuild of the retired Hermes nhc supplier job (deleted 2026-05-29; only 3 of 7 jobs documented, all prompt text lost — write fresh). Cadence 1st + 15th. Scope: standing watchlist (Wise Wool, Lenzing/Tencel, Yatas foam CertiPUR, Gobi cashmere) + strong-wool commodity price. Delivery: report into the wiki's raw/inbox/ → 07:00 Wiki Router auto-ingest (pattern verified live) + Telegram summary. Codex scrapes/drafts, Opus reviews before inbox drop. Effort S.

Finance — recipient: Shahbaz (Rajiv oversight)

FIN-1AR / collections watchdog
Needs plumbingWave 3
Blocked because
Verified: warehouse has no invoice data at all — transaction types are only WorkOrd/SalesOrd/PurchOrd; no duedate, no amount-remaining column anywhere (checked information_schema).
Unblock (choose)
(a) Extend the ETL (recommended): add CustInvc (+ CustPymt) to /opt/NetsuiteReporting with duedate, amount-remaining, entity — ~1 Codex day incl. sync_log wiring; also unblocks FIN-2 v2 + the finance feed. (b) Direct SuiteQL per run — no ETL work, slower, invisible to Metabase.
Cycle (once data lands)
Daily 08:00: aging buckets 0-30/31-60/61-90/90+ by customer; NEW overdue since yesterday (state diff); top 10 by amount; Codex drafts chase-email text (saved to digest, never sent); assigned-debtor invoices flagged for bank-routing (feeds FIN-2).
Output · build
Digest + finance feed card. Codex ETL + queries; Opus chase-email tone (outward text = ships). Effort M.
FIN-2Bank compliance calendar
ReadyWave 2
Watches
The obligations attached to our two bank facilities: reporting deadlines (audited financials within a fixed window of FYE; half-yearly projections), facility review/renewal dates, monthly covenant self-checks (net-worth floor, leverage cap, profit-retention share), and turnover-routing shares — including which assigned customers' payments must route through which bank.
Cycle (v1 — calendar mode)
Monthly, 1st business day: date-driven reminders → checklist to Shahbaz ("confirm covenant figures from latest management accounts — reply with numbers") → replies logged to loops.fin2_attestations (the audit trail banks like) → escalate to Rajiv if a month goes unanswered → flag new contracts with assigned debtors for correct routing.
v2
After FIN-1's ETL extension: compute invoice-value share by assigned debtor and flag routing drift quantitatively.
Build
Codex; Opus escalation wording once. Effort S. No blockers.
Deliberate redaction: bank names, facility amounts, and exact covenant figures are kept off this public page. The precise values live in Finance's records and the loop's private config — the loop design doesn't change.
FIN-3Ad-spend three-way match
Needs prepWave 4
Cycle
Monthly: platform spend (google-ads-pp-cli — needs developer token + OAuth in vault; Meta via Marketing API/vault-curl or the ads-skills repo scripts) vs agency/platform invoices (Gmail/Drive search) vs bank-out lines (manual statement extract from Shahbaz's team in v1 — don't over-automate). Output: reconciliation table, unexplained gaps flagged, running total vs the AED 720K precedent. Effort M. Prereq: ads API credentials (same unlock as HUX-L3).
FIN-4Expense-queue nudge
Needs prepWave 3
Data
Expense app (expenseapptest.huxapps.com, prototype): Postgres db expenseapptest (container-local). Documented API is only /api/health + /api/extract-receiptread the queue directly from Postgres (read-only role via SSH tunnel). States: pending review → approved / rejected / correction-requested (→ back to pending).
Cycle
Weekly, only when something is stuck: claims pending > 7d, corrections unanswered > 7d, per-division submission counts. Effort S. Go-live gated on the app's production sign-off (backup/restore drill checkpoint).

Operations & construction — recipients: Yasar, Ifham, factory leads

OPS-1Retention & DLP tracker
Needs prepWave 2
Data reality
Verified: home is the Joinery Billing App — SQLite billing.db at /opt/joinery-billing/data/ (container mig-joinery-billing): contract value, advance %, retention %, MIR/WIR/TOC/DLP milestones. ⚠️ Schema undocumented; whether DLP dates are computed/populated is unconfirmed. NetSuite holds retention accounting (fields undocumented); joinery reporting mart and Traxx have none of it (verified absent).
Step 0 (built into the loop)
Dump schema from a read-only COPY of billing.db (never the live file), locate project/milestone/retention tables, check TOC-date population. Branch: (a) populated → weekly DLP-due alerts at T-90/30/0 with retention amounts + TOC-passed-retention-not-invoiced flags; (b) not populated → backfill campaign: ask Yasar's team 5 projects/week until ~100% coverage, then switch to (a).
Build
Codex schema/queries/digest; Opus branch decision + alert wording. Effort S–M. Recipients Yasar + Shahbaz; later a finance feed card ("retention receivable due next 90 days").
OPS-2MIR/WIR cycle-time monitor
Needs plumbingWave 4
Honest status
Verified: Traxx has no MIR/WIR/TOC fields; the joinery mart has none; billing.db may hold milestone timestamps (OPS-1's step-0 dump answers this). If timestamps exist → weekly stage-duration report (median days Delivered→MIR, Installed→WIR, per project) vs targets set with Yasar. If not → this becomes a feature request on the Joinery Billing app (add milestone dates) — flag and stop, don't pretend. Effort M–L.
OPS-3Protector replenishment radar
ReadyWave 2
Data (verified)
Join path works: customer = raw.transactions.entity_id/entity_name (type='SalesOrd'), item family via raw.items.parent_id hierarchy, qty = transaction_lines.quantity. ⚠️ SO line quantities are negative (NetSuite sign convention — use abs()); SO history thin pre-2025 (~1.5 usable years).
Cycle
Monthly to Ifham + Mithun: hotel customers who bought protectors 10–14 months ago and haven't reordered — v1 is a reorder-window report, not full forecasting (history too thin). Effort S.

IT & governance — recipient: Nish

IT-1Wiki & GBrain hygiene
ReadyWave 2
Key design fact
gbrain-dream.timer already runs a ~20-phase self-maintenance pass every 4h (lint, backlinks, sync, orphans — logs /opt/gbrain/logs/dream.log). IT-1 supervises and fills gaps, not duplicates.
Cycle
Weekly Sunday (Claude runtime — needs the /lint skill): (1) dream.log health; (2) generated-source timers status (netsuite-to-brain was failing in June); (3) for wikis edited this week (git log --since), run gbrain sync --source <id> via the docker-exec wrapper — the hourly wikis-sync timer only git-pulls, it does NOT re-embed; (4) /lint <wiki> --fix on 2 highest-churn wikis, rotating (auto-fix frontmatter/index drift only); (5) first run: fix the live merge-conflict artifact in vps-architecture/index.md frontmatter (being indexed into GBrain as-is).
Build
Opus + Codex mechanical checks. Effort S.
IT-2Workspace security posture
Needs prepWave 4
Cycle
Monthly GAM sweep (after the one-time 2SV/OU hardening project): 2SV enrollment coverage, Super Admin count, OAuth grants (gam print tokens) diffed vs last month, suspended/never-logged-in accounts vs offboarding checklist, forwarding rules, root-OU population. Deltas only. Codex over the google-workspace-cli skill. Effort S.
IT-3The loop-of-loops
ReadyWhen ≥4 live
Cycle
Weekly: Healthchecks API all-checks; cron list + recent logs per Hermes profile; token/cost per profile and Claude routine; each loop's last-3-cycles outcomes (state files). Scorecard: healthy / stalled / noisy / over-budget / kill-candidate. Killing a useless loop is a success. Opus. Effort S.
04 — Huxberry marketing (D2C) build sheets

L0–L6, with the reference playbook.

Reference implementation — bookmark, don't clone yet: ivangfalco/ads-skills (ColdIQ; $300K/mo managed spend from Claude Code; ~40 strategy files + 39 Python API scripts; MIT + Commons Clause — free to use/adapt internally, cannot be resold). Clone at build time and reuse the API scripts (drops HUX-L3 effort M→S) and the audit-checklist structure — but NOT its strategy content for D2C (it's B2B demand-gen; build a hux-paid-ads knowledge skill with our GCC/D2C thresholds instead; its B2B files transfer to §05 nearly as-is).
Credential gotchas: Meta long-lived tokens die ~2 months → wire into the assetops cred_expiry_check loop from day one; Google needs a developer token via an MCC manager account (request early); LinkedIn Advertising API approval takes days.

HUX-L0Tracking foundation (one-time project)
Needs prepWave 1
Scope
Shopify-native installs: Meta pixel + Conversions API, GA4 + enhanced e-commerce, Google Ads conversion import, Search Console verification (unblocks L2's GSC data). O2O: per-campaign promo codes, QR codes on creatives, "how did you hear about us?" at checkout. Every ads loop is blind without it — the AED 720K happened because this was never done. Owner decision pending (agency dispute).
HUX-L1Ad creative factory (goal-loop)
ReadyWave 2
Cycle
Brief → Opus concepts + copy (3 concepts × formats) → verifier = hux-brand-guidelines skill as a scored checklist (palette/typography/tone/claim-safety, pass = all mandatory) → Codex export manifest (sizes per placement, naming) → upload to Directus DAM review folder via directus-pp-cli (the ONE sanctioned write, scoped to the review folder, "AI Agent" role) → Sherif approves in DAM. Never touches Meta/Google. Post-Fable: add one extra Sherif review cycle in month 1. Effort S–M.
HUX-L2SERP & competitor watch
ReadyWave 2
Cycle
Daily: rankings for ~10 money keywords ("organic mattress dubai" — we're #2, "luxury mattress dubai" #3, "best mattress for hot climate" — open gap) via ahrefs-pp-cli/semrush-pp-cli (vault keys) + google-search-console-pp-cli after L0 (CLI only, no MCP); Codex scrape-diff of Heveya + 3 competitors' PDPs/prices/promos; position state file; digest to Sherif + ecommerce feed card. Effort S.
HUX-L3Ads watchdog (daily + weekly ritual)
Needs plumbingWave 3
Daily
Spend pacing over/under, disapprovals, threshold breaches — e.g. any ad set > AED 500 spend / 0 conversions / 7 days ⇒ RED.
Weekly ritual (ColdIQ's $300K/mo routine)
(1) zero-conversion search terms to exclude; (2) creative fatigue — CTR-drop per ad; (3) audience saturation (frequency creep — distinct from creative fatigue); (4) quality-score audit — where we overpay or miss demand; (5) WoW compare with a written "what moved and what I'd do" narrative.
Build
Repo scripts or google-ads-pp-cli + Meta Marketing API via vault-curl. Effort S with repo scripts (M from scratch). Prereqs: L0; Meta token renewal wired into cred_expiry_check.
HUX-L4Content pillar loop
ReadyWave 3
Cycle
Weekly: next backlog item (Gulf-climate sleep pillar page first — biggest SERP gap, zero GCC content) → Opus drafts with L2's keyword data → SEO checklist + brand verifier → review by Sherif/Nish → on approval publish to Shopify blog via MCP (write = gated). Effort S per piece once the harness exists.
HUX-L5Creative refresh (composite)
Needs prepWave 4
Cycle
L3 fatigue trigger (frequency ↑ / CTR ↓ thresholds) auto-opens an L1 run into the DAM review folder. Build after L1+L3 have a clean month. Effort S — wiring, not new machinery.
HUX-L6Negative-keyword janitor (first gated write)
Needs prepWave 4
Cycle
Weekly: L3's zero-conversion terms (> AED 100 spend / 0 conversions / 30 days) → exclusion list with spend evidence to Sherif → on Approve, adds negative keywords via API (repo's negative-keywords script + our gate). Unlock: L3 audits match Sherif's judgment ≥4 consecutive weeks. Worst case = we stop paying for a junk query (reversible). Per-batch approval always; never auto-runs in v1. Effort S.
05 — B2B pipeline & automated go-to-market build sheets

The signal engine, vendor verdicts, and phased costs.

Story and market case: the go-to-market page. This section is the technical residue of the absorbed "Auto Go To Market" design (July 2026, incl. its 64-claim fact-check) — the original folder is superseded.

B2B-L1LinkedIn account watchdog & audit
Needs plumbingOn decision
Design
Day one: the ads-skills repo's 35-item account audit as baseline, fixes ranked by impact. Weekly: spend pacing, zero-converting audiences, bid efficiency, converting job titles / company sizes per campaign, CTR fatigue. Recipient: B2B marketing owner (TBD) + BU lead. Read-only. Effort S with repo scripts. Prereqs: budget+owner decision, LinkedIn ads account, Advertising API approval (days — request now, free), creds in vault.
B2B-L3Lead-form capture
Needs plumbingOn decision
Design
LinkedIn lead-gen forms → qualified vs ICP → proposed as crm.lead via odoo-write, per-record BU-lead approval, source/medium/campaign stamped at creation (today 100% of deals have no source recorded). Reply-only sync principle: contacts enter the CRM only on real interaction — never raw list imports. Shares CRM-3's qualification harness. Effort S once B2B-L1 exists.
B2B-L2Audience builder from our CRM (gated write + data export)
Needs plumbingOn decision · last
Design
Matched/custom audiences from our own lists (GCC hotel chains for Hospitality ABM, big-box buyer titles for OEM) uploaded to LinkedIn/Meta. Every upload per-batch approved after list review; business contacts only; UAE PDPL consent check with Rajiv/legal before the first upload. Depends on CRM-1 cleanup + B2B-L1 proving itself. Effort M. Goes last — ascending blast radius.
B2B-L4Outbound assistant — the Signal-to-Odoo engine, human-sent
Needs prepPhase 1
Pipeline
On a qualified CRM-3 signal: match (v1: TOPHOTELPROJECTS included contacts + Sales Navigator + Apollo free tier ~10k email credits/mo; v2: Clay Growth waterfall stacking Prospeo/Hunter/Apollo — lifts MENA find rates past the ~60% single-database ceiling) → verify (SMTP-checked email, corporate domains only, <5% dup gate, normalized identity) → draft a message referencing the specific project + stage (quality generic fallback if no signal detail) → deliver the ready-to-send package to Ifham/Yasar, who send from their own accountson reply, propose contact + interaction into Odoo (odoo-write, per-record approval, reply-only, source stamped).
Why human-send
The original design's own fact-check: ALL LinkedIn automation (Expandi/HeyReach/Waalaxy) violates LinkedIn's User Agreement, with active enforcement — HeyReach's own company page banned Mar 2026; published "safe rate limits" are third-party folklore (LinkedIn enforces by behavioral detection, no numeric policy exists). Human-send = zero ban risk; at two senders, volume was never the constraint.
Weekly reports (to the humans, about their own outreach)
Connection-acceptance rate — pause and recalibrate below 30%; reply rate; invites pending >14–21 days to withdraw. If a new/dormant account ever needs warm-up (human-operated): wk1 = 5 invites + 20 profile views/day, wk2 = 10+40, wk3 = 15+60, wk4 = normal.
Build
Effort M. Prereqs: CRM-3 live; Phase-1 budget on conversion evidence.

Vendor verdicts (fact-checked 2026-07-02 — preserved so we never re-litigate)

ToolVerdictWhy
Clay (Growth $495/mo, $446 annual)✅ v2 only, after Phase-1 conversionOnly tier with HTTP API + webhooks (Odoo sync); the multi-provider waterfall is the real MENA find-rate edge
Apollo✅ free tier for v1~10k email credits/mo free; NO native Odoo integration — use Captivea cap_apollo_connector (Odoo v19, $29.87) or Balane apollo_connector (v16–19, CSV any plan)
Evaboot ($9–99/mo)✅ replaces Scrupp$0.02–0.09/contact, credits roll over, clean billing record
Sales Navigator (~$100/seat/mo)✅ base layerGold standard for GCC decision-maker identity
Albato/Make✅ v2 middlewareLive Clay↔Odoo integration exists on Albato (trigger → Create/Update Lead)
TOPHOTELPROJECTS✅ own seat (~€85–250/mo)~8,500 hotel projects + ~20,000 decision-maker contacts included; per-user license — never share logins
BNC Network✅ pay-as-you-go entryProject signal only (no contacts included; CRM sync add-on top tier only) — people-finding stays with Apollo/Clay/Evaboot
Scrupp❌ droppedPricing was misstated; 2026 reviews report broken cancellations + silent auto-renewals
Cognism❌ droppedMENA-coverage claim unsupported (real strength EU/UK; $15–50k/yr)
ZoomInfo❌ avoid$15k–45k+/yr; stale MENA data
Expandi / HeyReach / Waalaxy❌ not in v1All violate LinkedIn's UA; active enforcement. Return only via explicit accepted-risk sign-off — human-send likely makes them unnecessary

Phased costs (corrected — the original plan's $743–1,090/mo omitted Sales Nav + both signal databases)

PhaseGateNew paid toolsMonthly
0 — prove demandnone (already Wave 3)None — CRM-3 Tier-0 public sweepsAED 0
1 — validatePhase-0 shortlists convert (or conviction)Sales Nav ×2 (~$200) · TOPHOTELPROJECTS Essential (~€85–90) · BNC PAYG ($50–100) · Evaboot ($29–49) · Apollo free · Odoo connector ($29.87 one-off)~$450–650
2 — scale findingPhase-1 reply/meeting metrics+ Clay Growth ($446 annual) + Albato. Sending stays human by default~$1,100–1,400 all-in