mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-06-17 23:09:35 +00:00
Launch Ops (5 modules) — برج إطلاق الـ Private Beta
- private_beta: 499 SAR × 7-day offer + safety notes + 6-question Arabic FAQ
- demo_flow: 12-minute minute-by-minute Arabic demo + 5 discovery Qs + 6 objection responses + close script
- outreach_messages: 4 segments × 5 prospects = 20 + per-segment Arabic messages + 3-step follow-ups + 6 reply handlers
- go_no_go: 10-gate readiness + 3 critical gates (no_secrets/live_sends_disabled/staging_health) + verdict + next-actions
- launch_scorecard: 11 event types + daily/weekly aggregation + targets (20 outreach/5 replies/3 demos/1 pilot daily)
Revenue Launch (7 modules) — تحويل Dealix إلى دخل
- offer_builder: 4 offers (Private Beta, 499 Pilot, Growth OS Pilot 1.5-3K, Free Case Study) + segment-aware recommend
- pipeline_tracker: 8-stage deterministic pipeline + add/update/summarize + revenue tracking + win rate
- outreach_sequence: re-export single source of truth from launch_ops with revenue-tier wrappers
- demo_closer: re-export from launch_ops
- pilot_delivery: 12-field intake form + 5-phase 24h delivery plan + per-service templates (First 10 / List Intel / Free Diagnostic)
- proof_pack_template: 5-line Arabic client summary + ROI estimate (pipeline_x + closed_won_x) + next-step recommendation (upsell/iterate/extend)
- payment_manual_flow: Moyasar invoice step-by-step (halalas-correct) + Arabic payment-link message + confirmation checklist; NEVER charges via API
Service Tower extensions (2 modules)
- contract_templates: re-export targeting_os contracts + new SLA outline (legal_review_required, PDPL-aware)
- vertical_service_map: 6 verticals (B2B SaaS / agencies / training-consulting / real estate / healthcare-local / retail-ecommerce) with primary+supporting services + buyer roles + common pains + winning offer
Routers (2 new) — 29 endpoints
- /api/v1/launch/* — 11 endpoints (private-beta/offer, demo/flow, outreach/{first-20, message, followup}, go-no-go, readiness, scorecard/{event, daily, weekly, demo})
- /api/v1/revenue-launch/* — 18 endpoints (offers + offers/recommend, outreach/{first-20, followup}, demo-flow, pipeline/{schema, summarize}, pilot-delivery/{intake-form, 24h-plan, first-10, list-intelligence, free-diagnostic}, payment/{invoice-instructions, link-message, confirmation-checklist}, proof-pack/{template, client-summary, next-step})
Tests (2 new files, 56 tests)
- test_launch_ops.py: 25 tests (Private Beta offer essentials + Arabic FAQ; demo flow 12-min structure; first-20 segments × 5; outreach Arabic + drafts only; followup steps differ; reply handlers include unsubscribe; go/no-go critical gates block; scorecard aggregation + verdict)
- test_revenue_launch.py: 31 tests (offers correct prices, no_live_charge=True; segment-aware recommends; pipeline 8 stages + add/update/summarize + win rate; outreach v2 Arabic; intake fields; 24h plan 5 phases; invoice halalas correct; payment confirmation blocks premature delivery; proof pack 5 lines + 3 next-step paths)
Scripts (1 new)
- scripts/launch_readiness_check.py: runs 10 gates locally + optional --staging-url; pretty/JSON output; critical gates determine GO/NO-GO/FIX-THEN-GO verdict
Landing pages (2 new, RTL Arabic)
- list-intelligence.html — List Intelligence service detail (499–1,500 SAR)
- growth-os.html — Growth OS Monthly subscription page (2,999 SAR/month)
Docs (1 new + 1 updated)
- REVENUE_TODAY_PLAYBOOK.md (Arabic) — 12-section playbook: offers, segments, messages, demo, pipeline, 24h delivery, Moyasar manual flow, proof pack, daily targets, go/no-go, what-not-to-do, next-step
- DEALIX_100_PERCENT_LAUNCH_PLAN.md — added §40 Launch Ops + §41 Revenue Launch + §42 Service Tower extensions + §43 Scripts
Test results
- 56/56 new tests pass
- Full suite: 824 passed, 2 skipped (missing API keys, unrelated)
- 0 existing tests broken
Safety integration
- All offers: live_send_allowed=False, no_live_charge=True, approval_required=True
- 10-gate go/no-go BLOCKS launch if no_secrets/live_sends_disabled/staging_health fail
- Moyasar: invoice/payment-link manual only; NEVER calls live charge API
- Payment confirmation checklist blocks delivery before invoice paid status
- All outreach messages: drafts only, follow-ups capped at 3, opt-out honored immediately
- 6 verticals mapped to safe service stacks; LinkedIn always Lead Forms (never scraping)
Integration with previous layers
- Launch Ops uses platform_services tool_gateway, intelligence_layer command_feed, security_curator redaction
- Revenue Launch uses targeting_os contractability + service_tower offers + intelligence_layer simulator
- Pipeline tracker integrates with action_ledger for stage transitions
- Proof Pack template references intelligence_layer proof metrics + service_excellence ROI
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
233 lines
8.5 KiB
Python
233 lines
8.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Dealix Launch Readiness — 10-gate Go/No-Go check.
|
|
|
|
Runs locally + against an optional staging URL. Reports which gates pass/fail
|
|
and what the next concrete actions are.
|
|
|
|
Usage:
|
|
python scripts/launch_readiness_check.py
|
|
python scripts/launch_readiness_check.py --staging-url https://staging.example
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import urllib.error
|
|
import urllib.request
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
|
|
SECRET_PATTERNS = (
|
|
r"ghp_[A-Za-z0-9]{20,}",
|
|
r"github_pat_[A-Za-z0-9_]{20,}",
|
|
r"sk-[A-Za-z0-9]{30,}",
|
|
r"sk-ant-[A-Za-z0-9_\-]{20,}",
|
|
r"AKIA[A-Z0-9]{16}",
|
|
r"AIza[A-Za-z0-9_\-]{30,}",
|
|
r"EAA[A-Za-z0-9]{30,}",
|
|
r"-----BEGIN (?:RSA |EC |OPENSSH |)PRIVATE KEY-----",
|
|
)
|
|
|
|
EXCLUDE_DIRS = (".git", ".venv", "node_modules", "__pycache__", ".pytest_cache")
|
|
|
|
|
|
def gate_tests_passed() -> tuple[bool, str]:
|
|
"""Run pytest with --noconftest on the new layer tests as a quick proxy."""
|
|
try:
|
|
result = subprocess.run(
|
|
[sys.executable, "-m", "pytest",
|
|
"tests/unit/test_launch_ops.py",
|
|
"tests/unit/test_revenue_launch.py",
|
|
"tests/unit/test_security_curator.py",
|
|
"--noconftest", "--no-cov", "-q", "-p", "no:cacheprovider"],
|
|
cwd=REPO_ROOT, capture_output=True, text=True, timeout=60,
|
|
)
|
|
ok = result.returncode == 0
|
|
last_line = (result.stdout or "").strip().splitlines()[-1:]
|
|
msg = last_line[0] if last_line else "no output"
|
|
return ok, msg
|
|
except Exception as exc: # noqa: BLE001
|
|
return False, f"pytest error: {exc}"
|
|
|
|
|
|
def gate_routes_check() -> tuple[bool, str]:
|
|
"""Run scripts/print_routes.py — should not raise."""
|
|
try:
|
|
result = subprocess.run(
|
|
[sys.executable, "scripts/print_routes.py"],
|
|
cwd=REPO_ROOT, capture_output=True, text=True, timeout=30,
|
|
)
|
|
if result.returncode == 0:
|
|
n_routes = result.stdout.count("/api/v1")
|
|
return True, f"{n_routes} v1 routes"
|
|
return False, f"exit={result.returncode}"
|
|
except Exception as exc: # noqa: BLE001
|
|
return False, f"err: {exc}"
|
|
|
|
|
|
def gate_no_secrets() -> tuple[bool, str]:
|
|
"""Scan repo for secret patterns. Skips known-safe directories."""
|
|
findings: list[str] = []
|
|
pat = re.compile("|".join(SECRET_PATTERNS))
|
|
for path in REPO_ROOT.rglob("*"):
|
|
if not path.is_file():
|
|
continue
|
|
if any(part in EXCLUDE_DIRS for part in path.parts):
|
|
continue
|
|
# Skip docs that intentionally mention patterns as examples.
|
|
if path.suffix in {".md", ".lock", ".pyc", ".png", ".jpg", ".jpeg",
|
|
".gif", ".woff", ".woff2", ".ttf"}:
|
|
continue
|
|
try:
|
|
text = path.read_text(encoding="utf-8", errors="ignore")
|
|
except Exception: # noqa: BLE001
|
|
continue
|
|
if pat.search(text):
|
|
findings.append(str(path.relative_to(REPO_ROOT)))
|
|
if len(findings) >= 3:
|
|
break
|
|
return (not findings), (
|
|
"clean" if not findings else f"FOUND in: {', '.join(findings)}"
|
|
)
|
|
|
|
|
|
def gate_staging_health(staging_url: str | None) -> tuple[bool, str]:
|
|
"""Hit /health on staging if a URL is provided."""
|
|
if not staging_url:
|
|
return False, "no --staging-url provided"
|
|
url = staging_url.rstrip("/") + "/health"
|
|
try:
|
|
req = urllib.request.Request(url, headers={"User-Agent": "Dealix/Readiness"})
|
|
with urllib.request.urlopen(req, timeout=10) as resp: # nosec
|
|
return resp.status == 200, f"status={resp.status}"
|
|
except Exception as exc: # noqa: BLE001
|
|
return False, f"err: {exc}"
|
|
|
|
|
|
def gate_supabase_staging() -> tuple[bool, str]:
|
|
"""We can only check whether SUPABASE_URL is configured, not connectivity."""
|
|
if os.getenv("SUPABASE_URL") and os.getenv("SUPABASE_SERVICE_ROLE_KEY"):
|
|
return True, "env vars configured"
|
|
return False, "SUPABASE_URL or SERVICE_ROLE_KEY not set in env"
|
|
|
|
|
|
def gate_service_catalog(staging_url: str | None) -> tuple[bool, str]:
|
|
if not staging_url:
|
|
return False, "no --staging-url provided"
|
|
url = staging_url.rstrip("/") + "/api/v1/services/catalog"
|
|
try:
|
|
req = urllib.request.Request(url, headers={"User-Agent": "Dealix/Readiness"})
|
|
with urllib.request.urlopen(req, timeout=10) as resp: # nosec
|
|
data = json.loads(resp.read().decode("utf-8", errors="ignore"))
|
|
total = int(data.get("total", 0))
|
|
return total >= 4, f"{total} services"
|
|
except Exception as exc: # noqa: BLE001
|
|
return False, f"err: {exc}"
|
|
|
|
|
|
def gate_private_beta_page() -> tuple[bool, str]:
|
|
p = REPO_ROOT / "landing" / "private-beta.html"
|
|
if not p.exists():
|
|
return False, "missing"
|
|
text = p.read_text(encoding="utf-8", errors="ignore")
|
|
has_cta = ("احجز" in text) or ("ابدأ" in text) or ("احصل" in text)
|
|
has_pilot = ("Pilot" in text) or ("بايلوت" in text)
|
|
return (has_cta and has_pilot), (
|
|
"ok" if has_cta and has_pilot else "missing CTA or Pilot mention"
|
|
)
|
|
|
|
|
|
def gate_first_20_ready() -> tuple[bool, str]:
|
|
"""Soft check: a tracker doc/sheet may exist as evidence."""
|
|
candidates = [
|
|
REPO_ROOT / "docs" / "FIRST_20_OUTREACH_MESSAGES.md",
|
|
REPO_ROOT / "docs" / "REVENUE_TODAY_PLAYBOOK.md",
|
|
]
|
|
found = [str(c.relative_to(REPO_ROOT)) for c in candidates if c.exists()]
|
|
return bool(found), ", ".join(found) or "no first-20 doc/sheet found"
|
|
|
|
|
|
def gate_live_sends_disabled() -> tuple[bool, str]:
|
|
"""Verify env flags for live sends are NOT set to true."""
|
|
flags = [
|
|
"WHATSAPP_ALLOW_LIVE_SEND",
|
|
"GMAIL_ALLOW_LIVE_SEND",
|
|
"CALENDAR_ALLOW_LIVE_INSERT",
|
|
"MOYASAR_ALLOW_LIVE_CHARGE",
|
|
"GBP_ALLOW_LIVE_REPLY",
|
|
]
|
|
enabled = [f for f in flags if os.getenv(f, "false").lower() == "true"]
|
|
return (not enabled), (
|
|
"all disabled" if not enabled else f"ENABLED: {', '.join(enabled)}"
|
|
)
|
|
|
|
|
|
def gate_payment_manual_ready() -> tuple[bool, str]:
|
|
"""Soft check: payment-manual flow module is present + accessible."""
|
|
p = REPO_ROOT / "auto_client_acquisition" / "revenue_launch" / "payment_manual_flow.py"
|
|
return p.exists(), ("module present" if p.exists() else "missing")
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--staging-url", default=None,
|
|
help="Optional staging URL for live checks")
|
|
parser.add_argument("--json", action="store_true",
|
|
help="Emit JSON instead of pretty output")
|
|
args = parser.parse_args()
|
|
|
|
print("Dealix Launch Readiness — 10 Gates")
|
|
print("─" * 60)
|
|
|
|
gates = [
|
|
("tests_passed", gate_tests_passed()),
|
|
("routes_check", gate_routes_check()),
|
|
("no_secrets", gate_no_secrets()),
|
|
("staging_health", gate_staging_health(args.staging_url)),
|
|
("supabase_staging", gate_supabase_staging()),
|
|
("service_catalog", gate_service_catalog(args.staging_url)),
|
|
("private_beta_page", gate_private_beta_page()),
|
|
("first_20_ready", gate_first_20_ready()),
|
|
("live_sends_disabled", gate_live_sends_disabled()),
|
|
("payment_manual_ready", gate_payment_manual_ready()),
|
|
]
|
|
|
|
passed = sum(1 for _, (ok, _) in gates if ok)
|
|
total = len(gates)
|
|
pct = round(100 * passed / total, 1)
|
|
|
|
if args.json:
|
|
out = {
|
|
"passed": passed, "total": total, "pct": pct,
|
|
"gates": [{"id": gid, "passed": ok, "info": info}
|
|
for gid, (ok, info) in gates],
|
|
}
|
|
print(json.dumps(out, ensure_ascii=False, indent=2))
|
|
else:
|
|
for gid, (ok, info) in gates:
|
|
mark = "✅" if ok else "🔴"
|
|
print(f"{mark} {gid:<24} {info}")
|
|
print("─" * 60)
|
|
critical = ("no_secrets", "live_sends_disabled", "staging_health")
|
|
critical_failed = [gid for gid, (ok, _) in gates
|
|
if gid in critical and not ok]
|
|
if critical_failed:
|
|
verdict = f"🔴 NO-GO — critical gates failed: {', '.join(critical_failed)}"
|
|
elif pct >= 70:
|
|
verdict = f"✅ GO (Private Beta) — {passed}/{total} = {pct}%"
|
|
else:
|
|
verdict = f"🟡 FIX-THEN-GO — only {passed}/{total} = {pct}%"
|
|
print(verdict)
|
|
|
|
return 0 if passed == total else (1 if passed < 7 else 0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|