{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Lab 10 Self-Driven Lab*\n",
    "\n",
    "# Developer Productivity & Operational Enablement\n",
    "\n",
    "**Objective:** onboard a twelve-engineer fintech team onto Claude Code in a regulated codebase, producing the configuration a new hire inherits on their first clone.\n",
    "\n",
    "## Challenge Outline\n",
    "\n",
    "Build a complete notebook that demonstrates the following outcomes:\n",
    "\n",
    "- **Write the project CLAUDE.md outline:** list the sections of the project CLAUDE.md with one line each, and state explicitly what you are keeping out and why.\n",
    "- **Sort the controls: hook or prompt?:** for each of four controls (linter before commit, block migrations/ edits, secret scan before writes, prefer small functions), decide hook/deny rule versus CLAUDE.md instruction, with a one-sentence justification each.\n",
    "- **Split committed vs. local settings:** place the allow/deny list and hooks in `.claude/settings.json` (shared, in git) versus `.claude/settings.local.json` (personal, gitignored), and name one thing that belongs only in a local file.\n",
    "- **Define the code-review policy for AI-assisted changes:** write the review rule in one or two sentences covering review requirement, provenance, and the same-bar principle, with no AI-authorship exemption.\n",
    "- **Specify the CI step:** describe the headless Claude Code invocation in the pipeline, what it checks, where credentials come from, and why CI is the enforcement point.\n",
    "- **The regulator's non-optional control:** state the one control the regulator makes non-optional and show where it is enforced (not as a CLAUDE.md instruction).\n",
    "\n",
    "Your solution should include enough code, output, or written observations to prove each outcome worked. Keep the notebook focused on final behavior and evidence rather than a guided walkthrough.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Student Workspace\n",
    "\n",
    "Use the sections below to build your solution. Each section maps to one required outcome from the challenge outline.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Part 1: Write the project CLAUDE.md outline\n",
    "\n",
    "list the sections of the project CLAUDE.md with one line each, and state explicitly what you are keeping out and why.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Part 1: Write the project CLAUDE.md outline\n",
    "# Add your implementation, outputs, or notes here.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Part 2: Sort the controls: hook or prompt?\n",
    "\n",
    "for each of four controls (linter before commit, block migrations/ edits, secret scan before writes, prefer small functions), decide hook/deny rule versus CLAUDE.md instruction, with a one-sentence justification each.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Part 2: Sort the controls: hook or prompt?\n",
    "# Add your implementation, outputs, or notes here.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Part 3: Split committed vs. local settings\n",
    "\n",
    "place the allow/deny list and hooks in `.claude/settings.json` (shared, in git) versus `.claude/settings.local.json` (personal, gitignored), and name one thing that belongs only in a local file.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Part 3: Split committed vs. local settings\n",
    "# Add your implementation, outputs, or notes here.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Part 4: Define the code-review policy for AI-assisted changes\n",
    "\n",
    "write the review rule in one or two sentences covering review requirement, provenance, and the same-bar principle, with no AI-authorship exemption.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Part 4: Define the code-review policy for AI-assisted changes\n",
    "# Add your implementation, outputs, or notes here.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Part 5: Specify the CI step\n",
    "\n",
    "describe the headless Claude Code invocation in the pipeline, what it checks, where credentials come from, and why CI is the enforcement point.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Part 5: Specify the CI step\n",
    "# Add your implementation, outputs, or notes here.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Part 6: The regulator's non-optional control\n",
    "\n",
    "state the one control the regulator makes non-optional and show where it is enforced (not as a CLAUDE.md instruction).\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Part 6: The regulator's non-optional control\n",
    "# Add your implementation, outputs, or notes here.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Verification Notes\n",
    "\n",
    "Summarize the evidence that each part worked. Capture API signals, validation outcomes, errors, recovery behavior, cost observations, or comparisons required by this lab.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Verification notes\n",
    "# Record the evidence that proves each lab outcome worked.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## Answer Key\n",
    "\n",
    "The cells below contain the completed reference implementation/content for this lab. Use this section only after attempting the self-driven lab.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Lab 10 Answer Key*\n",
    "\n",
    "# Developer Productivity & Operational Enablement\n",
    "\n",
    "**Objective:** onboard a twelve-engineer fintech team onto Claude Code in a regulated codebase, producing the configuration a new hire inherits on their first clone.\n",
    "\n",
    "Domain 7 is the smallest domain on the exam (about four questions), and it has almost no API surface, so this notebook is deliberately a configuration worksheet rather than an API lab. The two cells that contain code produce config files, not programs.\n",
    "\n",
    "**Scenario recap:** a fintech team of twelve adopting Claude Code across a Python monorepo under regulatory scope. Denials of a customer action must be attributable to a named human, `migrations/` must never be edited without review, the linter is mandatory, and no secret ever reaches a commit.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Part 1: Write the project CLAUDE.md outline\n",
    "\n",
    "The project CLAUDE.md is the team's shared context, checked into the repository and reviewed like code. A CLAUDE.md that duplicates the codebase is a second thing to keep in sync and a place for drift to hide.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Project `./CLAUDE.md` outline (committed):**\n",
    "\n",
    "- **Build & test:** `uv sync` to install; `uv run pytest` must pass before any commit; `uv run ruff check .` is the mandatory linter.\n",
    "- **Architecture invariants:** every denial of a customer action requires a named human and is never auto-executed; the classification step stays a single augmented LLM call, not an agent loop; the structured-output schema in `schemas/` is the contract.\n",
    "- **Conventions:** type-hint everything, no bare `except`; all model calls go through `llm/client.py`, never an inline `anthropic.Anthropic()`.\n",
    "- **Never list:** never commit secrets (credentials come from the environment); never edit `migrations/` or `vendor/` without asking.\n",
    "\n",
    "**Kept out, deliberately:**\n",
    "\n",
    "- **Secrets.** Never in a context file: a key in CLAUDE.md is leaked to everyone who clones and to the model's context.\n",
    "- **Long prose and restated code.** Anything the code already says is a second copy that drifts; the file holds only what an engineer would otherwise learn by breaking it.\n",
    "- **Enforcement.** The named-human rule is *stated* here as context, but its *enforcement* lives in hooks and deny rules (Part 2), because prose is a request, not a control.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Part 2: Sort the controls: hook or prompt?\n",
    "\n",
    "Hooks enforce; prompts request. If a control must always hold, it belongs in a hook or a deny rule, not in a context file. The regulator's rule is the tell for at least one of them.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| Control | Mechanism | Why |\n",
    "|---|---|---|\n",
    "| Run the linter before commit | **Hook** (PreToolUse/pre-commit, exit non-zero on failure) | \"Mandatory\" means it must always hold; a hook executes and cannot be talked out of the way. |\n",
    "| Block edits to `migrations/` | **Deny rule** (permissions deny on Edit/Write for that path) | Removing the capability beats asking the model not to use it; a denied edit cannot happen by clever phrasing. |\n",
    "| Scan for secrets before any write | **Hook** (PreToolUse on Write/Edit running the scanner, blocking on hit) | The no-secret rule is regulatory and absolute, so it needs deterministic code that runs on every write event. |\n",
    "| Prefer small functions | **CLAUDE.md instruction** | This is style guidance with judgment involved; it should shape behavior, not gate it, and a failed \"preference\" should not block work. |\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Part 3: Split committed vs. local settings\n",
    "\n",
    "The committed `.claude/settings.json` is the team's shared policy: every clone inherits the same permissions, hooks, and MCP servers. `settings.local.json` is one engineer's personal overrides and is gitignored.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "\n",
    "# .claude/settings.json: COMMITTED. The team's shared policy.\n",
    "shared_settings = {\n",
    "    \"permissions\": {\n",
    "        \"allow\": [\n",
    "            \"Bash(uv run pytest:*)\",\n",
    "            \"Bash(uv run ruff:*)\",\n",
    "            \"Read\", \"Edit\", \"Grep\", \"Glob\",\n",
    "        ],\n",
    "        \"deny\": [\n",
    "            \"Edit(./migrations/**)\",\n",
    "            \"Write(./migrations/**)\",\n",
    "            \"Bash(rm -rf:*)\",\n",
    "            \"Bash(git push --force:*)\",\n",
    "            \"Read(./.env)\",\n",
    "            \"Read(./secrets/**)\",\n",
    "        ],\n",
    "    },\n",
    "    \"hooks\": {\n",
    "        \"PreToolUse\": [\n",
    "            {\n",
    "                \"matcher\": \"Write|Edit\",\n",
    "                \"hooks\": [{\"type\": \"command\", \"command\": \"./.claude/hooks/scan-secrets.sh\"}],\n",
    "            }\n",
    "        ]\n",
    "    },\n",
    "}\n",
    "\n",
    "# .claude/settings.local.json: GITIGNORED. One engineer's personal overrides,\n",
    "# e.g. a local-only MCP server or a personal editor integration. Never team policy.\n",
    "local_settings = {\n",
    "    \"permissions\": {\"allow\": [\"Bash(open http://localhost:8000:*)\"]},\n",
    "}\n",
    "\n",
    "print(json.dumps(shared_settings, indent=2))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The allow/deny list and the hooks are team policy, so they live in the committed file. A personal local-server allowance (or any preference that is one engineer's business) belongs only in `settings.local.json`. Enterprise-managed policy sits above both and overrides them; that is where the security team enforces org-wide rules.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Part 4: Define the code-review policy for AI-assisted changes\n",
    "\n",
    "Generated code is reviewed like any other code. The policy has three parts: a review requirement, provenance, and the flat rule that AI authorship changes nothing about the bar a change must clear.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**The policy:**\n",
    "\n",
    "> Every change, AI-assisted or not, merges only after approval by a human reviewer who did not author it, and AI-assisted commits are labelled as such (commit trailer or PR tag) so a regression can be traced to its provenance. AI authorship is never grounds for reduced scrutiny; if anything, a large generated diff earns more review, not less.\n",
    "\n",
    "Note what is absent: there is no fast path for \"the model wrote it and tests pass.\" Tests generated alongside the code can assert current behavior, bugs included, so a green suite is not a substitute for the reviewer.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Part 5: Specify the CI step\n",
    "\n",
    "Claude Code runs headless (non-interactively) as a pipeline step. The project CLAUDE.md and committed settings travel with the repo, so the pipeline inherits the same context, permissions, and hooks the whole team uses.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**The CI step:**\n",
    "\n",
    "```bash\n",
    "# Runs on every PR. Print mode, no file edits.\n",
    "claude -p \"Review the diff on this branch against our conventions in \\\n",
    "CLAUDE.md. List any violations, including edits under migrations/ and \\\n",
    "unreviewed denial-path changes. Do not edit files.\" > review-output.txt\n",
    "```\n",
    "\n",
    "- **What it checks:** convention violations against the project CLAUDE.md, migrations/ touches, and denial-path changes lacking the named-human gate.\n",
    "- **Credentials:** from the CI environment via Workload Identity Federation or an injected environment variable, never from a flag, a file in the repo, or CLAUDE.md.\n",
    "- **Why CI is the enforcement point:** a local run depends on each of twelve engineers remembering to run it; the pipeline runs on every PR unconditionally, and its failure blocks merge. Trusting individuals is a request; a required CI check is a control.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Part 6: The regulator's non-optional control\n",
    "\n",
    "A regulated constraint is a hook, a deny rule, or an egress gate, not a request. If your answer is an instruction in CLAUDE.md, revise it.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**The control:** no denial of a customer action is ever executed without a named human approver.\n",
    "\n",
    "**Where it is enforced:** in the application, not in the assistant. The denial branch of the workflow has no auto-execute path: the code requires an approver ID before the action fires, and the audit log rejects a denial record without one. On the developer side, the deny rules and secret-scan hook keep Claude Code from editing the gate out (`migrations/` and the denial-path modules are deny-listed for unattended edits), and the CI review step flags any PR that touches the denial path for mandatory human review.\n",
    "\n",
    "The CLAUDE.md line about the rule is context so the model designs *with* the gate; the enforcement is the code path, the deny rule, and the required CI check. Prose requests; hooks, gates, and pipelines enforce.\n"
   ]
  }
 ],
 "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
}
