{
 "nbformat": 4,
 "nbformat_minor": 5,
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3"
  },
  "srai": {
   "book": 7,
   "canonical_version": "1.0",
   "unit": "V7_0_N01",
   "track": "0",
   "status": "publication-candidate",
   "data": "synthetic-only",
   "production_unit_version": "0.2"
  }
 },
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# V7_0_N01 — Start With the Decision\n",
    "\n",
    "**SRAI Book 7 — Applied AI Studio: National Sector Intelligence**  \n",
    "Publication candidate v0.2. This notebook supports learning and review; it does not authorize an operational decision."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Learning outcomes\n",
    "By the end, you can: (1) define a public decision before choosing a model; (2) name the accountable owner and lawful intervention; (3) state the decision horizon and error trade-offs; (4) define abstention and escalation; and (5) produce a machine-readable decision contract."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Why the decision comes first\n",
    "A forecast is useful only when a named institution can act within the forecast horizon. We therefore separate: **signal**, **analytical interpretation**, **recommendation**, **authorization**, and **action**. The model owns none of the last three."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "DecisionContract ready\n"
     ]
    }
   ],
   "source": [
    "from dataclasses import dataclass, asdict\n",
    "from typing import Optional\n",
    "import json\n",
    "\n",
    "@dataclass\n",
    "class DecisionContract:\n",
    "    decision_id: str\n",
    "    public_problem: str\n",
    "    accountable_owner: str\n",
    "    decision: str\n",
    "    horizon_days: int\n",
    "    intervention: str\n",
    "    false_positive_cost: str\n",
    "    false_negative_cost: str\n",
    "    abstain_when: str\n",
    "    escalation_route: str\n",
    "    review_cycle_days: int\n",
    "\n",
    "print('DecisionContract ready')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. A worked agricultural contract\n",
    "The analytical system may identify areas requiring field verification. It does not declare a food-security emergency or allocate resources automatically."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "  \"decision_id\": \"AG-A05\",\n",
      "  \"public_problem\": \"An agricultural frame may omit or misclassify active holdings\",\n",
      "  \"accountable_owner\": \"National Statistical Office / Ministry of Agriculture\",\n",
      "  \"decision\": \"Which areas should receive frame verification before sample selection?\",\n",
      "  \"horizon_days\": 30,\n",
      "  \"intervention\": \"Targeted listing and frame reconciliation\",\n",
      "  \"false_positive_cost\": \"Unnecessary field visit and cost\",\n",
      "  \"false_negative_cost\": \"Biased frame and undercoverage\",\n",
      "  \"abstain_when\": \"coverage diagnostics are stale or geographic identifiers fail validation\",\n",
      "  \"escalation_route\": \"Survey director \\u2192 methodology committee\",\n",
      "  \"review_cycle_days\": 14\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "agri = DecisionContract(\n",
    " 'AG-A05','An agricultural frame may omit or misclassify active holdings',\n",
    " 'National Statistical Office / Ministry of Agriculture',\n",
    " 'Which areas should receive frame verification before sample selection?',30,\n",
    " 'Targeted listing and frame reconciliation',\n",
    " 'Unnecessary field visit and cost','Biased frame and undercoverage',\n",
    " 'coverage diagnostics are stale or geographic identifiers fail validation',\n",
    " 'Survey director → methodology committee',14)\n",
    "print(json.dumps(asdict(agri),indent=2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Loss is not the same as model error\n",
    "A false negative can be more consequential than a false positive, or vice versa. The relative cost is a policy judgement to document—not a number for the model to invent."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "conservative 33\n",
      "balanced 45\n",
      "restrictive 74\n"
     ]
    }
   ],
   "source": [
    "def expected_policy_loss(fp, fn, cost_fp=1, cost_fn=5):\n",
    "    return fp*cost_fp + fn*cost_fn\n",
    "scenarios=[('conservative',18,3),('balanced',10,7),('restrictive',4,14)]\n",
    "for name,fp,fn in scenarios:\n",
    "    print(name, expected_policy_loss(fp,fn))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Actionability gate\n",
    "A high accuracy score is insufficient. A candidate passes only if the owner, intervention, horizon, authority, and operational capacity are all present."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'named_owner': True, 'defined_intervention': True, 'positive_horizon': True, 'lawful_authority': True, 'operational_capacity': True, 'current_data': True} PASS= True\n"
     ]
    }
   ],
   "source": [
    "def actionability_gate(contract, lawful=True, capacity=True, data_current=True):\n",
    "    checks={\n",
    "      'named_owner': bool(contract.accountable_owner.strip()),\n",
    "      'defined_intervention': bool(contract.intervention.strip()),\n",
    "      'positive_horizon': contract.horizon_days>0,\n",
    "      'lawful_authority': lawful, 'operational_capacity':capacity,\n",
    "      'current_data':data_current}\n",
    "    return checks, all(checks.values())\n",
    "checks,passed=actionability_gate(agri)\n",
    "print(checks,'PASS=',passed)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Abstention is a valid output\n",
    "When required evidence is missing, the safest analytical output may be: *insufficient evidence—refer for review*. This protects decision-makers from false precision."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.82 0.95 ESCALATE FOR HUMAN REVIEW\n",
      "0.52 0.96 ABSTAIN — UNCERTAIN BAND\n",
      "0.2 0.93 ROUTINE MONITORING\n",
      "0.9 0.62 ABSTAIN — DATA QUALITY REVIEW\n"
     ]
    }
   ],
   "source": [
    "def disposition(probability, data_quality, low=.35, high=.70):\n",
    "    if data_quality < .80: return 'ABSTAIN — DATA QUALITY REVIEW'\n",
    "    if probability >= high: return 'ESCALATE FOR HUMAN REVIEW'\n",
    "    if probability <= low: return 'ROUTINE MONITORING'\n",
    "    return 'ABSTAIN — UNCERTAIN BAND'\n",
    "for p,q in [(.82,.95),(.52,.96),(.20,.93),(.90,.62)]: print(p,q,disposition(p,q))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. Cross-sector exercise\n",
    "Complete the contract below for an attendance/dropout early-warning service. Do not write 'the AI system' as the accountable owner."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "  \"decision_id\": \"ED-E01\",\n",
      "  \"public_problem\": \"Persistent absence may precede dropout\",\n",
      "  \"accountable_owner\": \"District education authority and school safeguarding lead\",\n",
      "  \"decision\": \"Which schools or learners require proportionate review and support?\",\n",
      "  \"horizon_days\": 14,\n",
      "  \"intervention\": \"Human review followed by appropriate learner support\",\n",
      "  \"false_positive_cost\": \"Unnecessary review or stigmatization\",\n",
      "  \"false_negative_cost\": \"Missed opportunity to prevent disengagement\",\n",
      "  \"abstain_when\": \"attendance records are incomplete, delayed, or identity linkage is uncertain\",\n",
      "  \"escalation_route\": \"School lead \\u2192 district safeguarding/education authority\",\n",
      "  \"review_cycle_days\": 7\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "education = DecisionContract(\n",
    " 'ED-E01','Persistent absence may precede dropout',\n",
    " 'District education authority and school safeguarding lead',\n",
    " 'Which schools or learners require proportionate review and support?',14,\n",
    " 'Human review followed by appropriate learner support',\n",
    " 'Unnecessary review or stigmatization','Missed opportunity to prevent disengagement',\n",
    " 'attendance records are incomplete, delayed, or identity linkage is uncertain',\n",
    " 'School lead → district safeguarding/education authority',7)\n",
    "checks,passed=actionability_gate(education)\n",
    "assert passed\n",
    "print(json.dumps(asdict(education),indent=2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Knowledge check\n",
    "1. Why must the decision horizon be defined before model selection?  \n",
    "2. Who owns the final decision?  \n",
    "3. Give two reasons to abstain.  \n",
    "4. Why is accuracy alone insufficient?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exact solutions\n",
    "1. It determines whether data latency and forecast lead time permit intervention.  \n",
    "2. The named lawful institutional authority, never the model.  \n",
    "3. Examples: inadequate data quality; uncertain probability band; failed linkage; distribution shift.  \n",
    "4. Accuracy does not encode consequences, equity, calibration, actionability, authority, or operational capacity."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "V7_0_N01_COMPLETE_EXECUTION_PASS 1377\n"
     ]
    }
   ],
   "source": [
    "payload={'schema_version':'1.0','contracts':[asdict(agri),asdict(education)]}\n",
    "text=json.dumps(payload,sort_keys=True)\n",
    "assert 'AI system' not in agri.accountable_owner\n",
    "assert all(c['horizon_days']>0 for c in payload['contracts'])\n",
    "print('V7_0_N01_COMPLETE_EXECUTION_PASS',len(text))"
   ]
  }
 ]
}
