OpenOutreach B2B compose + Aaron narrative + Frappe CRM schema + social proof handoff.
B2B email + social proof bridge — separate risk surfaces.
#!/usr/bin/env python3
"""Outreach Bridge Agent
Hybrids: OpenOutreach ICP pack · Aaron narrative skills · Frappe DocType schema
· simplified-cli assets · social handoff (not email spam)
"""
from __future__ import annotations
import json, os
from dataclasses import dataclass, asdict
from pathlib import Path
from typing import Literal
Verdict = Literal["PASS", "FAIL", "INCONCLUSIVE"]
@dataclass
class R:
name: str
verdict: Verdict
detail: str
evidence: dict
def openoutreach_compose() -> R:
# Produces onboarding payload for docker OpenOutreach — does not send mail here
icp = os.environ.get("ICP", "VP Engineering at Series B SaaS")
product = os.environ.get("PRODUCT", "manager.addict.best social ops plane")
payload = {
"product": product,
"icp": icp,
"channels": ["email"], # browserless B2B — zero social ToS surface
"followup_turns": 3,
"self_promo_fraction": 0, # disable freemium promo if forking
"docker": "ghcr.io/eracle/openoutreach:latest",
}
path = Path(os.environ.get("OUT_DIR", ".")) / "openoutreach_job.json"
path.write_text(json.dumps(payload, indent=2))
return R("openoutreach", "PASS", str(path), payload)
def aaron_narrative_brief() -> R:
brief = {
"trace": "pain: multi-account chaos",
"architect": "promise: one hybrid control plane",
"land": "proof: queue+stealth+gates",
"evaluate": "metric: publish without checkpoints",
"gate": "SHIP",
}
if not brief["promise" if False else "architect"]:
return R("aaron-narrative", "FAIL", "BLOCK empty", {})
path = Path(os.environ.get("OUT_DIR", ".")) / "narrative_brief.json"
path.write_text(json.dumps(brief, indent=2))
return R("aaron-narrative", "PASS", "SHIP brief", brief)
def frappe_doctypes() -> R:
# Schema blueprint for CRM under social/B2B manager
types = [
{"doctype": "SocialAccount", "fields": ["platform", "handle", "profile_path", "proxy", "health"]},
{"doctype": "PublishJob", "fields": ["account", "media", "caption", "status", "verdict"]},
{"doctype": "OutreachLead", "fields": ["company", "role", "email_conf", "stage"]},
{"doctype": "IncidentEvent", "fields": ["code", "level", "action", "until"]},
]
path = Path(os.environ.get("OUT_DIR", ".")) / "frappe_doctypes.json"
path.write_text(json.dumps(types, indent=2))
return R("frappe-schema", "PASS", f"{len(types)} DocTypes", {"path": str(path)})
def social_handoff_pack() -> R:
# After email interest → soft social proof post (own content)
pack = {
"ig_story": "Customer win (permissioned) — 15s",
"linkedin": "Lesson learned shipping multi-account ops",
"source": "outreach-bridge",
"rule": "own content / permissioned logos only",
}
path = Path(os.environ.get("OUT_DIR", ".")) / "social_handoff.json"
path.write_text(json.dumps(pack, indent=2))
return R("social-handoff", "PASS", str(path), pack)
def main():
Path(os.environ.get("OUT_DIR", ".")).mkdir(parents=True, exist_ok=True)
results = [openoutreach_compose(), aaron_narrative_brief(), frappe_doctypes(), social_handoff_pack()]
if os.environ.get("SIMPLIFIED_API_KEY"):
results.append(R("simplified-cli", "INCONCLUSIVE", "run ai-image externally if needed", {}))
else:
results.append(R("simplified-cli", "INCONCLUSIVE", "no key", {}))
fails = [r for r in results if r.verdict == "FAIL"]
inc = [r for r in results if r.verdict == "INCONCLUSIVE"]
status = "FAIL" if fails else ("INCONCLUSIVE" if inc else "PASS")
# note: simplified INCONCLUSIVE alone shouldn't block compose — treat soft
hard_fail = fails
status = "FAIL" if hard_fail else "PASS"
# If only soft INCONCLUSIVE on optional tools, PASS compose
if not hard_fail and inc:
status = "PASS"
report = {"status": status, "all_pass": status == "PASS", "results": [asdict(r) for r in results]}
print(json.dumps(report, indent=2))
Path(os.environ.get("OUT_DIR", "."), "outreach_report.json").write_text(json.dumps(report, indent=2))
raise SystemExit(0 if report["all_pass"] else 2)
if __name__ == "__main__":
main()# OpenOutreach side-car (B2B email — not social scraping) docker run --pull always -it \ -v ~/.openoutreach/data:/app/data \ ghcr.io/eracle/openoutreach:latest # Then load openoutreach_job.json ICP during onboarding