{"spec_id":"burndown-sprint","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nburndown-sprint: Agile Sprint Burndown Chart\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 89/100 | Created: 2026-06-14\n\"\"\"\n\nimport sys\n\n\nsys.path = sys.path[1:]  # prevent this file from shadowing the seaborn library\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\n\n\n# Theme tokens — see prompts/default-style-guide.md \"Theme-adaptive Chrome\"\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint palette — canonical order, first series always #009E73\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\nBRAND = IMPRINT_PALETTE[0]  # actual burndown line — always first series\nDANGER = IMPRINT_PALETTE[4]  # scope-change marker — semantic: risk / alert\n\n# Data — 10-working-day sprint, 14 calendar days (Mon–Fri × 2 weeks)\nnp.random.seed(42)\ncalendar_days = np.arange(15)  # days 0 through 14\n\n# Actual remaining story points (step series, recorded at end-of-day)\nremaining = np.array(\n    [\n        40,  # Day 0  Mon W1 — sprint start, 40 SP committed\n        37,  # Day 1  Tue W1\n        34,  # Day 2  Wed W1\n        32,  # Day 3  Thu W1\n        36,  # Day 4  Fri W1 — scope change: burned 4, added 8 (net +4)\n        36,  # Day 5  Sat     — weekend, no progress\n        36,  # Day 6  Sun     — weekend, no progress\n        31,  # Day 7  Mon W2\n        25,  # Day 8  Tue W2\n        18,  # Day 9  Wed W2\n        10,  # Day 10 Thu W2\n        3,  # Day 11 Fri W2 — sprint winds down\n        3,  # Day 12 Sat     — weekend\n        3,  # Day 13 Sun     — weekend\n        0,  # Day 14 Mon     — retro day, all SP delivered\n    ]\n)\n\n# Ideal burndown: straight line from original scope (40 SP) on day 0 to 0 on day 14\nideal = 40.0 * (1.0 - calendar_days / 14.0)\n\n# X-axis labels: abbreviated day names across both sprint weeks\nday_labels = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\", \"Mon\"]\n\n# DataFrames for seaborn plotting API\ndf_actual = pd.DataFrame({\"day\": calendar_days, \"remaining\": remaining})\ndf_ideal = pd.DataFrame({\"day\": calendar_days, \"ideal\": ideal})\n\n# Seaborn theme — Imprint chrome tokens applied globally\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.15,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Plot\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Weekend shading bands (Sat–Sun, weeks 1 and 2)\nfor sat_day in [5, 12]:\n    ax.axvspan(sat_day - 0.5, sat_day + 1.5, color=INK_SOFT, alpha=0.07, zorder=0, linewidth=0)\n\n# Ideal burndown — dashed muted reference line via seaborn lineplot\nsns.lineplot(\n    data=df_ideal,\n    x=\"day\",\n    y=\"ideal\",\n    ax=ax,\n    color=INK_MUTED,\n    linewidth=2.0,\n    linestyle=\"--\",\n    label=\"Ideal burndown\",\n    alpha=0.9,\n    zorder=2,\n)\n\n# Actual remaining — step series via seaborn lineplot with drawstyle (brand green, always first series)\nsns.lineplot(\n    data=df_actual,\n    x=\"day\",\n    y=\"remaining\",\n    ax=ax,\n    color=BRAND,\n    linewidth=2.8,\n    drawstyle=\"steps-post\",\n    label=\"Actual remaining\",\n    zorder=4,\n)\nax.fill_between(calendar_days, remaining, 0, step=\"post\", color=BRAND, alpha=0.07, zorder=1)\n\n# Scope-change marker at day 4 — vertical dotted line (semantic danger red)\nax.axvline(x=4, color=DANGER, linewidth=1.5, linestyle=\":\", alpha=0.9, zorder=3)\nax.text(\n    4.22,\n    41.5,\n    \"+8 SP\\nscope change\",\n    fontsize=9,\n    color=DANGER,\n    va=\"top\",\n    ha=\"left\",\n    fontweight=\"medium\",\n    linespacing=1.4,\n)\n\n# Style\ntitle = \"burndown-sprint · python · seaborn · anyplot.ai\"\nax.set_title(title, fontsize=12, fontweight=\"medium\", color=INK, pad=12)\nax.set_xlabel(\"Sprint Day\", fontsize=10, color=INK, labelpad=6)\nax.set_ylabel(\"Remaining Story Points\", fontsize=10, color=INK, labelpad=6)\nax.tick_params(axis=\"both\", colors=INK_SOFT)\n\n# X-axis ticks — one per calendar day with abbreviated day name\nax.set_xticks(calendar_days)\nax.set_xticklabels(day_labels, fontsize=8, color=INK_SOFT)\nax.set_xlim(-0.5, 14.5)\nax.set_ylim(-2, 48)\n\n# Spines\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nax.spines[\"left\"].set_color(INK_SOFT)\nax.spines[\"bottom\"].set_color(INK_SOFT)\n\n# Subtle y-axis grid\nax.yaxis.grid(True, alpha=0.15, linewidth=0.8, color=INK, zorder=0)\nax.set_axisbelow(True)\n\n# Legend\nlegend = ax.legend(fontsize=8, loc=\"upper right\", framealpha=0.9, edgecolor=INK_SOFT, borderpad=0.8)\nlegend.get_frame().set_facecolor(ELEVATED_BG)\nfor text in legend.get_texts():\n    text.set_color(INK_SOFT)\n\nfig.subplots_adjust(left=0.09, right=0.97, top=0.91, bottom=0.12)\n\n# Save — no bbox_inches='tight' (seaborn canvas rule: figsize × dpi must be exact)\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\nplt.close()\n"}