{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Final Project*\n",
    "\n",
    "# Enterprise Prospecting & Outreach Orchestrator\n",
    "\n",
    "The capstone integrates the Claude API, the Claude Agent SDK, and Claude Code into a single production system. You will build a coordinator agent that decomposes prospecting requests into parallel research subagents spawned with the Task tool, connects to CRM data through MCP, ships outreach at scale through Message Batches, and stays reliable under long-running sessions with deterministic hooks, case facts, and an independent CI review pipeline.\n",
    "\n",
    "## Project Overview\n",
    "\n",
    "You are building the **Enterprise Prospecting & Outreach Orchestrator**: a system that identifies high-value prospects, performs deep research using a multi-agent hub-and-spoke model, generates high-volume outreach campaigns, and maintains strict reliability through deterministic hooks, re-injected case facts, and crash-recoverable state. Every concept from Modules 1 through 11 lands in this single project.\n",
    "\n",
    "## Phase 1: Coordinator & Agent Definitions\n",
    "\n",
    "Define the coordinator with the Claude Agent SDK so the runtime owns history, tool dispatch, and termination. The coordinator delegates through the `Task` tool; each research specialist is an `AgentDefinition` with its own description, prompt, tool allowlist, and model.\n",
    "\n",
    "- **Coordinator Tools:** the coordinator's `allowed_tools` must include `\"Task\"` — without it, the coordinator cannot spawn subagents at all.\n",
    "- **AgentDefinition Fields:** each subagent declares a `description` (when the coordinator should pick it), a `prompt` (its system prompt), `tools` (its allowlist), and a `model` — use lighter models for narrow research tasks.\n",
    "- **Research Capability:** give research subagents a client-side `web_lookup` custom tool — your application executes the search and returns results to the agent — or a tool exposed by an MCP server. The application, not the model provider, owns search execution and rate limits.\n",
    "- **Interview Pattern:** before planning, the coordinator must ask 2-3 clarifying questions about outreach goals, such as tone constraints, competitor sensitivity, required technical depth, and any claims that require human approval."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "from claude_agent_sdk import AgentDefinition, ClaudeAgentOptions\n",
    "\n",
    "options = ClaudeAgentOptions(\n",
    "    system_prompt=(\n",
    "        \"You are the lead orchestrator. Use the Task tool to delegate to \"\n",
    "        \"specialist subagents. Always maintain a 'case facts' block with \"\n",
    "        \"non-negotiable transactional data. Before generating the research \"\n",
    "        \"plan, ask 2-3 concise clarifying questions about outreach goals, \"\n",
    "        \"including tone, competitor sensitivity, and technical depth.\"\n",
    "    ),\n",
    "    # The coordinator MUST have \"Task\" to spawn subagents.\n",
    "    allowed_tools=[\"Task\", \"Read\", \"Grep\", \"mcp__crm__crm_query\"],\n",
    "    agents={\n",
    "        \"web-research\": AgentDefinition(\n",
    "            description=(\n",
    "                \"Researches a prospect's public footprint. Use for market \"\n",
    "                \"signals, news, and technology-adoption research.\"\n",
    "            ),\n",
    "            prompt=(\n",
    "                \"Research the assigned prospect with the web_lookup tool. \"\n",
    "                \"Return findings with sources; never speculate.\"\n",
    "            ),\n",
    "            tools=[\"mcp__research__web_lookup\"],\n",
    "            model=\"sonnet\",\n",
    "        ),\n",
    "        \"financial-analysis\": AgentDefinition(\n",
    "            description=\"Analyzes prospect financials from CRM data.\",\n",
    "            prompt=(\n",
    "                \"Analyze revenue, budget, and spend signals from CRM records. \"\n",
    "                \"Return a structured summary with nullable fields for unknowns.\"\n",
    "            ),\n",
    "            tools=[\"mcp__crm__crm_query\"],\n",
    "            model=\"sonnet\",\n",
    "        ),\n",
    "        \"competitor-tracking\": AgentDefinition(\n",
    "            description=\"Tracks competitor positioning in a prospect's market.\",\n",
    "            prompt=(\n",
    "                \"Identify competitors and positioning gaps for the assigned \"\n",
    "                \"market segment. Return findings with sources.\"\n",
    "            ),\n",
    "            tools=[\"mcp__research__web_lookup\"],\n",
    "            model=\"haiku\",\n",
    "        ),\n",
    "    },\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Phase 2: Knowledge Mapping with MCP & Resources\n",
    "\n",
    "Connect the CRM to the agent through an MCP server declared in the project's `.mcp.json`. Expose Resources as a navigable map of the data so Claude can plan its tool sequence before executing.\n",
    "\n",
    "- **Project Config:** `.mcp.json` with environment variable substitution (`${CRM_TOKEN}`) keeps secrets out of version control.\n",
    "- **CRM Resource Hierarchy:** expose `crm://docs`, `crm://schemas`, and `crm://workflows` resources so Claude can inspect the CRM's documentation hierarchy before calling query or write tools.\n",
    "- **Selection Reliability:** tool descriptions must explain why CRM MCP tools are preferred over built-in text tools such as `Grep` for structured data lookups.\n",
    "- **Discovery Tax Reduction:** Resources act as catalogs. The agent should inspect `crm://schemas` and `crm://workflows` before exploratory CRM queries instead of discovering tables through repeated tool calls.\n",
    "\n",
    "*.mcp.json*\n",
    "```json\n",
    "{\n",
    "  \"mcpServers\": {\n",
    "    \"crm\": {\n",
    "      \"command\": \"node\",\n",
    "      \"args\": [\"./mcp-servers/crm-server.js\"],\n",
    "      \"env\": {\n",
    "        \"CRM_TOKEN\": \"${CRM_TOKEN}\",\n",
    "        \"CRM_BASE_URL\": \"${CRM_BASE_URL}\"\n",
    "      }\n",
    "    }\n",
    "  }\n",
    "}\n",
    "```\n",
    "\n",
    "*CRM resources/list response*\n",
    "```json\n",
    "{\n",
    "  \"resources\": [\n",
    "    {\n",
    "      \"uri\": \"crm://docs/prospecting\",\n",
    "      \"name\": \"CRM prospecting documentation\",\n",
    "      \"description\": \"Use before lead searches. Explains lead stages, required fields, and routing rules.\",\n",
    "      \"mimeType\": \"text/markdown\"\n",
    "    },\n",
    "    {\n",
    "      \"uri\": \"crm://schemas/leads\",\n",
    "      \"name\": \"Leads schema\",\n",
    "      \"description\": \"Use before CRM lead lookups. Lists column names, allowed enum values, and join keys so the agent chooses crm_query_leads instead of built-in Grep for structured account data.\",\n",
    "      \"mimeType\": \"application/json\"\n",
    "    },\n",
    "    {\n",
    "      \"uri\": \"crm://workflows/outreach-approval\",\n",
    "      \"name\": \"Outreach approval workflow\",\n",
    "      \"description\": \"Approval gates required before process_outreach can run.\",\n",
    "      \"mimeType\": \"text/markdown\"\n",
    "    }\n",
    "  ]\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Phase 3: Multi-Agent Research Workflow\n",
    "\n",
    "Hub-and-Spoke architecture: the coordinator decomposes a prospecting request and spawns specialist subagents in parallel inside a single turn.\n",
    "\n",
    "- **Parallel Execution:** the coordinator emits multiple `Task` tool calls in one response — one per subagent — so Web Research, Financial Analysis, and Competitor Tracking run concurrently.\n",
    "- **Isolated Context:** subagents do not inherit the coordinator's history. Everything a subagent needs must be passed explicitly in its Task prompt, and findings return to the coordinator only through the Task result.\n",
    "- **Structured Errors:** subagents return `errorCategory` and `isRetryable` so the coordinator can recover intelligently rather than failing the workflow."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "import anyio\n",
    "from claude_agent_sdk import ClaudeSDKClient\n",
    "\n",
    "async def run_research():\n",
    "    # `options` is the ClaudeAgentOptions from Phase 1 (Task + AgentDefinitions)\n",
    "    async with ClaudeSDKClient(options=options) as coordinator:\n",
    "        await coordinator.query(\n",
    "            \"Research AI adoption in Fintech and Healthcare simultaneously. \"\n",
    "            \"Delegate to the web-research, financial-analysis, and \"\n",
    "            \"competitor-tracking subagents — issue one Task call per subagent, \"\n",
    "            \"all in the same response, so they run in parallel. Each subagent \"\n",
    "            \"starts with an empty context: include the prospect summary and \"\n",
    "            \"the specific question in each Task prompt.\"\n",
    "        )\n",
    "        async for message in coordinator.receive_response():\n",
    "            print(message)\n",
    "\n",
    "anyio.run(run_research)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> **Exam tip:** Task subagents run in isolated context windows. Nothing is shared implicitly — the coordinator forwards inputs in the Task prompt, and the subagent's findings come back as the Task tool result, which the coordinator then synthesizes.\n",
    "\n",
    "*subagent error handler*"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "def subagent_tool_handler(tool_input):\n",
    "    try:\n",
    "        return {\"data\": run_research(tool_input)}\n",
    "    except TimeoutError:\n",
    "        return {\n",
    "            \"errorCategory\": \"TRANSIENT\",\n",
    "            \"failure_type\": \"timeout\",\n",
    "            \"isRetryable\": True,\n",
    "            \"message\": \"Research API timed out. Retry with a narrower query.\"\n",
    "        }\n",
    "    except PermissionError:\n",
    "        return {\n",
    "            \"errorCategory\": \"PERMISSION\",\n",
    "            \"isRetryable\": False,\n",
    "            \"message\": \"Token lacks access. Coordinator should pivot, not retry.\"\n",
    "        }"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Phase 4: Synthesis & Scaled Outreach\n",
    "\n",
    "Synthesize research findings, then ship outreach at volume.\n",
    "\n",
    "- **Message Batches:** 50% discount on bulk outreach. Each request needs a unique `custom_id`; results return out of order.\n",
    "- **Single-Shot Only:** batch requests do **not** support multi-turn tool calling — there is nobody to execute a tool and send the result back mid-batch. Do all research first with the interactive agent, then submit plain, self-contained generation requests.\n",
    "- **Structured Extraction:** JSON-schema outputs with nullable fields for `annual_revenue`, `budget_range`, etc., to prevent hallucination when data is missing.\n",
    "- **Position-Aware Input Ordering:** the synthesis prompt must put `key_findings` and decision-critical summaries first, then detailed evidence under explicit prospect and source-type section headers.\n",
    "- **Extensible Categories:** prospect schemas must use an enum with `other` plus a required detail field for extensible fields such as `industry` or `reason_for_outreach`."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "aggregated_research = {\n",
    "    \"key_findings\": [\n",
    "        \"Fintech CTOs show high interest in agentic workflow governance.\",\n",
    "        \"Healthcare prospects require stricter compliance language and human review.\"\n",
    "    ],\n",
    "    \"sections\": {\n",
    "        \"prospect_startup_segment\": startup_evidence,\n",
    "        \"prospect_fortune_500_segment\": enterprise_evidence,\n",
    "        \"source_crm_notes\": crm_notes,\n",
    "        \"source_web_research\": cited_web_findings,\n",
    "    },\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Plain single-shot generation requests: research is already done, so each\n",
    "# request carries everything the model needs. No tools inside the batch.\n",
    "message_batch = client.messages.batches.create(\n",
    "    requests=[\n",
    "        {\n",
    "            \"custom_id\": f\"outreach-{prospect['crm_id']}\",   # unique per prospect\n",
    "            \"params\": {\n",
    "                \"model\": \"claude-sonnet-4-6\",\n",
    "                \"max_tokens\": 2048,\n",
    "                \"messages\": [{\n",
    "                    \"role\": \"user\",\n",
    "                    \"content\": (\n",
    "                        \"Draft a personalized outreach email.\\n\"\n",
    "                        f\"Prospect brief: {prospect['brief']}\\n\"\n",
    "                        f\"Key findings: {prospect['key_findings']}\\n\"\n",
    "                        \"Follow the standard outreach template rules.\"\n",
    "                    ),\n",
    "                }],\n",
    "            },\n",
    "        }\n",
    "        for prospect in qualified_prospects\n",
    "    ]\n",
    ")\n",
    "\n",
    "# Later: correlate by custom_id — results arrive in any order.\n",
    "results = {}\n",
    "for result in client.messages.batches.results(message_batch.id):\n",
    "    results[result.custom_id] = result.result\n",
    "\n",
    "# Resubmit ONLY the failed/expired requests, never the whole batch.\n",
    "retry_ids = [cid for cid, r in results.items() if r.type in (\"errored\", \"expired\")]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> **Exam tip:** Four batch rules the exam loves: (1) no multi-turn tool calling inside a batch — every request must be a self-contained single shot; (2) batches have a **24-hour** processing window, and requests still unfinished at the deadline expire; (3) results are **unordered** — always correlate by `custom_id`, never by position; (4) on partial failure, resubmit only the failed or expired `custom_id`s.\n",
    "\n",
    "For CRM-ready extraction, structured outputs work inside batch requests too — the schema below uses nullable fields and `enum` + `other` + detail-field pairs:\n",
    "\n",
    "*structured extraction schema*"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "output_config = {\n",
    "    \"format\": {\n",
    "        \"type\": \"json_schema\",\n",
    "        \"schema\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\n",
    "                \"company_name\":   {\"type\": \"string\"},\n",
    "                \"contact_email\":  {\"type\": \"string\"},\n",
    "                \"annual_revenue\": {\"type\": [\"integer\", \"null\"]},\n",
    "                \"budget_range\":   {\"type\": [\"string\", \"null\"]},\n",
    "                \"industry\": {\n",
    "                    \"type\": \"string\",\n",
    "                    \"enum\": [\"fintech\", \"healthcare\", \"manufacturing\", \"retail\", \"other\"]\n",
    "                },\n",
    "                \"industry_detail\": {\"type\": [\"string\", \"null\"]},\n",
    "                \"reason_for_outreach\": {\n",
    "                    \"type\": \"string\",\n",
    "                    \"enum\": [\"ai_governance\", \"workflow_automation\", \"security_review\", \"other\"]\n",
    "                },\n",
    "                \"reason_detail\": {\"type\": [\"string\", \"null\"]}\n",
    "            },\n",
    "            \"required\": [\n",
    "                \"company_name\", \"contact_email\", \"annual_revenue\", \"budget_range\",\n",
    "                \"industry\", \"industry_detail\", \"reason_for_outreach\", \"reason_detail\"\n",
    "            ],\n",
    "            \"additionalProperties\": False\n",
    "        }\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Phase 5: Production Reliability & Context Hygiene\n",
    "\n",
    "Long-running research sessions need active context management and deterministic safety rails.\n",
    "\n",
    "- **Deterministic Hooks:** a `PreToolUse` hook blocks any `process_outreach` call if the prospect's `risk_score` has not been verified. A hook can only **deny with a reason** — it cannot redirect the call to a different tool. The denial reason is returned to the model, and the model then calls `escalate_to_human` itself.\n",
    "- **Case Facts Pattern:** non-negotiable transactional data (*\"Max Budget: $50k\"*) lives in a case-facts block that the orchestration code **re-injects verbatim on every turn**, so drift and summarization can never paraphrase it away.\n",
    "- **State Manifest:** each agent exports a scratchpad/state manifest (completed subtasks, pending prospects, verified facts) to disk after every phase, so a crashed run can be resumed without re-doing finished research.\n",
    "- **Fresh Session with Summary:** when a long session's context degrades — repetition, forgotten constraints, contradictions — stop patching it. Start a fresh session seeded with the case-facts block plus a concise summary of verified findings from the state manifest.\n",
    "- **Human-in-the-Loop:** if a prospect requests \"no AI contact\" or a policy gap is detected, the agent must call `escalate_to_human` immediately.\n",
    "\n",
    "*case facts re-injection*"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "CASE_FACTS = (\n",
    "    \"<case_facts>\\n\"\n",
    "    \"Max Budget: $50,000\\n\"\n",
    "    \"Contract term: 12 months\\n\"\n",
    "    \"Prospect opt-outs: no phone contact\\n\"\n",
    "    \"</case_facts>\"\n",
    ")\n",
    "\n",
    "def build_turn(user_text: str) -> dict:\n",
    "    # Re-inject case facts verbatim on EVERY turn so they can never\n",
    "    # be paraphrased away as the conversation grows.\n",
    "    return {\n",
    "        \"role\": \"user\",\n",
    "        \"content\": f\"{CASE_FACTS}\\n\\n{user_text}\",\n",
    "    }\n",
    "\n",
    "def export_state_manifest(path: str, state: dict) -> None:\n",
    "    \"\"\"Each agent writes its progress after every phase for crash recovery.\"\"\"\n",
    "    import json\n",
    "    with open(path, \"w\") as f:\n",
    "        json.dump({\n",
    "            \"completed_subtasks\": state[\"done\"],\n",
    "            \"pending_prospects\": state[\"pending\"],\n",
    "            \"verified_facts\": state[\"facts\"],\n",
    "        }, f, indent=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*PreToolUse hook*\n",
    "```json\n",
    "{\n",
    "  \"hooks\": {\n",
    "    \"PreToolUse\": [\n",
    "      {\n",
    "        \"matcher\": {\"tool_name\": \"process_outreach\"},\n",
    "        \"hooks\": [\n",
    "          {\n",
    "            \"type\": \"command\",\n",
    "            \"command\": \"python verify_risk_score.py\"\n",
    "          }\n",
    "        ]\n",
    "      }\n",
    "    ]\n",
    "  }\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Phase 6: Claude Code & Skills\n",
    "\n",
    "Manage the project's development standards through Claude Code.\n",
    "\n",
    "- **Path-Scoped Rules:** a rule file in `.claude/rules/` with a `paths` frontmatter key enforces the standard outreach template only when Claude is editing files under `outreach/`.\n",
    "- **Agent Skills:** a `generate-audit` skill with `context: fork` performs deep compliance checks on generated emails without polluting the main development context.\n",
    "- **Three-Level Progressive Disclosure:** the skill must keep trigger logic in frontmatter, actionable workflow steps in `SKILL.md`, and detailed compliance material in `references/` so the agent only loads extra context when needed.\n",
    "- **CI Review Output:** use `claude -p` with `--output-format json` and `--json-schema` so automated review findings can be posted as inline PR comments.\n",
    "- **CI Dedupe:** when re-running the review after new commits land on the same PR, include the prior findings in the prompt and instruct Claude to report **only new or unaddressed issues** — otherwise every push re-posts the same comments.\n",
    "\n",
    "*.claude/rules/outreach-template.md*\n",
    "```markdown\n",
    "---\n",
    "paths: [\"outreach/**/*.md\", \"outreach/**/*.html\"]\n",
    "---\n",
    "# Standard Outreach Template Rules\n",
    "- Open with the prospect's confirmed pain point from CRM, never a generic hook.\n",
    "- Include one quantified case study from the same industry vertical.\n",
    "- Close with a single, specific call to action (no menu of options).\n",
    "- Never reference compliance regimes the prospect has not opted into.\n",
    "```\n",
    "\n",
    "*.claude/skills/generate-audit/SKILL.md*\n",
    "```yaml\n",
    "---\n",
    "name: generate-audit\n",
    "description: Audit generated outreach emails when asked to review campaign copy, regulated claims, unsupported personalization, or compliance-sensitive prospect messaging.\n",
    "context: fork\n",
    "allowed-tools: [Read, Grep, Glob]\n",
    "---\n",
    "# Audit Skill\n",
    "1. Read the email under review.\n",
    "2. Cross-check claims against the prospect's stated regulated regimes.\n",
    "3. Open `references/compliance-claims.md` only when the email mentions HIPAA, SOC 2, ISO 27001, privacy, safety, or procurement claims.\n",
    "4. Flag unsupported claims, weak personalization, policy conflicts, and missing escalation triggers.\n",
    "5. Return a structured JSON report with findings, severity, evidence, and recommended next action.\n",
    "```\n",
    "\n",
    "```bash\n",
    "# First run on a PR\n",
    "claude -p \"Review this capstone PR and return inline findings.\" \\\n",
    "  --output-format json \\\n",
    "  --json-schema .claude/schemas/pr-review-findings.schema.json\n",
    "\n",
    "# Re-run after new commits: pass prior findings and dedupe\n",
    "claude -p \"Review this PR again. Prior findings: $(cat prior-findings.json). \\\n",
    "Report ONLY issues that are new or still unaddressed — do not repeat \\\n",
    "findings that were already posted or have been fixed.\" \\\n",
    "  --output-format json \\\n",
    "  --json-schema .claude/schemas/pr-review-findings.schema.json\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "from collections import defaultdict\n",
    "\n",
    "# Phase 7 support: stratified random sampling of high-confidence extractions\n",
    "# across prospect segments AND source types, so aggregate accuracy cannot\n",
    "# hide a weak segment.\n",
    "records = [\n",
    "    {\"id\": \"startup-1\",   \"segment\": \"startup\",     \"source\": \"web_research\", \"confidence\": 0.96},\n",
    "    {\"id\": \"startup-2\",   \"segment\": \"startup\",     \"source\": \"crm_note\",     \"confidence\": 0.95},\n",
    "    {\"id\": \"midmarket-1\", \"segment\": \"mid_market\",  \"source\": \"web_research\", \"confidence\": 0.94},\n",
    "    {\"id\": \"midmarket-2\", \"segment\": \"mid_market\",  \"source\": \"crm_note\",     \"confidence\": 0.93},\n",
    "    {\"id\": \"f500-1\",      \"segment\": \"fortune_500\", \"source\": \"web_research\", \"confidence\": 0.97},\n",
    "    {\"id\": \"f500-2\",      \"segment\": \"fortune_500\", \"source\": \"crm_note\",     \"confidence\": 0.92},\n",
    "]\n",
    "\n",
    "def stratified_high_confidence_sample(items, total):\n",
    "    by_segment = defaultdict(list)\n",
    "    for item in sorted(items, key=lambda row: row[\"confidence\"], reverse=True):\n",
    "        by_segment[item[\"segment\"]].append(item)\n",
    "    sample = []\n",
    "    while len(sample) < total and any(by_segment.values()):\n",
    "        for segment in sorted(by_segment):\n",
    "            if by_segment[segment] and len(sample) < total:\n",
    "                sample.append(by_segment[segment].pop(0))\n",
    "    return sample\n",
    "\n",
    "sample = stratified_high_confidence_sample(records, total=len(records))\n",
    "segments = {item[\"segment\"] for item in sample}\n",
    "sources = {item[\"source\"] for item in sample}\n",
    "print([item[\"id\"] for item in sample])\n",
    "assert segments == {\"startup\", \"mid_market\", \"fortune_500\"}\n",
    "assert sources == {\"web_research\", \"crm_note\"}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Phase 7: Evaluation & Success Metrics\n",
    "\n",
    "Evaluate the orchestrator as a product, not only as a demo.\n",
    "\n",
    "- **Quantitative:** skill trigger reliability reaches at least 90% of relevant queries, the workflow uses fewer tool calls than a baseline, the coordinator salvages `partial_results` rather than re-running failed searches, and 10 high-confidence extractions are manually sampled across prospect segments and source types to catch segment-specific failures.\n",
    "- **Qualitative:** a new user can complete the task on the first try with minimal guidance, escalation summaries are actionable, PR review findings are specific enough to become inline comments, and a completely independent Claude instance in a fresh session catches unsupported claims or weak outreach that the generator missed.\n",
    "- **Independent Review:** start a fresh Claude session with no prior reasoning context. Provide only the final outreach, source evidence, and review rubric. The reviewer must catch unsupported claims, policy issues, weak personalization, and missing escalations that the generator may have rationalized.\n",
    "- **Stratified Random Sampling:** sample extractions across prospect segments and source types rather than relying on aggregate accuracy. Include at least one startup, one mid-market account, one Fortune 500 account, one CRM-sourced record, and one web-researched record.\n",
    "\n",
    "## Final Audit Checklist\n",
    "\n",
    "- **Tool descriptions:** every tool description states its inputs *and* its boundaries — what the tool does, what it must never be used for, and when to prefer it over neighboring tools.\n",
    "- **Subagent failures:** every subagent failure returns structured context (`errorCategory`, `isRetryable`, a human-readable message) *with partial results*, so the coordinator can salvage completed work instead of re-running the whole task.\n",
    "- **Batch discipline:** batch jobs are single-shot generation requests with unique `custom_id`s — no multi-turn tool calling, results correlated by `custom_id`, only failures resubmitted.\n",
    "- **Escalation triggers:** escalation covers all three cases — an explicit human request, a detected policy gap, and no measurable progress after repeated attempts.\n",
    "- **Handoff payload:** every `escalate_to_human` handoff stands alone — a human can act on the summary, case facts, and pending decision without reading the transcript.\n",
    "- **Review pipeline:** the review runs per-file passes plus a cross-file integration pass, executed by an independent Claude instance in a fresh session.\n",
    "- **Independent review:** final outreach must be audited by a fresh Claude session, not the generation session, so the reviewer is isolated from the generator's assumptions.\n",
    "- **Subagents:** isolated context by default. The coordinator must explicitly forward only the data each subagent needs."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "independent_review_command = (\n",
    "    'claude -p \"Review this generated outreach in a fresh session for '\n",
    "    'unsupported claims, policy issues, and weak personalization.\" '\n",
    "    \"--output-format json \"\n",
    "    \"--json-schema .claude/schemas/outreach-review.schema.json\"\n",
    ")\n",
    "\n",
    "assert \"--output-format json\" in independent_review_command\n",
    "assert \"--json-schema\" in independent_review_command\n",
    "assert \"--resume\" not in independent_review_command  # must be a fresh session\n",
    "print(independent_review_command)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}