---
name: fix-docker-parent-image-build-failures
description: >
  Triages and fixes vulnerability-scan failures in the theorchard/docker-parent-images
  Jenkins pipeline — finding the latest failed build (or a specific build number if the
  user names one) via Datadog CI Visibility (the `pup` CLI), parsing which parent images
  have blocking or near-expiry vulnerabilities, and recommending whether each one should
  be fixed in the Dockerfile or handled with a scan exception in python-deployment-utils'
  ecr_scan/config.py. Use this whenever the user mentions the docker-parent-images pipeline
  failing, docker parent image build failures, ECR scan/vulnerability failures blocking a
  docker-parent-images build, or asks to triage/fix/investigate why that pipeline is red —
  even if they just say something like "the parent images build is failing again" or "can
  you check the ecr scan failures." Always presents findings and a fix-vs-exception
  recommendation for user review before touching any repo, and never uses Jenkins tools
  directly — Datadog CI Visibility via `pup` is the source of truth here.
---

# Fix docker-parent-images Build Failures

`docker-parent-images` builds ~30 parent image variants in parallel and vulnerability-scans each one; a scan that finds a "blocking" vulnerability fails the whole variant's branch (and the build). This skill triages a failed build end to end: find it, understand exactly which vulnerabilities are blocking it (and which non-blocking ones are about to become blocking), work out whether each is better fixed in the Dockerfile or excepted in the scan config, and present that as a clear recommendation — before making any change.

**The hard rule underlying every step here: recommend, then wait for confirmation, then act.** This pipeline gates production image releases across the org, and the two repos involved (`docker-parent-images`, `python-deployment-utils`) are shared infrastructure. Treat every fix or exception as something the user signs off on, not something to apply autonomously — see "Present recommendations before acting" below.

## Workflow

### 1. Find the failed build and its vulnerabilities

By default, investigate the latest *failed* master build — not simply the most recent build, which may have passed. If the user names a specific build number instead (e.g. "check build 187"), use that one — no need to look up "latest" first.

Read **`references/finding-build-failures.md`** and follow it — it covers the exact `pup` commands to use (including scoping the pull to exactly one build via a `--query` filter, since a wide `--from` time window can silently pull in events from more than one build number), and the bundled `scripts/parse_build_findings.py` helper that does the table-parsing for you. One gotcha worth knowing about upfront: `pup cicd events search --level=job` doesn't return data for this pipeline — use `pipelines list` instead.

Out of this step you should have, per parent image variant: which are truly failing (blocking vulnerabilities), and which produced non-blocking warnings with a grace period of less than 7 days.

### 2. Work out fix vs. exception for each blocking (and near-expiry) finding

Read **`references/fix-vs-exception.md`**. In short: check whether the vulnerable package is installed directly in the Dockerfile or bundled in the base image, verify a fixed version actually exists before recommending it, and default to fixing rather than excepting — an exception is for when no fix exists yet or the available fix is disproportionately risky to rush in, not a shortcut around doing the fix.

For anything you conclude should be an exception instead, read **`references/exceptions-config.md`** to know where and how it belongs in `ecr_scan/config.py`.

### 3. Present recommendations before acting

Summarize for the user, per blocking vulnerability: the package, installed vs. fixed version, which image(s) it affects, and your fix-vs-exception recommendation with the reasoning. Include non-blocking findings under the grace-period threshold as a heads-up. Flag anything unusual you noticed about the build itself (e.g. it was parameterized in a way that suggests it isn't a normal release run) rather than silently treating it as routine.

**Get an explicit decision on each recommendation using `AskUserQuestion`, rather than a free-text "let me know what you'd like to do."** A structured choice is faster for the user to answer and impossible to misread compared to parsing a prose reply, and it keeps you from drifting into action on an ambiguous "sounds good."

Group by CVE, not by image — the same CVE (and the same recommendation) often applies to several image variants at once (e.g. one `pip` CVE hitting four different Dockerfiles), and asking about it four separate times is just noise. Phrase each question around the fix, e.g. "How should we handle CVE-2026-8643 (pip) affecting python312, lambda-python312, kafka-connect77, kafka-connect79?", with options along the lines of:
- Your recommended action (fix or exception) — labeled "(Recommended)"
- The other of fix/exception, if it's a plausible alternative
- Skip this one for now

`AskUserQuestion` allows at most 4 questions per call. If there are more than 4 CVE groups needing a decision — counting blocking and near-expiry findings together — ask in batches of up to 4 rather than truncating the list; every one of them needs an answer, not just the first four.

Ask about near-expiry (non-blocking) findings the same way, grouped by CVE — they haven't failed the build yet, but they're heading there, and it's cheap to get ahead of them now while you're already in the relevant Dockerfile or config file. Include a "leave it for now" option (it still has some grace period left, after all) alongside the fix/exception choices, and don't treat silence or a vague answer as consent to act — only proceed on the ones the user explicitly picked a fix/exception for.

**Only proceed to step 4 with the specific things the user said yes to.** Don't clone-and-edit-and-commit in the same breath as the analysis, and don't act on a recommendation the user didn't explicitly confirm — the value of doing the analysis carefully is wasted if anything changes without being reviewed first.

### 4. Apply the confirmed changes

Locate the user's local copies of the `theorchard/docker-parent-images` and `theorchard/python-deployment-utils` repos. If they don't have them, ask them to clone them locally and point you at the checkouts.

**Make the edits:**
- Dockerfile fixes go in `theorchard/docker-parent-images`.
- Exceptions go in `ecr_scan/config.py` in `theorchard/python-deployment-utils`.
- Keep each edit minimal and consistent with the file's existing style — this isn't the moment to also clean up unrelated things in the same file.

If the confirmed decisions span both repos (some fixes, some exceptions), there's no ordering dependency between them — they're separate files in separate repos, neither reads the other. Make the edits in parallel (batch the Dockerfile edits and the `config.py` edit into the same turn) rather than working through them one repo at a time.

**Report back** what was changed, where.
