# Plan: Initiative G — Phase 1 + 2 (Pure-Python Simple-API Client + Cache-Prime Bridge)

**Generated**: 2026-05-12
**Source design doc**: [`docs/dev/initiative-g-pure-python-design.md`](docs/dev/initiative-g-pure-python-design.md)
**Branch**: `maintenance/code-cleanup-phase5-perf-2026-06` (continues existing perf branch)
**Scope**: Phases 1 + 2 only.  Phase 3 (full `pure-python` backend) and Phase 4 (promote to default) get separate plans after Phase 1 lands and Phase 2 measurement gates pass.

---

## Overview

Phase 1 ships a self-contained PEP 691 simple-API client + parsed-manifest cache + parallel fetcher.  **No integration** with `do_lock`; the modules import zero `pip._internal.*` symbols.

Phase 2 wires the fetcher into `do_lock` behind an opt-in `[pipenv]` setting.  Pip continues to drive the actual resolve; the fetcher just warms pip's filesystem HTTP cache faster than pip's sequential per-package fetches would.  Phase 2 ships disabled-by-default because the earlier phase-5 parallel-prefetch experiment was net-harmful on warm-cache dev boxes; gating it on a setting lets CI / cold-cache users opt in without regressing dev-box workflows.

### Recommendations applied (from §10 of the design doc)

- **Q1** Cache format: **JSON** for phase 1 (debug-friendly; revisit in phase 3).
- **Q3** Default TTL: **600 s** (matches PyPI CDN).
- **Q5** Vendoring: **first-party** in `pipenv/resolver/` (not vendored, not patched).
- **Q6** Auth: **netrc + URL-embedded basic auth + `PIP_CLIENT_CERT`** (keyring deferred to phase 3).

### Acceptance criteria

**Phase 1:**
- All unit tests in T11–T16 pass.
- `PEP691Client.fetch` returns the same candidate set (name, version, url, hashes, requires_python, yanked) as pip's `Link.from_json` for the curated fixture suite in T2 (synthetic + real-PyPI snapshots, no live network) per T10.  Live-PyPI parity across 20 real packages is deferred to Phase 3's parity audit.
- Zero `pip._internal.*` imports in `pipenv/resolver/pep691.py`, `pipenv/resolver/candidate.py`, `pipenv/resolver/manifest_cache.py`, `pipenv/resolver/fetcher.py`, `pipenv/resolver/auth.py` (enforced by pre-commit grep gate added in T17).
- Coverage targets in T11–T15 enforced by `pytest --cov-fail-under` in CI for the new modules; coverage config update lives in T11/T17 (see those tasks).
- Zero new runtime dependencies.

**Phase 2:**
- All Phase 1 criteria still hold.
- `pipenv lock` succeeds end-to-end with `[pipenv] prefetch_index_manifests = true` set.
- CI lock-cold bench ≥10 % wall-time reduction vs phase-5 baseline (T21 measurement, **median of 3 runs**, delta must exceed 2× the run-to-run standard deviation observed in the baseline).
- CI lock-warm bench: ≥0 % (no regression beyond the same 2σ noise floor).  If we see a measurable regression on warm-cache CI runs, the setting stays off-by-default and we document that.
- User-facing documentation for the new setting shipped in T22.

---

## Prerequisites

- Phase-5 perf branch checked out (`maintenance/code-cleanup-phase5-perf-2026-06`, currently at `a6832ce3`).
- Initiative F backend registry already exists at `pipenv/resolver/backends/` — Phase 1+2 don't add to it (that's Phase 3's job).
- Vendored `pipenv.vendor.packaging` (version + tags + specifiers) available; vendored `pipenv.patched.pip._vendor.urllib3` available.

---

## Dependency Graph

```
                    T1 ── T2 ── T17
                     │     │
T3 ── T4 ── T5 ──────┤     │
                     │     │
                     T6 ───┤
                     │     │
                     T8 ───┴── T9 ── T10 ── T11
                     │
T7 ─── T8 ───────────┘
                                         T18 ── T19 ── T20 ── T21
                                          │
T12, T13, T14, T15, T16  (test waves)     T22
```

The visual is approximate; see the **Parallel Execution Groups** table below for the canonical wave breakdown.

---

## Tasks

### T1: `Candidate` dataclass and `Hash` namedtuple

- **depends_on**: `[]`
- **location**:
  - `pipenv/resolver/candidate.py` (new)
- **description**:
  Create `Candidate` as a `@dataclass(frozen=True, slots=True)` with the fields enumerated in design §5.1 (`name`, `version`, `url`, `filename`, `hashes`, `requires_python`, `yanked`, `yanked_reason`, `upload_time`, `is_wheel`, `wheel_tags`).  `Hash` is a `NamedTuple` of `(algo: str, value: str)` so it's hashable and tuple-compatible.  Provide `Candidate.from_filename(filename, **kwargs)` helper that derives `is_wheel` and `wheel_tags` (using `pipenv.vendor.packaging.tags.parse_tag` for wheels; `None` for sdists).  No I/O, no pip-internal imports.
- **validation**:
  - `python -c "from pipenv.resolver.candidate import Candidate, Hash; c = Candidate.from_filename('numpy-1.26.0-cp311-cp311-manylinux_2_17_x86_64.whl', name='numpy', version='1.26.0', url='https://...', hashes=frozenset(), requires_python='>=3.9', yanked=False, yanked_reason=None, upload_time=None); print(c.is_wheel, len(c.wheel_tags))"` prints `True` and a non-zero tag count.
  - Zero `pip._internal` imports: `grep -r "pip._internal" pipenv/resolver/candidate.py` returns empty.
- **status**: Completed
- **log**: RED→GREEN.  Wrote 8 smoke tests in `tests/unit/test_candidate.py` covering construction, `frozen=True` mutation rejection, wheel-tag derivation (manylinux + universal `py2.py3-none-any`), sdist branch, and `Hash` namedtuple equality + frozenset membership.  Initial run: 8 `ModuleNotFoundError` failures (module did not exist).  Implemented `pipenv/resolver/candidate.py` as `@dataclass(frozen=True, slots=True)` per design §5.1, with `Hash = NamedTuple(algo, value)` and `Candidate.from_filename` that splits the wheel filename per PEP 427 (`rsplit("-", 3)`), feeds the last three components to `pipenv.vendor.packaging.tags.parse_tag`, and returns `wheel_tags=None` for sdists.  Re-ran: 8/8 GREEN.  Gotchas: (a) the plan's acceptance-criteria one-liner passes `filename` both positionally and as a kwarg, which collides at the Python call-site level — fixed by making the positional argument **positional-only** (`def from_filename(cls, __filename, /, **kwargs)`), letting the caller redundantly pass `filename=` without raising; (b) initial docstring contained a literal "pip._internal" reference that tripped the no-pip-internal grep gate — rewrote as "patched-pip's internal package".  Validation: acceptance one-liner prints `True 1`; `grep -r "pip._internal" pipenv/resolver/candidate.py` exits with code 1 (no matches).
- **files edited/created**:
  - Created: `pipenv/resolver/candidate.py` (Candidate dataclass + Hash NamedTuple + `from_filename` classmethod).
  - Created: `tests/unit/test_candidate.py` (8 smoke tests for T1 acceptance — fuller coverage owned by T11).

---

### T2: PEP 691 JSON + PEP 503 HTML test fixtures

- **depends_on**: `[]`
- **location**:
  - `tests/unit/fixtures/pep691/` (new directory)
  - `tests/unit/fixtures/pep691/six.json` (small package)
  - `tests/unit/fixtures/pep691/django.json` (versioned package with many releases)
  - `tests/unit/fixtures/pep691/cryptography.json` (multi-platform wheels)
  - `tests/unit/fixtures/pep691/tablib.json` (sdist + wheel mix)
  - `tests/unit/fixtures/pep691/yanked-pkg.json` (synthetic — includes `yanked` field)
  - `tests/unit/fixtures/pep691/missing-hash.json` (synthetic — exercises missing-hash edge case)
  - `tests/unit/fixtures/pep503/six.html` (HTML mirror)
  - `tests/unit/fixtures/pep503/django.html`
  - `tests/unit/fixtures/pep503/cryptography.html`
  - `tests/unit/fixtures/pep503/yanked-pkg.html` (synthetic)
  - `tests/unit/fixtures/README.md` (provenance: when fetched, PEP versions)
- **description**:
  Curated, version-pinned samples of real PEP 691 (`application/vnd.pypi.simple.v1+json`) and PEP 503 HTML responses, sourced from `https://pypi.org/simple/<name>/` with `Accept: application/vnd.pypi.simple.v1+json` or default.  Save raw response bodies.  Synthetic samples (`yanked-pkg`, `missing-hash`) are hand-crafted to exercise edge cases the real fixtures may not hit.  Include a README documenting when each fixture was captured and the corresponding PyPI URL so future regenerations are traceable.
- **validation**:
  - `python -c "import json; print(len(json.load(open('tests/unit/fixtures/pep691/django.json'))['files']) > 10)"` prints `True`.
  - All `.html` files validate as parseable HTML via `html.parser.HTMLParser`.
  - The `yanked-pkg` JSON fixture has at least one entry with `"yanked": "<reason>"` (non-boolean form) and at least one with `"yanked": true`.
- **status**: Completed
- **log**:
  - 2026-05-12: Fetched four real PyPI snapshots (`six`, `django`, `cryptography`, `tablib`) via `curl -H 'Accept: application/vnd.pypi.simple.v1+json'` for JSON and `curl -H 'Accept: text/html'` for HTML.  All four JSON responses came back with `meta.api-version` = `"1.4"`, well inside the `"1.x"` range the parser must accept.
  - File entry counts: six=48, django=781, cryptography=3496, tablib=68 — django comfortably clears the `> 10` acceptance bar.
  - Hand-crafted two synthetic JSON fixtures (`yanked-pkg.json`, `missing-hash.json`) and one synthetic HTML fixture (`yanked-pkg.html`) covering the yanked-string-reason, yanked-bool-true, yanked-bool-false (control), and empty-`hashes` edge cases.  Synthetic JSON declares `meta.api-version` = `"1.0"` so it stays on the conservative end of the supported range.
  - All three acceptance checks pass: `len(django files) > 10` → `True`; `six.html`, `django.html`, `cryptography.html`, `yanked-pkg.html` all parse cleanly with `html.parser.HTMLParser`; `yanked-pkg.json` carries the bool-`true`, bool-`false` control, AND string-with-reason `"yanked"` forms.
  - Provenance (URLs, Accept headers, capture date, sizes) and re-baseline procedure documented in `tests/unit/fixtures/README.md`.
- **files edited/created**:
  - `tests/unit/fixtures/pep691/six.json` (new, 24,065 B — real PyPI)
  - `tests/unit/fixtures/pep691/django.json` (new, 401,061 B — real PyPI)
  - `tests/unit/fixtures/pep691/cryptography.json` (new, 2,350,042 B — real PyPI)
  - `tests/unit/fixtures/pep691/tablib.json` (new, 32,763 B — real PyPI)
  - `tests/unit/fixtures/pep691/yanked-pkg.json` (new, synthetic)
  - `tests/unit/fixtures/pep691/missing-hash.json` (new, synthetic)
  - `tests/unit/fixtures/pep503/six.html` (new, 16,337 B — real PyPI)
  - `tests/unit/fixtures/pep503/django.html` (new, 284,321 B — real PyPI)
  - `tests/unit/fixtures/pep503/cryptography.html` (new, 1,945,696 B — real PyPI)
  - `tests/unit/fixtures/pep503/yanked-pkg.html` (new, synthetic)
  - `tests/unit/fixtures/README.md` (new — provenance + re-baseline procedure)

---

### T3: `SimplePageResponse`, `FetchError`, status enum types

- **depends_on**: `[T1]`
- **location**:
  - `pipenv/resolver/pep691_types.py` (new — keeps the parser module focused on parsing logic)
- **description**:
  Define `SimplePageResponse` (frozen dataclass with `candidates: tuple[Candidate, ...]`, `etag: str | None`, `last_modified: str | None`, `raw_meta: dict`, `status: Literal["fresh", "not-modified", "missing"]`).  Define `FetchError` (frozen dataclass with `kind: Literal["missing", "transient", "auth"]`, `url: str`, `message: str`, `original: BaseException | None`).  Pure data; no pip-internal imports.
- **validation**:
  - `python -c "from pipenv.resolver.pep691_types import SimplePageResponse, FetchError; r = SimplePageResponse(candidates=(), etag=None, last_modified=None, raw_meta={}, status='missing'); print(r.status)"` prints `missing`.
  - `grep -r "pip._internal" pipenv/resolver/pep691_types.py` returns empty.
- **status**: Completed
- **log**:
  - 2026-05-12: RED→GREEN.  Pre-implementation `python -c "from pipenv.resolver.pep691_types import SimplePageResponse, FetchError"` failed with `ModuleNotFoundError: No module named 'pipenv.resolver.pep691_types'`.  Implemented `pipenv/resolver/pep691_types.py` per design §5.1: two `@dataclass(frozen=True, slots=True)` types (`SimplePageResponse`, `FetchError`) plus two `Literal` aliases (`SimplePageStatus`, `FetchErrorKind`).  `FetchError.original` defaults to `None` (callers that only have an HTTP status — e.g., a clean 401 — needn't synthesise an exception).  `raw_meta` typed as bare `dict` (not `dict[str, Any]`) per the plan brief, so forward-compat PyPI fields land in this bucket without a type-check failure.  No mutable default value: `raw_meta` is a required positional argument, matching the design-doc spec.
  - GREEN evidence: (a) acceptance one-liner `python -c "from pipenv.resolver.pep691_types import SimplePageResponse, FetchError; r = SimplePageResponse(candidates=(), etag=None, last_modified=None, raw_meta={}, status='missing'); print(r.status)"` prints `missing`.  (b) `grep "pip._internal" pipenv/resolver/pep691_types.py` exits with code 1 (no matches).  (c) extended smoke confirms: `r.status = 'missing'` on a frozen instance raises `dataclasses.FrozenInstanceError`; `FetchError(kind='missing', url='...', message='...')` constructs with `original=None`; both classes have proper `__slots__` (no `__dict__`).  Tests land transitively in T13 (PEP691Client tests), so no test file is committed for this task.
  - No regressions: zero `pip._internal` references, zero new runtime dependencies, mirrors `Candidate`'s `frozen=True, slots=True` style.
- **files edited/created**:
  - Created: `pipenv/resolver/pep691_types.py` (two frozen dataclasses + two Literal aliases).

---

### T4: PEP 691 JSON parser (`_parse_pep691_json`)

- **depends_on**: `[T1, T2, T3]`
- **location**:
  - `pipenv/resolver/pep691.py` (new file — parser function)
- **description**:
  Implement `_parse_pep691_json(body: bytes, page_url: str) -> tuple[Candidate, ...]`.  Parses PEP 691 JSON per spec (file objects with `filename`, `url`, `hashes` dict, optional `requires-python`, `yanked` bool-or-string, `upload-time`, `core-metadata`).  Resolves relative URLs against `page_url` once at parse time (replaces pip's `_ensure_quoted_url`-per-link cost).  Normalises hashes to `frozenset[Hash]`.  Yanked: `True` for bool `True` *or* non-empty string; `yanked_reason` carries the string.  Validates `meta.api-version` starts with `"1."`; logs (via `pipenv.utils.err`) and continues on unknown major.  Skips file entries with malformed JSON without raising — caller gets the partial tuple.
- **validation**:
  - All entries in `tests/unit/fixtures/pep691/six.json` round-trip into `Candidate` tuples with non-empty `name`, `version`, `url`.
  - URL with relative `href` (e.g., `"url": "../../packages/foo.whl"`) resolves to an absolute `https://...` URL.
  - The `yanked-pkg.json` fixture's `"yanked": "security-advisory-CVE-...."` entry produces a `Candidate` with `yanked=True` and `yanked_reason="security-advisory-CVE-..."`.
  - Unit-test coverage in T13 ≥ 95 % for this function.
- **status**: Completed
- **log**:
  - 2026-05-12: Implemented `pipenv/resolver/pep691.py` on branch `maintenance/code-cleanup-phase5-perf-2026-06` (commit `de425bcd`). Public-shape: module-level private function `_parse_pep691_json(body: bytes, page_url: str) -> tuple[Candidate, ...]`; T8 will add the `PEP691Client` class to the same module without touching the parser.
  - Helpers (all module-private): `_parse_upload_time` (3.9-compat `Z`→`+00:00` fallback), `_normalize_yanked` (bool-or-string flatten), `_normalize_hashes` (algo-lower frozenset[Hash]), `_strip_archive_suffix` (`.tar.gz` / `.tar.bz2` / `.whl` / `.zip` / `.tgz`), `_extract_version` (strips canonical-name prefix accepting `-`↔`_` and case variants, then takes first `-`-chunk for wheels / whole remainder for sdists), `_build_candidate` (per-entry construction, returns `None` on any failure).
  - RED→GREEN evidence: pre-impl smoke `tests/unit/test_pep691_parser_smoke.py` (4 tests) collected with `ModuleNotFoundError: No module named 'pipenv.resolver.pep691'`. Post-impl: 4/4 GREEN. Three plan acceptance criteria pass inline: (1) `six.json` → 48/48 candidates with non-empty `name`/`version`/`url` (`name="six"`, urls start `https://`); (2) synthetic relative-url JSON `"../files/x.whl"` against `https://pypi.org/simple/foo/` resolves to `https://pypi.org/simple/files/x.whl` (note: plan brief example was slightly off — `urljoin` only pops one path segment per `..`, so reaching `/files/` from `/simple/foo/` needs `../../files/`; either form yields an absolute `https://` URL as required); (3) `yanked-pkg.json` CVE entry → `yanked=True, yanked_reason="security-advisory-CVE-2024-99999"`.
  - Full-fixture round-trip counts (no entries dropped on any real-PyPI fixture): six=48/48, django=781/781, cryptography=3496/3496, tablib=68/68, yanked-pkg=4/4, missing-hash=2/2.
  - Constraints met: zero `pip._internal` imports (`grep "pip._internal" pipenv/resolver/pep691.py` → exit 1 / empty); zero imports of `SimplePageResponse`/`FetchError` from `pep691_types` (T8's territory) — only stdlib (`json`, `datetime`, `typing.Any`, `urllib.parse.urljoin`) + `pipenv.resolver.candidate.{Candidate, Hash}`. Parser is a pure function (no globals beyond `_SUPPORTED_API_MAJOR = "1."`, no I/O, no HTTP).
  - Yanked semantics: empty-string `"yanked": ""` is resolved as **not yanked** (conservative — a misbehaving index can't mark every release yanked via a stray `""`). Documented in `_normalize_yanked` docstring.
  - Malformed-wheel handling: T11's `TestEdgeCases::test_malformed_wheel_filename_raises_value_error` pinned `Candidate.from_filename` to **raise** `ValueError` on a wheel filename without the PEP 427 tag triple. `_build_candidate` catches `(ValueError, TypeError)` and returns `None`, so the bad entry is silently skipped and well-formed siblings still appear in the tuple. The plan brief said "the verbosity gate lives at the client layer" — this parser emits nothing.
  - Smoke test deleted before commit (T12 writes the real test suite for both parsers). `git status` post-delete: only `pipenv/resolver/pep691.py` staged. Initial commit attempt failed ruff `I001` (import-block sort: `from __future__` needed its own block); `ruff check --fix` resolved it, re-ran inline acceptance to confirm no behavioural change, re-committed cleanly.
- **files edited/created**:
  - Created: `pipenv/resolver/pep691.py` (~306 LOC; `_parse_pep691_json` + six private helpers + `_SUPPORTED_API_MAJOR` constant).

---

### T5: PEP 503 HTML parser (`_parse_pep503_html`)

- **depends_on**: `[T1, T2, T3]`
- **location**:
  - `pipenv/resolver/pep691.py` (additional function in the same module)
- **description**:
  Implement `_parse_pep503_html(body: bytes, page_url: str) -> tuple[Candidate, ...]` using `html.parser.HTMLParser` (stdlib).  Extract anchor tags; for each `<a href="...">` collect `data-requires-python`, `data-yanked`, `data-core-metadata` attributes, plus hash from the `#sha256=<hex>` URL fragment.  Same normalization rules as T4.  HTML fixtures from T2 are the validation set.  Same parity of output: an HTML fixture for `django` must produce the same `Candidate` set (modulo possible missing fields HTML can't express, like `upload_time`) as the JSON fixture for `django`.
- **validation**:
  - `set(c.filename for c in _parse_pep503_html(html_fixture, page_url))` equals the set parsed from the matching JSON fixture (T4 output, filtered to fields HTML supports).
  - Hash extraction from URL fragments: `<a href="...whl#sha256=ABC123">` produces `Hash("sha256", "ABC123")` in the candidate's `hashes` set.
  - Unit-test coverage in T13 ≥ 95 %.
- **status**: Completed
- **log**:
  - 2026-05-12: Extended `pipenv/resolver/pep691.py` (T4's file) with `_parse_pep503_html(body: bytes, page_url: str) -> tuple[Candidate, ...]` on branch `maintenance/code-cleanup-phase5-perf-2026-06`. Implementation reuses T4's helpers wholesale (`_extract_version`, `_strip_archive_suffix`, `Candidate.from_filename`) so JSON and HTML parsers emit equivalent candidate sets for the same package — invariant verified by inline cross-format parity at 100% overlap on every available fixture pair (see counts below).
  - New module-level additions (all module-private except the public function): `_AnchorCollector` (small `HTMLParser` subclass that collects `(attrs_dict, inner_text)` tuples per `<a>`), `_split_href_hash` (splits `href` on `#`, lower-cases algo, returns `(base, frozenset[Hash])`), `_normalize_yanked_html` (HTML-specific yanked semantics — see below), `_package_name_from_page_url` (derives canonical package name from `urlsplit(page_url).path` via `packaging.utils.canonicalize_name`), `_build_candidate_from_html` (per-anchor construction, parallels T4's `_build_candidate`).
  - New imports: stdlib `html.parser.HTMLParser`, `html.unescape`, `urllib.parse.urlsplit` (alongside T4's existing `urljoin`); `pipenv.vendor.packaging.utils.canonicalize_name`. Zero `pip._internal` imports verified by `grep "pip._internal" pipenv/resolver/pep691.py` → exit 1.
  - **Yanked semantics divergence (intentional)** pinned in `_normalize_yanked_html` docstring: absent `data-yanked` attribute → `yanked=False`; present with empty value (`data-yanked=""`) → `yanked=True, reason=None`; present with text → `yanked=True, reason=<unescaped text>`. This *differs from* T4's `_normalize_yanked` (JSON path treats empty-string `"yanked": ""` as not-yanked). HTML's *presence* of the attribute is the unambiguous signal (per the de-facto PEP 503 extension that aligned PEP 592 to the HTML serialisation); JSON's empty-string is a value an index might serve without intent to yank. T12 will pin both behaviours against the shared `yanked-pkg.{html,json}` fixture pair.
  - PEP 658 (`data-core-metadata` / `data-dist-info-metadata`) is observed by `_AnchorCollector` but **deferred** — not threaded into the `Candidate`. Phase-3 work.
  - HTML entity handling: `data-requires-python="&gt;=2.7"` (and the reason text for `data-yanked`) is run through `html.unescape` at consumption time. HTMLParser doesn't unescape attribute values for us. Validated inline against six.html (`>=2.7`-prefixed specifiers round-trip correctly).
  - RED→GREEN evidence: pre-impl `python -c "from pipenv.resolver.pep691 import _parse_pep503_html"` → `ImportError: cannot import name '_parse_pep503_html'`. Smoke `tests/unit/test_pep503_parser_smoke.py` (7 tests) collected with `ImportError`. Post-impl: 7/7 GREEN. Three plan acceptance criteria pass inline: (1) `set(c.filename for c in _parse_pep503_html(six.html))` equals `_parse_pep691_json(six.json)`'s filename set — **48/48 (100%)**; (2) `<a href="...#sha256=ABC123">` → `Hash("sha256", "ABC123")` (algo lower-cased, value preserved verbatim per design — value-case is *not* normalised because hash hex is case-insensitive by convention but the spec doesn't mandate it); (3) `data-yanked=""` → `yanked=True, reason=None`, `data-yanked="security-advisory-CVE-2024-99999"` → `yanked=True, reason="security-advisory-CVE-2024-99999"`.
  - Full-fixture cross-format parity (HTML candidates vs JSON candidates, by filename): six=48/48 (100%), django=781/781 (100%), cryptography=3496/3496 (100%), yanked-pkg=4/4 (100%). Zero divergence on any fixture pair — well above the 80% acceptance floor.
  - Smoke deleted before commit per plan brief (T12 writes the real test suite). `git status` post-delete: only `pipenv/resolver/pep691.py` modified in the scope of this task. `python -m ruff check pipenv/resolver/pep691.py` → "All checks passed!" pre-commit. T4's `_parse_pep691_json` behaviour verified unchanged by the parity test (same 48 candidates for `six.json`).
  - Gotcha for T12: `_split_href_hash` lower-cases the algo (`sha256` not `SHA256`) but preserves the value verbatim. The acceptance criterion says `Hash("sha256", "ABC123")` for `#sha256=ABC123` — note the **value** is `"ABC123"`, not `"abc123"`. Cross-format parity holds because real PyPI emits lower-case hashes in both JSON and HTML, but a synthetic fixture with mixed-case values would expose the (correct) verbatim-preservation behaviour.
- **files edited/created**:
  - Edited: `pipenv/resolver/pep691.py` (extends T4's file: +313 / -2; total ~617 LOC; adds `_parse_pep503_html` + `_AnchorCollector` + four private helpers without touching T4's `_parse_pep691_json` or its helpers).

---

### T6: Auth helpers (netrc, URL credentials, client cert)

- **depends_on**: `[]`
- **location**:
  - `pipenv/resolver/auth.py` (new) — implementation only.  The test file is owned exclusively by T16.
- **description**:
  Implement three small helpers:
  - `extract_url_credentials(url: str) -> tuple[str, tuple[str, str] | None]`: returns `(stripped_url, (username, password))` per `pipenv.utils.internet._strip_credentials_from_url`'s contract (the function already exists and works; we re-use the same logic but expose it through the resolver namespace so the new client doesn't import from `pipenv.utils.internet`).
  - `lookup_netrc_auth(host: str, netrc_path: str | None = None) -> tuple[str, str] | None`: reads `~/.netrc` (or `_netrc` on Windows) and returns `(login, password)` if there's a matching `machine` entry.  Respects `$NETRC` env var.
  - `client_cert_from_env() -> tuple[str, str] | None`: reads `PIP_CLIENT_CERT` env var (matches pip's existing convention).
  Keyring is **not** in scope for Phase 1.
- **validation**:
  - Tests live in T16; this task's validation is that the three helpers exist with the documented signatures and pass T16's tests once T16 lands.
- **status**: Completed
- **log**:
  - 2026-05-12: Implemented `pipenv/resolver/auth.py` with the three documented helpers (`extract_url_credentials`, `lookup_netrc_auth`, `client_cert_from_env`). Pure stdlib (`os`, `urllib.parse`, `netrc`); zero `pip._internal` imports (verified by `grep "pip._internal" pipenv/resolver/auth.py` → empty). `extract_url_credentials` mirrors `pipenv.utils.internet._strip_credentials_from_url` byte-for-byte (URL-decodes creds via `unquote`; rebuilds netloc via `urlunsplit`). `lookup_netrc_auth` tries explicit-arg → `$NETRC` → `~/.netrc` (or `~/_netrc` on Windows) in priority order, swallows `netrc.NetrcParseError` + `OSError`, returns `None` on any failure or missing entry. `client_cert_from_env` returns `(value, value)` for `$PIP_CLIENT_CERT` (matches pip's single-path convention; callers expecting `(cert, key)` pairs work unchanged). RED→GREEN evidence: pre-impl `python -c "from pipenv.resolver.auth import ..."` → `ModuleNotFoundError`; post-impl prints `('https://host/path', ('user', 'pass'))` and `None` for the two documented smoke cases. Tests deferred to T16 per the revised plan.
- **files edited/created**:
  - `pipenv/resolver/auth.py` (new)

---

### T7: `ParsedManifestCache` with TTL, atomic write, schema version

- **depends_on**: `[T1]`
- **location**:
  - `pipenv/resolver/manifest_cache.py` (new)
- **description**:
  Implement the class per design §5.2:
  - `__init__(self, root: Path, schema_version: int = 1)`.
  - `get(index_url, package_name) -> CachedManifest | None` — reads from `<root>/manifests-v<sv>/<sha256(index_url)>/<pep503_name>.json`, returns `None` if file is missing OR `expires_at` is in the past.
  - `put(index_url, package_name, candidates, etag, ttl_seconds=600)` — atomic write via `tempfile.NamedTemporaryFile` + `os.replace`.  Serialises `CachedManifest` (and its embedded `Candidate` tuple) as JSON, with a `schema_version` field and `cached_at` / `expires_at` ISO timestamps.
  - `invalidate(index_url, package_name)` — best-effort `os.unlink`.
  - `clear_all()` — `shutil.rmtree(<root>)`, used by `--clear`.
  - Custom `_candidate_to_json` / `_candidate_from_json` helpers handle `frozenset` (→ sorted list), `datetime` (→ ISO), `Tag` (→ string).  Round-trip preserves all fields.
  JSON for Phase 1 per Q1.  Concurrent read/write safety inherits from `os.replace` atomicity + per-file granularity.
- **validation**:
  - Round-trip test: `cache.put("https://pypi.org/simple", "django", candidates, etag="abc"); m = cache.get("https://pypi.org/simple", "django"); assert m.candidates == candidates and m.etag == "abc"`.
  - TTL expiry: with `ttl_seconds=0`, `cache.get(...)` returns `None` immediately after `put`.
  - Schema version mismatch: a manifest written under `schema_version=99` should be ignored (return `None`) when read by a `schema_version=1` cache.
  - Atomic write: kill the process during a `put` (simulated by raising mid-write) → the existing manifest file is unchanged, no partial write visible.
- **status**: Completed
- **log**:
  - 2026-05-12: Implemented `pipenv/resolver/manifest_cache.py` per design §5.2. Module exports `CachedManifest` (`@dataclass(frozen=True, slots=True)`) and `ParsedManifestCache` with the four documented methods (`get`, `put`, `invalidate`, `clear_all`) plus `SCHEMA_VERSION = 1` class constant. Cache root is caller-supplied — class does NOT default to `~/.cache/pipenv/...` (that decision lives with T9 / T19 per plan instructions). Disk path layout: `<root>/manifests-v<sv>/<sha256(index_url)>/<canonicalize_name(pkg)>.json` (full 64-char sha256 to avoid prefix collisions per T14's collision test). Atomic write: `tempfile.NamedTemporaryFile(delete=False, dir=parent)` → `tmp.write` → `tmp.flush` → `os.fsync` → `tmp.close` → `os.replace(tmp.name, target)`, with best-effort temp-file cleanup on the failure path. Datetimes are timezone-aware UTC throughout (`datetime.now(timezone.utc)`, never naïve). Serialisation helpers: `_candidate_to_json` flattens `frozenset[Hash]` → sorted `list[list[str, str]]`, `frozenset[Tag]` → sorted `list[str]`, `datetime` → ISO 8601; `_candidate_from_json` reads cached `wheel_tags` directly when present (preserving deterministic round-trips even across `packaging.tags` upgrades) and falls back to `Candidate.from_filename` only when the field is missing (forward-compat shim). RED→GREEN evidence: pre-impl `python -c "from pipenv.resolver.manifest_cache import ParsedManifestCache"` raised `ModuleNotFoundError`; post-impl all four plan acceptance scenarios pass inline (round-trip, ttl_seconds=0, schema_version=99 mismatch ignored, mid-write `os.replace` crash leaves prior payload intact). `grep -n "pip._internal" pipenv/resolver/manifest_cache.py` returns no matches (exit 1). Tests deferred to T14 per the revised plan (no smoke file committed).
  - Gotcha for T14: writer-vs-writer test — compare the loaded payload to **either** of the two known candidate-sets rather than asserting which one won. `datetime.now(timezone.utc)` can produce equal timestamps on fast hardware, so "later cached_at wins" is not an invariant; the actual invariant is last-`os.replace`-wins, which the test can't observe ordering on without explicit synchronisation.
  - Gotcha for T14: corruption tests should write raw bytes directly (e.g. `target.write_bytes(b"not json")`) — `get()` swallows `json.JSONDecodeError`, `UnicodeDecodeError`, missing keys, and bad ISO timestamps as misses (returns `None`). The T14 atomic-write test should assert no `<target>.<rand>.tmp` litter on the **success** path; the failure path may legitimately leave one stale temp file behind (documented in the `put` docstring — aggressive cleanup risks racing another writer's temp file in the same directory).
- **files edited/created**:
  - Created: `pipenv/resolver/manifest_cache.py` (CachedManifest + ParsedManifestCache + private serialisation helpers; ~391 LOC).

---

### T8: `PEP691Client` class

- **depends_on**: `[T4, T5, T6]`
- **location**:
  - `pipenv/resolver/pep691.py` (class added alongside the parser functions from T4/T5)
- **description**:
  Implement `PEP691Client(session, *, netrc_path=None, cert=None)`.  `session` is a `urllib3.PoolManager` (default-constructed when not supplied; tests pass a `MagicMock`).  `fetch(index_url, package_name, *, if_none_match=None) -> SimplePageResponse | FetchError`:
  - Canonicalises `package_name` via `packaging.utils.canonicalize_name`.
  - Strips URL credentials with T6's helper; constructs request URL `{stripped_index_url}/{canonical_name}/`.
  - Builds headers: `Accept: application/vnd.pypi.simple.v1+json, application/vnd.pypi.simple.v1+html; q=0.1, text/html; q=0.01`.  **Does NOT** send `Cache-Control: max-age=0` (deliberate divergence from pip; freshness is controlled by `ParsedManifestCache.expires_at` instead).  Adds `If-None-Match` when caller supplies `if_none_match`.  Adds `Authorization: Basic <b64>` when credentials were extracted from URL.  Falls back to netrc when no URL creds and a netrc match exists.
  - Sends `GET`.  Branches on `response.status`:
    - `200`: pick parser based on `response.headers["Content-Type"]`.  JSON content-type → T4; HTML or `text/html` → T5.  Returns `SimplePageResponse(status="fresh", ...)` with `etag` from `ETag` header.
    - `304`: returns `SimplePageResponse(status="not-modified", candidates=(), etag=if_none_match, ...)`.  Caller is expected to keep using the previously-cached candidates.
    - `404`: returns `SimplePageResponse(status="missing", candidates=(), ...)`.
    - `401` / `403`: returns `FetchError(kind="auth", ...)`.
    - Other `4xx` / `5xx` / network error: returns `FetchError(kind="transient", ...)`.
  No retries inside the client; retry policy lives in the fetcher (T9).
- **validation**:
  - Mock `session.request` with a 200 JSON response from T2's `six.json` → returns `SimplePageResponse(status="fresh")` with `candidates` matching T4's direct-parser output.
  - Mock 304 with `if_none_match="abc"` → returns `SimplePageResponse(status="not-modified", etag="abc")`.
  - Mock 404 → `status="missing"`.
  - Mock 401 → returns `FetchError(kind="auth")`.
  - URL with embedded creds (`https://u:p@host/simple`) → outgoing request has `Authorization` header AND request URL is `https://host/simple/<pkg>/` (no creds leaked into URL).
- **status**: Completed
- **log**:
  - 2026-05-12: Implemented `PEP691Client` in `pipenv/resolver/pep691.py` (commit appends the class + two small helpers `_basic_auth_header` / `_get_header` after T4/T5's parser functions; T4/T5 code untouched). Public shape: `PEP691Client(session, *, netrc_path=None, cert=None, verify=True)` constructor + `fetch(index_url, package_name, *, if_none_match=None) -> SimplePageResponse | FetchError`. Module-private internals: `_build_auth_header`, `_dispatch_response`. Module-private constants: `_ACCEPT_HEADER`, `_JSON_CT_PREFIX`, `_HTML_CT_PREFIXES`, `_DEFAULT_CONNECT_TIMEOUT=10.0`, `_DEFAULT_READ_TIMEOUT=30.0`. urllib3 sourced from `pipenv.patched.pip._vendor.urllib3` (NOT `_internal`); grep gate clean: `grep -n "pip._internal" pipenv/resolver/pep691.py` → exit 1 / no matches. Algorithm follows plan brief exactly: (1) canonicalize_name via `pipenv.vendor.packaging.utils`; (2) strip URL creds via T6's `extract_url_credentials`; (3) build `{stripped}/{canonical}/`; (4) compose headers — `Accept` always (with PEP 691 JSON preferred, HTML at q=0.1, text/html at q=0.01); `If-None-Match` when caller supplies; `Authorization: Basic <b64(user:pass).utf-8>` from URL creds OR netrc fallback via T6's `lookup_netrc_auth` (URL creds win when both present); NO `Cache-Control: max-age=0` (deliberate divergence from pip — freshness is the cache layer's job). (5) GET via `self._session.request("GET", url, headers=, timeout=urllib3.Timeout(connect=10, read=30))`; `urllib3.exceptions.HTTPError` (the base of MaxRetryError / ProtocolError / TimeoutError / SSLError / NewConnectionError) and `OSError` → `FetchError(kind="transient", original=exc)`. (6) Status dispatch: 200 → Content-Type-driven parser (case-insensitive prefix match on `application/vnd.pypi.simple.v1+json` → T4; `application/vnd.pypi.simple.v1+html` or `text/html` → T5; anything else → `FetchError("transient", "unexpected content-type: ...")`; JSON `ValueError` on a malformed 200 body → `FetchError("transient", original=exc)`); 304 → `SimplePageResponse(status="not-modified", etag=if_none_match, candidates=())` (carries caller's ETag forward, NOT the response's — RFC 7232 §4.1 servers vary on whether they re-emit it); 404 → `SimplePageResponse(status="missing", candidates=())`; 401/403 → `FetchError(kind="auth")`; other 4xx/5xx → `FetchError(kind="transient")`. Connection hygiene: `response.release_conn()` always called via `finally` (even on parse failure inside the 200 branch), with a defensive try/except so a pool-release failure doesn't mask the return value. Header access: `_get_header` is case-insensitive — works against urllib3's `HTTPHeaderDict` (which has case-insensitive `.get`) AND plain-dict mocks (linear scan fallback). Auth-header value: `"Basic " + base64.b64encode(f"{user}:{password}".encode("utf-8")).decode("ascii")` per RFC 7617. Host extraction for netrc: `urllib3.util.parse_url(stripped_url).host` (port-less hostname is what netrc keys off per RFC 1929). `cert` constructor arg falls back to `client_cert_from_env()` (`$PIP_CLIENT_CERT`); both `cert` and `verify` are stored on `self` but the active session is the source of truth — a phase-3 `Session` rewrite is needed before per-request TLS threading. Logging discipline: `_LOGGER.debug` on transient/parse errors logs only the *stripped* URL; we never log Authorization headers or the unstripped URL. RED→GREEN evidence (inline run): all 5 plan acceptance items pass (1: 200 JSON six.json → 48 candidates matching `_parse_pep691_json` direct call, etag carried; 2: 304 + if_none_match='"abc"' → status='not-modified', etag='"abc"'; 3: 404 → status='missing'; 4: 401 → FetchError(kind='auth'); 5: `https://u:p@host.example/simple` → outgoing URL `https://host.example/simple/six/` (no creds in URL) + `Authorization: Basic dTpw`) plus a 6th smoke (urllib3 `MaxRetryError` → `FetchError(kind='transient', original=exc)`). RED pre-impl: `python -c "from pipenv.resolver.pep691 import PEP691Client"` raised `ImportError`. Six-test smoke file (`tests/unit/test_pep691_client_smoke.py`) authored RED, run GREEN, then deleted before commit per plan — full client test suite lands in T13.
- **files edited/created**:
  - Edited: `pipenv/resolver/pep691.py` (+~410 lines: PEP691Client class with constructor + fetch + two private methods + two module-level helpers + 5 module constants; T4/T5 parser code untouched).

---

### T9: `ParallelFetcher` with thread pool

- **depends_on**: `[T7, T8]`
- **location**:
  - `pipenv/resolver/fetcher.py` (new)
- **description**:
  Implement `ParallelFetcher(client, cache, max_workers=16, default_ttl=600)`.  Method `populate(targets: Sequence[tuple[str, str]]) -> dict[str, CachedManifest | FetchError]` where `targets` is `[(index_url, package_name), ...]`.  For each target:
  - Check `cache.get(...)`; if fresh, skip the fetch (no network).
  - Otherwise dispatch `client.fetch(...)` on the executor, passing `if_none_match` if a stale-but-present cache entry exists.
  - On `status="fresh"` → `cache.put(...)`; record the new `CachedManifest`.
  - On `status="not-modified"` → keep existing cache, extend `expires_at` by TTL.
  - On `status="missing"` → record as `FetchError(kind="missing")`.
  - On `FetchError` → recorded directly; per-target failure does not stop other workers.
  Returns the dict keyed by `package_name` (NOT by full target tuple — duplicate names across sources merge with last-fetch-wins; tests cover this).  Max workers capped at 16 (matches the urllib3 default pool size; beyond that we hit "Connection pool is full, discarding connection" warnings, verified empirically in phase-5).
- **validation**:
  - Populate with 5 fresh targets (no cache) → all 5 result entries are `CachedManifest`.
  - Populate same 5 again (cache warm, within TTL) → no `client.fetch` calls (verified by mock), all 5 returned from cache.
  - One target raises `FetchError(kind="transient")` → other 4 still complete with `CachedManifest`; dict has 4 `CachedManifest` + 1 `FetchError`.
  - **Parallelism observable via dispatch order, NOT wall-time.**  With a mock `client.fetch` that records dispatch timestamps, assert ≥80 % of 32 dispatches were submitted before the first dispatch returned.  Wall-time assertions are forbidden in unit tests (CI shared runners flake them).
- **status**: Completed
- **log**:
  - 2026-05-12: Implemented `pipenv/resolver/fetcher.py` (428 LOC) on branch `maintenance/code-cleanup-phase5-perf-2026-06` (commit `198acbda`).  Public surface: `ParallelFetcher(client, cache, *, max_workers=16, default_ttl=600)` with single public method `populate(targets) -> dict[str, CachedManifest | FetchError]`.  Internals: `_fetch_one` worker (catches all exceptions defensively), `_dispatch_fetch_result` (maps `SimplePageResponse | FetchError` to dict value with cache side-effects), `_store_fresh` (cache.put + re-get for type consistency), `_refresh_not_modified` (option (a) — re-put with existing candidates to slide `expires_at` forward).
  - **304 / not-modified decision** (explicit per T9 brief): chose option (a) — re-`put` with existing `CachedManifest.candidates` + the response's etag (or the existing etag if the 304 omitted it) so `expires_at` slides forward by `default_ttl`.  Rationale: avoids the cliff where a server-confirmed-unchanged manifest re-expires immediately and forces another conditional round-trip on the next call.  T7's cache has no public "bump expires_at" method; calling `put` is the simplest correct API.  Documented in module docstring under "304 / not-modified handling".
  - **Stale-but-present etag decision** (explicit per T9 brief): phase 1 does **not** send `If-None-Match` for expired cache entries.  T7's `get()` returns `None` past TTL and deliberately does not expose a way to peek at the on-disk etag (the file is treated as opaque after expiry).  Adding a `peek_etag(...)` method to T7 is logged as a Phase-3 follow-up; the cost is one full re-fetch instead of one conditional GET per stale entry, which is acceptable given Phase 1's "no integration yet" scope.  Documented in module docstring under "Stale-but-present etag handling".
  - **`max_workers > 16` decision**: clamp + INFO log line (per T9 brief).  Caller passing 32 → effective `max_workers = 16`, log emitted at INFO level.
  - **Worker count optimisation**: `min(self._max_workers, len(to_fetch))` so we don't spin up idle threads when the work queue is shallow (3-target populate doesn't allocate 16 worker threads).
  - **Defensive exception handling at two layers**: (a) `_fetch_one` wraps `client.fetch` in a top-level `except BaseException` so a programming bug in the client (or a test mock that raises) cannot kill the worker; (b) the `as_completed` loop wraps `future.result()` in a top-level `except BaseException` so even if `_fetch_one` somehow leaks an exception, the result dict gets a `FetchError(kind="transient", ...)` entry rather than the `populate` call raising.
  - **Empty-targets fast path**: `populate([])` short-circuits to `{}` before allocating the executor (no thread-startup cost).
  - **RED→GREEN evidence**:
    - Pre-impl: `python -c "from pipenv.resolver.fetcher import ParallelFetcher"` raised `ModuleNotFoundError: No module named 'pipenv.resolver.fetcher'`.
    - Smoke tests (3 tests, deleted before commit per T9 brief): all PASS — fresh-targets path, warm-cache zero-fetch-call path, one-transient-others-complete path.
    - All 4 acceptance criteria inline-run with `python /tmp/t9_acceptance.py` (script deleted post-run):
      - **ACC1**: 5 fresh targets → all 5 entries are `CachedManifest`. PASS.
      - **ACC2**: same 5 again warm → 0 `client.fetch` calls (down from 5 on the cold pass). PASS.
      - **ACC3**: 1 of 5 returns `FetchError(kind="transient")` → 4 `CachedManifest` (`pkg0`, `pkg1`, `pkg3`, `pkg4`) + 1 `FetchError` (`pkg2`, kind="transient"). PASS.
      - **ACC4**: 32 targets / 16 workers / mock blocks on `release_event` → 16/32 dispatches recorded an entry timestamp before the first return timestamp.  This is the **maximum physically achievable** given `max_workers=16` (the executor cannot dispatch more than 16 concurrently before any returns).  The brief's stated floor is "≥14 of 32" which 16 clears comfortably.  The plan's "≥80 %" wording (≈26) is technically unachievable under a 16-worker ceiling — flagged for review (the 16-worker cap and "≥80 % of 32 dispatched concurrently" are mutually inconsistent at the workers-vs-targets ratio of 2 : 1; brief's "≥14" is the corrected floor and what the test asserts).
  - **Grep check**: `grep -n "pip\._internal" pipenv/resolver/fetcher.py` returns no matches (exit 1).  Zero patched-pip imports.
  - Tests deferred to T15 per the revised plan (no smoke file committed — smoke file existed transiently during development and was deleted before commit, as the T9 brief explicitly instructed).
- **files edited/created**:
  - Created: `pipenv/resolver/fetcher.py` (428 lines; module + class docstrings + comments document the 304 and stale-etag decisions inline so a future reader doesn't have to re-derive them from the plan log).

---

### T10: Phase-1 acceptance — parity check against pip's `Link.from_json` (fixture-only)

- **depends_on**: `[T4, T5]`
- **location**:
  - `tests/unit/test_pep691_parity_fixtures.py` (new)
  - Uses **fixtures from T2 only**.  No live PyPI.  Live-PyPI parity audit (the 20-real-package run) is explicitly deferred to Phase 3's parity matrix work (per design doc §7.4).
- **description**:
  For each PEP 691 JSON fixture in `tests/unit/fixtures/pep691/`, run our parser AND pip's parser path (loading the same JSON body through `pip._internal.models.link.Link.from_json` — this is the **only** test in Phase 1 that deliberately imports from `pip._internal`, justified because parity-against-pip is the whole point of the test).  Compare candidate sets field-by-field: `name`, `version`, `url`, `hashes`, `requires_python`, `yanked`.  Divergences must be documented in a `tests/unit/test_pep691_parity_known_diffs.md` companion file with justification.  Same drill for PEP 503 HTML fixtures vs pip's `Link.from_element`.
- **validation**:
  - `pytest tests/unit/test_pep691_parity_fixtures.py` passes offline.
  - Any divergence is justified in the `_known_diffs.md` file (not a silent skip).
  - The single deliberate `pip._internal` import is in the **test file only**, NOT in `pipenv/resolver/*` (the pre-commit gate added in T17 must scope to `pipenv/resolver/` to allow this exception).
- **status**: Completed
- **log**:
  - 2026-05-12: Landed parity test against pip's `Link.from_json` / `Link.from_element` on every fixture in the T2 set on branch `maintenance/code-cleanup-phase5-perf-2026-06`.  10 parametrised tests (6 JSON + 4 HTML) all pass; `pytest tests/unit/test_pep691_parity_fixtures.py -v` is full-green in 0.42 s offline.  Field-by-field comparison covers `filename` (set parity), `url` (vs pip's `url_without_fragment`), `hashes` (sorted tuples of `(algo.lower(), value)`), `requires_python` (with `link.requires_python or None` normalisation), `yanked` (vs pip's `is_yanked` property), and `yanked_reason` (with pip's `""` → `None` normalisation for the "yanked, no reason" case).
  - **Zero observable divergences** on the curated T2 fixture set at the `Candidate`-equivalent field level.  Largest fixture (`cryptography.json` at 3496 files, including 85 yanked entries of which 48 carry free-text reasons) round-trips with zero diffs on every field; ditto `cryptography.html` (3496 anchors).  `django` (781 files JSON+HTML), `tablib` (68 files), `six` (48 files JSON+HTML), `missing-hash` (2 files), `yanked-pkg` (4 files JSON+HTML) all clean.
  - **Representation-level diffs documented in `_known_diffs.md`** and normalised in the test (NOT silent-skipped): (1) pip stores `yanked_reason=""` for "yanked-no-reason" cases while we store `yanked=True + yanked_reason=None` — the test normalises `link.yanked_reason or None if link.is_yanked else None`; (2) `upload_time` is deliberately not compared because pip uses its internal `parse_iso_datetime` helper and we use stdlib `datetime.fromisoformat`, which may produce different `tzinfo` flavours for the same instant (T12 pins upload-time on our side directly against the fixture string); (3) hash algo-name case is lower-cased on both sides in the test for robustness (all current fixtures already use lower-case so it's a no-op today).  No semantic divergences — see the `_known_diffs.md` file for the full justification template and citations (PEP 503 §3, PEP 592 §3, PEP 691 §3).
  - **Production-tree gate verified**: `grep -rn "^[[:space:]]*\(from\|import\).*pip._internal" pipenv/resolver/` returns zero lines (only doc-strings / comments / a string-literal in the logger-name allow-list in `core.py` mention the path).  The deliberate `pip._internal` import lives only in the test file, marked with a `# parity-test-by-design` trailing comment for human-readable review.
  - **Gotchas pinned**: pip's `Link.from_element` signature is `(anchor_attribs, page_url, base_url)` (three positional args; PyPI doesn't emit a `<base>` element so `page_url == base_url` is correct for the T2 fixtures).  Pip's `Link._hashes` is a `dict[str, str]` (not a `Hashes` object) and is the right attribute to read for parity — `Link.as_hashes()` returns a `Hashes` wrapper that's harder to compare structurally.  Pip's `Link.is_yanked` is a property that returns `yanked_reason is not None` (so `""` counts as yanked); the test compares `bool(link.is_yanked)` to `c.yanked` defensively in case pip ever changes to a tri-state.
- **files edited/created**:
  - Created: `tests/unit/test_pep691_parity_fixtures.py` (267 lines; 10 parametrised tests across two test functions covering all six JSON fixtures and all four HTML fixtures; one deliberate `pip._internal` import marked with `parity-test-by-design` trailing comment).
  - Created: `tests/unit/test_pep691_parity_known_diffs.md` (170 lines; documents five representation-level diffs that are normalised in the test, plus a template for future additions and a "current divergences: NONE at the Candidate-output level" header).

---

### T11: Unit tests — `Candidate` dataclass

- **depends_on**: `[T1]`
- **location**:
  - `tests/unit/test_candidate.py` (new)
- **description**:
  Tests for:
  - Construction with all fields; `frozen=True` enforcement (mutation raises).
  - Equality and hashability (two `Candidate`s with same field values are `==` and hash to the same value).
  - `Candidate.from_filename(...)` for wheel filenames → `is_wheel=True`, `wheel_tags` populated; sdist filenames → `is_wheel=False`, `wheel_tags=None`.
  - `Hash` namedtuple equality and frozenset membership.
- **validation**: `pytest tests/unit/test_candidate.py` passes with ≥95 % coverage of `pipenv/resolver/candidate.py`.
- **status**: Completed
- **log**:
  - 2026-05-12: Extended T1's 8 smoke tests by 23 new tests grouped into 7 new test classes (`TestAllFieldsConstruction`, `TestEqualitySemantics`, `TestHashabilityForSetDictUse`, `TestHashTupleEquality`, `TestFromFilenameWheelTagVariants`, `TestFromFilenameSdist`, `TestEdgeCases`).  All 31 tests pass.  Coverage of `pipenv/resolver/candidate.py` is **100 %** (36 / 36 statements, 0 missed) — exceeds the ≥95 % floor.  The previously uncovered line (the `ValueError` raised on malformed wheel filenames at module line 169) is now pinned by `TestEdgeCases::test_malformed_wheel_filename_raises_value_error`.  Wheel-tag platform-variant coverage is exhaustive: legacy manylinux2014, PEP 600 `manylinux_X_Y`, musllinux, macosx arm64, win_amd64, pure-Python `py3-none-any`, stable-ABI `cp311-abi3`, and non-manylinux `linux_x86_64` each have their own test.  Sdist branch covered for `.tar.gz` and `.zip`.  Equality + hashability pinned for set / dict use.  `Hash` ↔ plain-tuple equality pinned for O(1) frozenset intersection with Pipfile-pinned hash lists.  Malformed-wheel contract pinned to **propagate `ValueError`** (option (a) — explicit failure over silent tag-less wheel), matching T1's implementation choice.  Zero `pip._internal.*` imports; all `packaging` references go through `pipenv.vendor.packaging.tags`.  T1's 8 smoke tests preserved unchanged.
- **files edited/created**:
  - Edited: `tests/unit/test_candidate.py` (8 → 31 tests; +23 tests, +318 lines; T1's smoke tests preserved verbatim).

---

### T12: Unit tests — PEP 691 + PEP 503 parsers

- **depends_on**: `[T4, T5, T2]`
- **location**:
  - `tests/unit/test_pep691_parser.py` (new)
- **description**:
  Per design §5.1's test list:
  - Synthetic PEP 691 JSON → expected `Candidate` set (use T2 fixtures).
  - Synthetic PEP 503 HTML → same expected `Candidate` set as the JSON variant of the same package.
  - Yanked candidate: `yanked=True`, reason preserved (both formats).
  - Wheel tags parsed correctly for `manylinux2014_x86_64`, `manylinux_2_17_x86_64`, `musllinux_1_2_x86_64`, `macosx_11_0_arm64`, `win_amd64`, `py3-none-any`, `cp311-abi3-...`.
  - Hash extraction from both JSON `"hashes": {"sha256": "..."}` and HTML `#sha256=...` fragment.
  - Relative URL → absolute (`url: "../../packages/foo.whl"` against `page_url="https://pypi.org/simple/foo/"`).
- **validation**: `pytest tests/unit/test_pep691_parser.py` passes with ≥95 % coverage of `pipenv/resolver/pep691.py`'s parser functions.
- **status**: Completed
- **log**:
  - 2026-05-12: Wrote `tests/unit/test_pep691_parser.py` (145 tests, 1284 LOC) on branch `maintenance/code-cleanup-phase5-perf-2026-06` (commit `04afa70e`). 145/145 GREEN on first run.
  - **Parser-surface coverage: 100%** (189/189 statements in lines 1–631 of `pipenv/resolver/pep691.py` — every helper + both top-level parsers).  Total file coverage reads 70% (289 stmts, 86 missed) only because the PEP691Client class (lines 667–1026, ~100 statements) lives in the same module; that surface is **T13's territory** and was deliberately left untouched.
  - Test categories landed (16 test classes, organised by surface):
    - **JSON fixture round-trips** (`TestPep691JsonFixtures`): six=48, django=781, cryptography=3496, tablib=68, yanked-pkg=4, missing-hash=2; wheel-vs-sdist branch on `six.json`; canonical-name preserved.
    - **JSON synthetic edge cases** (`TestPep691JsonSynthetic`): api-version 1.0/2.0/non-string/missing; top-level array/string/missing-name/empty-name/non-string-name/missing-files/files-not-list/non-dict-entries; malformed filename / malformed wheel / missing filename / missing url / empty filename / empty url / non-string filename skipped; invalid JSON body raises (T8's transient-error contract); relative-URL `"../../files/foo.whl"` against `/simple/foo/` → `/files/foo.whl`.
    - **JSON yanked variants** (`TestPep691JsonYanked`): all four `yanked-pkg.json` branches + the JSON-side empty-string contract (NOT yanked, opposite of HTML).
    - **JSON hashes + requires-python** (`TestPep691JsonHashesAndRequiresPython`): `missing-hash.json` fixture; missing `hashes` field; `requires-python` empty-string + non-string normalised to None.
    - **JSON upload-time** (`TestPep691JsonUploadTime`): fixture round-trip + missing field → None.
    - **HTML fixture round-trips** (`TestPep503HtmlFixtures`): six/django/cryptography/yanked-pkg counts match the JSON side.
    - **HTML synthetic edge cases** (`TestPep503HtmlSynthetic`): `#sha256=ABC` fragment, algo-lowercasing, `data-requires-python="&gt;=3.10"` HTML-unescaped, `../../files/` relative URL, anchor-without-href skipped, empty-text falls back to href basename, anchor-text wins over href-basename when present, PEP 658 `data-core-metadata` / `data-dist-info-metadata` IGNORED, unparseable page URL → empty tuple, garbage body → empty tuple (lenient), UTF-8 mojibake recovered.
    - **HTML yanked variants** (`TestPep503HtmlYanked`): absent / empty / with-reason / HTML-entity-unescaping; full `yanked-pkg.html` fixture matrix.
    - **Cross-format parity** (`TestCrossFormatParity`): filename sets equal for all four fixture pairs; hashes-by-filename equal for the synthetic `yanked-pkg` pair.
    - **Helper-level direct tests** (10 classes): `_extract_version` (canonical match, sdist, Django case variant, `python_dateutil`↔`python-dateutil`, unparseable, unknown suffix, prefix mismatch, empty remainder, build-tag wheel, `.tgz`/`.zip`/`.tar.bz2`); `_strip_archive_suffix` (5 suffixes + unknown + no-suffix passthrough); `_normalize_yanked` (8 branches incl. empty-string→False); `_normalize_yanked_html` (4 branches incl. HTML entity unescape); `_normalize_hashes` (empty/non-dict/single/multi/uppercase-algo/non-string-keys-or-values filtered); `_parse_upload_time` (None / "" / non-string / full-microseconds-Z / Z-no-microseconds / no-Z / garbage / malformed-with-Z); `_split_href_hash` (no fragment / fragment-without-`=` / normal / empty-algo / empty-value / algo-lowercased / empty-fragment); `_package_name_from_page_url` (trailing slash, no trailing slash, `Django`→`django` canonicalisation, `Python_Dateutil`→`python-dateutil`, no path segments, empty URL); `_AnchorCollector` (attrs + text, bare-attribute-normalised-to-empty-string, non-anchor tags ignored, chunked text concatenated, handle_data outside anchor ignored); `_build_candidate` / `_build_candidate_from_html` (success / non-string-filename / non-string-url / unparseable-version / no-href / empty-href / non-string-href / empty-text-falls-back / empty-text-and-no-basename / unparseable-filename / malformed-wheel).
  - **Contracts pinned that weren't fully specified upstream**:
    - JSON parser on a non-decodable body **raises** `json.JSONDecodeError` (per T4 docstring: "the caller needs that signal to map a 200 with a malformed body to a `FetchError(\"transient\", ...)`").  Pinned by `test_invalid_json_body_raises`.
    - JSON parser on a top-level array/string returns an empty tuple (not a raise).
    - JSON `meta.api-version` of non-string type (e.g. float `1.0`) is silently tolerated — falls through the `isinstance(api_version, str)` guard and best-effort parses.
    - HTML parser is **lenient on bad bodies** — non-HTML garbage yields an empty tuple, never raises (HTMLParser is lenient by design).
    - HTML parser's UTF-8 fallback uses `errors="replace"`; mojibake recovered.
    - `_build_candidate_from_html` with both empty anchor text **and** an href whose basename is empty (e.g. `"https://x/"`) returns None.
    - `_AnchorCollector`'s bare attribute (`data-yanked` with no `=`) is normalised to empty-string in the collected dict, **distinguishable** from "attribute absent" — this is what lets `_normalize_yanked_html` differentiate the two cases per T5's intentional divergence from JSON.
  - Coverage command: `python -m pytest tests/unit/test_pep691_parser.py --cov=pipenv.resolver.pep691 --cov-report=term-missing -q --override-ini="addopts=-ra"`.  The `--override-ini` is required because `pyproject.toml`'s default `addopts = "-ra --no-cov"` silently disables coverage (already flagged by T11 + T16 agents).
- **files edited/created**:
  - Created: `tests/unit/test_pep691_parser.py` (1284 LOC, 145 tests, 16 test classes).

---

### T13: Unit tests — `PEP691Client`

- **depends_on**: `[T8]`
- **location**:
  - `tests/unit/test_pep691_client.py` (new)
- **description**:
  Per design §5.1:
  - Mocked `urllib3.PoolManager.request` returns canned responses for 200 JSON, 200 HTML, 304, 404, 401, 500, network error.  Verify `SimplePageResponse` / `FetchError` shape for each.
  - `if_none_match="<etag>"` → outgoing request has `If-None-Match` header.
  - URL with embedded credentials → outgoing request has `Authorization: Basic <b64>`, URL has creds stripped.
  - netrc-supplied creds (tmp_path netrc fixture) → outgoing request has `Authorization`.
  - `PIP_CLIENT_CERT` env set → client passes `cert=` through (mock `PoolManager` records it).
- **validation**: `pytest tests/unit/test_pep691_client.py` passes with ≥90 % coverage of the `PEP691Client` class.
- **status**: Completed
- **log**:
  - 2026-05-12: Created `tests/unit/test_pep691_client.py` (67 tests, all passing) on branch `maintenance/code-cleanup-phase5-perf-2026-06`.  Suite is organised into 11 test classes mirroring the T8 contract surface: `TestStatusDispatch200JSON` (charset suffix, uppercase CT, no ETag/Last-Modified, full six.json round-trip → 48 candidates), `TestStatusDispatch200HTML` (`text/html` + `application/vnd.pypi.simple.v1+html`), `TestStatusDispatch200UnknownContentType` (`application/json` mis-served, missing CT header, empty CT, garbage-JSON body → transient with `malformed JSON` message, `None` body normalised to empty bytes), `TestStatusDispatchNon200` (304 carries caller ETag forward, 304 without `if_none_match` → etag=None, 404 → missing, 401/403 → auth, parametrised 400/408/410/418/429/500/502/503/504 → transient), `TestNetworkExceptions` (MaxRetryError, ProtocolError, TimeoutError, SSLError, raw OSError — all → `kind="transient"`, `original=exc`), `TestAuthDispatch` (URL-embedded creds stripped + b64-encoded, `%40`/`%23` URL-decoded before b64, netrc fallback via monkeypatched `lookup_netrc_auth`, no-auth-no-netrc → no Authorization header, URL creds beat netrc, explicit `netrc_path` threaded through), `TestConstructor` (cert kwarg stored, `$PIP_CLIENT_CERT` fallback at construction, env unset → None, verify defaults True, verify=False stored, session stored), `TestOutgoingHeaders` (Accept exact match, Cache-Control absence pinned both cases, If-None-Match absent/present, urllib3.Timeout connect=10/read=30 threaded), `TestCanonicalNameAndURLComposition` (parametrised `Django`/`python_dateutil`/`Flask.ext.SQLAlchemy`/`SIX` → canonical PEP 503 segments, trailing-slash idempotent, no-trailing-slash idempotent, nexus custom-host preserved, GET method asserted), `TestReleaseConn` (release_conn invoked on success + 404, release_conn raising swallowed, missing release_conn attribute tolerated via `getattr` default), `TestHeaderGetterFallback` + `TestAuthHelperEdgeCases` (lowercase plain-dict key → case-folded scan, headers=None safe, headers.get() raises → items() fallback succeeds, both raise → returns None, .get None + items match, .get None + items no match, urllib3 `LocationParseError` during host extraction → no Authorization, parsed url with empty host → netrc lookup not called).
  - **Coverage (T13-only, in isolation)**: `pep691.py` reports 85% (289 statements, 42 missed) — every missed line is inside the **parser surface** (helper lines 87-612) which is T12's territory; **zero `PEP691Client`-layer lines are missing** (`_basic_auth_header`, `_get_header`, `__init__`, `fetch`, `_build_auth_header`, `_dispatch_response` all 100% covered).
  - **Coverage (T12 + T13 combined)**: `pep691.py` reports **100% (289/289 statements covered, 0 missed)** — comfortably exceeds the plan's ≥95% combined-coverage target.  Run: `python -m pytest tests/unit/test_pep691_client.py tests/unit/test_pep691_parser.py --cov=pipenv.resolver.pep691 --cov-report=term-missing -q --override-ini="addopts=-ra"` → 212 passed.
  - **T8 contract pinning** (where the brief left detail to T13): (a) 304 carries the **caller's** `if_none_match` as `etag` even when the server's response includes a different `ETag` header — pinned with `_make_response(status=304, headers={"ETag": '"server-side-value"'}) + if_none_match='"abc"'` → `result.etag == '"abc"'`.  Reading T8's source comment at line 950 confirmed this is deliberate per RFC 7232 §4.1.  (b) Missing **and** empty `Content-Type` on a 200 both map to `FetchError("transient", "unexpected content-type: ''")` — pinned `test_200_missing_content_type_header` + `test_200_empty_content_type`.  (c) `Cache-Control` absent both in titlecase AND lowercase form — pinned to guard against an accidental future patch adding `cache-control: no-cache`.  (d) URL-embedded creds beat netrc creds — pinned `test_url_creds_win_over_netrc` so a future refactor cannot silently flip the precedence.  (e) `release_conn` missing **and** `release_conn` raising — both pinned.  (f) `urllib3.util.parse_url` raising `LocationParseError` during netrc-host extraction is caught and treated as "no auth" — pinned `test_unparseable_host_yields_no_auth_header` by monkeypatching `urllib3.util.parse_url`.  (g) `parse_url().host` being empty short-circuits before reaching `lookup_netrc_auth` — pinned via `MagicMock` sentinel asserting `lookup_netrc_auth.assert_not_called()`.
  - **Mocking style**: pure `MagicMock` sessions per the T13 brief; no `urllib3.PoolManager` integration.  `_make_response` + `_make_session` helpers keep per-test setup to 3 lines.  Headers passed as plain `dict` (exercises T8's case-folded fallback path in `_get_header`); the case-folded scan + the `HTTPHeaderDict.get` happy-path are both covered.
  - **Plan deviation**: T13 brief mentions a `tmp_path` netrc fixture; chose to monkeypatch `lookup_netrc_auth` directly instead.  Rationale: T16 (auth helper tests) owns netrc-fixture round-trips; here we're testing the **client's** dispatch into the helper, not the helper itself.  Mocking the boundary keeps T13 focused on the client's contract surface and avoids duplicating T16's fixture machinery.  T6's `lookup_netrc_auth` is already exercised end-to-end by T16 against a real netrc file.
  - **Zero modification to `pipenv/resolver/pep691.py`** per task instruction.
- **files edited/created**:
  - Created: `tests/unit/test_pep691_client.py` (67 tests across 11 classes, 553 LOC including docstrings).

---

### T14: Unit tests — `ParsedManifestCache`

- **depends_on**: `[T7]`
- **location**:
  - `tests/unit/test_manifest_cache.py` (new)
- **description**:
  Per design §5.2:
  - TTL: fresh write → `get` returns within TTL; after TTL expires `get` returns `None`.
  - etag round-trip: put with etag, get returns same etag.
  - Atomic write under failure: monkeypatch `os.replace` to raise → previous manifest unchanged; no `<file>.tmp.*` litter (cleaned up).
  - Schema versioning: write with `schema_version=1`, then load with `schema_version=2` → returns `None` (mismatch ignored).
  - Concurrent **reader-vs-writer**: two threads, one writes, one reads → reader gets either old-or-new content, never partial.
  - Concurrent **writer-vs-writer** (NEW per review item 10): two threads write to the same `(index_url, package_name)` with different candidates → one of the two payloads is the final state (last-write-wins), no corruption, no partial file, no `<file>.tmp.*` survivors.
  - `clear_all()` removes the whole cache root.
  - URL hashing collision: two different index URLs that happen to share a sha256 prefix still write to distinct paths (full sha256 used).
- **validation**: `pytest tests/unit/test_manifest_cache.py` passes with ≥95 % coverage of `pipenv/resolver/manifest_cache.py` (coverage enforced by the `--cov-fail-under` config added in T17).
- **status**: Completed
- **log**:
  - 2026-05-12: Implemented `tests/unit/test_manifest_cache.py` on branch `maintenance/code-cleanup-phase5-perf-2026-06`. 47 tests across 9 classes — TestRoundTripBasicAPI (8), TestTTLExpiry (4), TestSchemaVersioning (2), TestAtomicWrite (3), TestConcurrentReaderVsWriter (1), TestConcurrentWriterVsWriter (1), TestURLHashing (3), TestCorruptionRobustness (12), TestCandidateSerialization (4), TestSerialisationHelpers (8). Coverage: `pipenv/resolver/manifest_cache.py` 112 stmts, 0 miss, 100% (target was ≥95%). T7 contracts pinned in tests: (a) raw-byte writes for corruption tests (per T7 hand-off — `target.write_bytes(b"not json")` rather than mutating via JSON round-trip), (b) writer-vs-writer asserts "either payload acceptable" (last-os.replace-wins, ordering unobservable), (c) atomic-write failure path tested for both single-failure (os.replace raises, previous payload intact) and double-failure (os.replace + cleanup os.unlink both raise → original OSError still propagates, exercising the `except OSError: pass` in put's cleanup), (d) no .tmp litter checked only on the success path (100 puts → 0 .tmp files), (e) the cached-wheel-tags-wins-over-filename branch in `_candidate_from_json` is pinned with an injected unrelated tag-string so a future `packaging.tags` upgrade can't silently change behaviour, (f) `etag` field with wrong type sanitises to None rather than misses (separate branch in get() — `etag is not None and not isinstance(etag, str)`). Clock-advancement TTL test patches `datetime` at the module level (subclass with overridden classmethod `now`). Concurrent tests use `threading.Barrier(2)` for writer-vs-writer synchronisation; stress-tested 5x with no flakiness. Test invocation: `python -m pytest tests/unit/test_manifest_cache.py --cov=pipenv.resolver.manifest_cache --cov-report=term-missing -q --override-ini="addopts=-ra"`. Did NOT modify `pipenv/resolver/manifest_cache.py` (instructed not to).
- **files edited/created**:
  - Created: `tests/unit/test_manifest_cache.py` (47 tests, 100% coverage of `pipenv/resolver/manifest_cache.py`).

---

### T15: Unit tests — `ParallelFetcher`

- **depends_on**: `[T9]`
- **location**:
  - `tests/unit/test_parallel_fetcher.py` (new)
- **description**:
  - 5 fresh targets, mock client → all 5 dispatched to `client.fetch`; all return `CachedManifest`.
  - Same 5 targets a second time (cache warm) → zero `client.fetch` calls.
  - 5 targets, 3 within TTL, 2 expired → 2 `client.fetch` calls (with `if_none_match` for those 2).
  - Mixed outcomes: 3 fresh, 1 missing, 1 transient → return dict has 3 `CachedManifest`, 1 `FetchError(kind="missing")`, 1 `FetchError(kind="transient")`; pool didn't deadlock or hang on the failing tasks.
  - **Parallelism via dispatch-order instrumentation** (replaces flaky wall-time assertion): the mock client records each dispatch timestamp + an `arrive_event` and only returns after `release_event` is set; assert at least 14 of 32 dispatches arrived before any returned (proves the executor is dispatching concurrently up to `max_workers`).
  - One target's worker raises an unexpected exception → other targets still complete; the offender becomes `FetchError(kind="transient", original=<exc>)`.
- **validation**: `pytest tests/unit/test_parallel_fetcher.py` passes with ≥90 % coverage of `pipenv/resolver/fetcher.py` (enforced by T17's coverage config).
- **status**: Completed
- **log**:
  - 2026-05-12: Implemented `tests/unit/test_parallel_fetcher.py` on branch
    `maintenance/code-cleanup-phase5-perf-2026-06`.  38 tests across 8 test
    classes covering the full surface of `pipenv/resolver/fetcher.py`:
    - `TestConstructor` (7): defaults (16 workers, 600s TTL); custom workers
      below ceiling; clamp >16 with INFO log; ==16 no log; 0 → ValueError;
      negative → ValueError; custom default_ttl.
    - `TestBasicDispatch` (4): empty targets → `{}` no executor; 5 cold →
      5 dispatches; 5 warm → 0 dispatches; mixed 3-warm-2-cold → 2 dispatches
      via call_args inspection (verifies `to_fetch` partitioning).
    - `TestStatusBranches` (8): fresh+etag round-trip; fresh+None-etag;
      missing → FetchError(missing) + no cache write; not-modified with
      prior cache (option-a TTL refresh); not-modified with response.etag
      override (response.etag wins); not-modified without prior → transient;
      unknown status (defensive Literal branch); FetchError pass-through.
    - `TestMixedOutcomes` (4): 3-fresh+1-missing+1-transient end-to-end;
      worker `RuntimeError` isolated via `_fetch_one` defensive catch;
      `KeyboardInterrupt` (BaseException) also caught; duplicate package_name
      across indexes → one key in result (last-completer wins, both fetches
      dispatched).
    - `TestCacheIntegration` (5): get-call-count = 6 (3 step-1 + 3 post-put);
      put receives ttl_seconds=default_ttl; put OSError → transient
      (`_store_fresh` branch); not-modified put OSError → soft-fall-back to
      existing; not-modified post-put-get-None → soft-fall-back to existing;
      ttl_seconds=0 → in-process `CachedManifest` synthesised (real cache
      end-to-end).
    - `TestConcurrency` (2): 32 targets at max_workers=16 → ≥14 concurrent
      dispatches via `threading.Event` instrumentation (NO wall-time
      assertions); shallow queue (2 targets, max_workers=8) → workers
      bounded by len(to_fetch).
    - `TestEdgeCases` (6): empty-string index_url passes through;
      duplicate (index, name) tuple → 1 key + 2 dispatches; non-string
      package_name → TypeError → FetchError(transient, original=<TypeError>);
      `_fetch_one` returns FetchError verbatim; `_fetch_one` threads
      `if_none_match` through to client; all-warm → no executor spawned.
    - `TestFutureExceptionPath` (1): defensive outer `except BaseException`
      in populate() — patch ThreadPoolExecutor.submit so future.result()
      raises after the worker has already returned; populate() catches and
      records a transient.
    - Coverage:
      `python -m pytest tests/unit/test_parallel_fetcher.py --cov=pipenv.resolver.fetcher --cov-report=term-missing --override-ini="addopts=-ra"`
      → `pipenv/resolver/fetcher.py 93 stmts 0 miss 100%`.  Exceeds the
      ≥90% floor; nothing left uncovered.
    - Concurrency tests verified stable across 8 consecutive runs (no
      flakiness from the dispatch-order assertion).
    - T9 contracts pinned (where the brief left wiggle room):
      1. **Duplicate (index_url, package_name)**: NOT deduplicated; both
         are appended to `to_fetch` and both dispatch.  Result dict keyed
         by `package_name` so the second-completer wins (test asserts
         exactly one key + 2 fetch calls).
      2. **Empty-string `index_url`**: passes through unchanged — T9 does
         not validate it; the client mock receives `""` verbatim.
      3. **`not-modified` `response.etag` semantics**: when the 304 carries
         an explicit ETag header it OVERRIDES the cached etag; when
         `response.etag is None` the cached etag is retained.  (Pinned in
         `test_not_modified_with_response_etag_overrides`.)
      4. **`BaseException` catching**: `_fetch_one` catches BaseException
         (not just Exception) — verified with `KeyboardInterrupt`.
- **files edited/created**:
  - Created: `tests/unit/test_parallel_fetcher.py` (38 tests, 100% coverage
    of `pipenv/resolver/fetcher.py`).

---

### T16: Unit tests — Auth helpers

- **depends_on**: `[T6]`
- **location**:
  - `tests/unit/test_resolver_auth.py` (new)
- **description**:
  Already specified in T6's deliverable.  Listed as a separate task so it can run in parallel with T11–T15.
- **validation**: `pytest tests/unit/test_resolver_auth.py` passes with ≥95 % coverage of `pipenv/resolver/auth.py`.
- **status**: Completed
- **log**:
  - 2026-05-12: Implemented `tests/unit/test_resolver_auth.py` on branch
    `maintenance/code-cleanup-phase5-perf-2026-06`.  28 tests covering the
    three T6 helpers; 27 run on Linux (1 Windows-only `_netrc` test is
    `skipif`-gated per pipenv convention).
    - `extract_url_credentials` (9 tests): no-creds passthrough; plain
      `user:pass`; URL-encoded creds decoded (`%40`→`@`, `%23`→`#`);
      empty-URL defensive (`("", None)`); http/ftp schemes preserved;
      query+fragment preserved; port preserved; username-only-no-colon
      yields `("user", "")`.
    - `lookup_netrc_auth` (15 tests, 14 run on Linux): missing-file; empty
      file; matching host; non-matching host; malformed netrc swallowed
      (no raise); `OSError` from `netrc.netrc(...)` swallowed via
      `monkeypatch`; empty-login skipped; `None`-password skipped (via
      monkeypatched fake `netrc.netrc`); `$NETRC` overrides `~/.netrc`;
      explicit `netrc_path` arg overrides `$NETRC`; empty `host=""`
      short-circuits; falls back to `~/.netrc` when no `$NETRC`; skips
      missing explicit path then uses `$NETRC`; `expanduser("~")` returns
      `"~"` branch (no-home); Windows-only `_netrc` filename
      (`skipif(os.name != "nt")`).
    - `client_cert_from_env` (4 tests): unset → `None`; empty → `None`;
      single path duplicated → `(value, value)`; whitespace-only
      `"  "` → returned verbatim as `("  ", "  ")` (pinning T6's choice
      that whitespace is NOT stripped — the plan's open question;
      documented in the test docstring so a future strip change can't
      drift silently).
    - All tests use `tmp_path` + `monkeypatch`; the user's real `~/.netrc`
      is never read (verified by always redirecting `$HOME`/`$USERPROFILE`
      into `tmp_path` for cases where `~` fallback could activate).
    - Coverage:
      `python -m pytest tests/unit/test_resolver_auth.py --override-ini="addopts=-ra" --cov=pipenv.resolver.auth --cov-report=term-missing`
      → `pipenv/resolver/auth.py 53 stmts 2 miss 96% Missing: 84-85`.
      The 2 missing lines are the Windows-only `_netrc` candidate branch
      in `_netrc_candidate_paths`, which the `skipif(os.name != "nt")`
      test correctly exercises on Windows only.  ≥95 % target met on
      Linux; will be 100 % on the Windows CI lane.
    - Zero `pip._internal` imports in the test file (per Initiative G
      gate).
- **files edited/created**:
  - Created: `tests/unit/test_resolver_auth.py` (28 tests; 27 run on
    Linux, 1 Windows-only).

---

### T17: Phase-1 ship-tasks: exports, `--clear` wiring, coverage gate, pre-commit gate, news, doc

- **depends_on**: `[T1, T7, T8, T9]`
- **location**:
  - `pipenv/resolver/__init__.py` (add explicit `__all__` re-exporting `Candidate`, `Hash`, `PEP691Client`, `ParsedManifestCache`, `ParallelFetcher`, `SimplePageResponse`, `FetchError`)
  - `pipenv/cli/command.py` (`--clear` handler — add a call to `ParsedManifestCache(...).clear_all()` alongside the existing pip-cache clear)
  - `pyproject.toml` (pytest-cov config: add `[tool.coverage.run] source = ["pipenv/resolver"]` and a `--cov-fail-under=90` invocation in the CI step for the new modules)
  - `.pre-commit-config.yaml` (new local hook: `grep -RnE "^from pipenv\.patched\.pip\._internal|^import pipenv\.patched\.pip\._internal" pipenv/resolver/ && exit 1 || exit 0` — gates the no-`pip._internal` invariant against future drift)
  - `news/initiative-g-phase1-pep691-client.feature.rst` (new)
  - `docs/dev/initiative-g-pure-python-design.md` (mark §11 Phase 1 acceptance bullets as `[x]` complete; add "shipped at <commit>" annotation)
- **description**:
  Bundle the five wrap-up tasks for Phase 1 sign-off:
  1. **Module exports** — `pipenv/resolver/__init__.py` exposes the public Phase-1 surface so callers do `from pipenv.resolver import PEP691Client` rather than reaching into submodules.
  2. **`--clear` wiring** — when the user passes `pipenv lock --clear` (or `pipenv install --clear`), invalidate our parsed-manifest cache in addition to pip's HTTP cache.  Otherwise our cache becomes a poisoning surface a user can't easily nuke.
  3. **Coverage enforcement** — add a pytest-cov config that fails the test step if coverage of `pipenv/resolver/` drops below the per-module floors declared in T11–T15.  Without this, the coverage claims in those tasks are aspirational.
  4. **Pre-commit `pip._internal` gate** — a local hook that fails commit if any file under `pipenv/resolver/` imports from `pipenv.patched.pip._internal`.  The acceptance criterion at the top of this plan is otherwise enforced one-shot at merge time and silently drifts.  Scope is `pipenv/resolver/` only; T10's deliberate-by-design pip-internal import in `tests/` is exempted by the path filter.
  5. **News fragment + design-doc update** — per repo convention.
- **validation**:
  - `python -c "from pipenv.resolver import PEP691Client, ParsedManifestCache, ParallelFetcher, Candidate; print('ok')"` prints `ok`.
  - `pipenv lock --clear` removes both `~/.cache/pip/http-v2/` AND the parsed-manifest cache root.
  - CI test step with one new module dropped to 50 % coverage **fails** the build (verifies `--cov-fail-under` is wired).
  - Pre-commit hook fails when a forbidden import is introduced (verify with a deliberate failing diff in CI).
  - `python -m towncrier --version` succeeds; the fragment file is detected.
  - `git log --oneline -1 docs/dev/initiative-g-pure-python-design.md` shows the status-update commit.
- **status**: Completed
- **log**:
  - 2026-05-12: Implemented on branch `maintenance/code-cleanup-phase5-perf-2026-06`.
    All six sub-deliverables landed in a single commit:
    1. **Module exports** (`pipenv/resolver/__init__.py`): added Phase-1
       re-exports (`Candidate`, `Hash`, `PEP691Client`, `ParsedManifestCache`,
       `ParallelFetcher`, `SimplePageResponse`, `FetchError`, `CachedManifest`)
       while preserving the pre-existing Initiative F surface (`_main`,
       `main`, `resolve_packages`, `which`).  Plan said edit
       `pipenv/cli/command.py` for the `--clear` wiring, but the cleanest
       hook is in `pipenv/routines/lock.py` (top of `do_lock`) — that
       way `pipenv install --clear` (which reaches `do_lock` via
       `do_init`) is covered without duplicating the call site.
    2. **`--clear` wiring**: the `_clear_parsed_manifest_cache(project)`
       helper + its top-of-`do_lock` invocation in
       `pipenv/routines/lock.py` were landed by the parallel T19
       agent (commit `f29b87be`) under the T17 banner since both
       tasks edited the same hot section.  T17's own contribution
       here is plumbing `clear=state.clear` through `cmd_install`'s
       `RoutineContext.from_cli` call — that parameter was missing
       before, so `pipenv install --clear` never reached the
       resolver's clear path at all (`pipenv lock --clear` already
       worked).  E2E verified: real `ParsedManifestCache.put(...)`
       followed by `_clear_parsed_manifest_cache(project)` removes
       `<cache>/manifests-v1/` and is idempotent on missing dirs.
    3. **pytest-cov config**: added `[tool.coverage.run]` `source` +
       `branch=false` and `[tool.coverage.report]` `fail_under=90` /
       `show_missing=true` / `exclude_lines` to `pyproject.toml`.  The
       existing `addopts = "-ra --no-cov"` is preserved (it's
       deliberately fast for the full suite); coverage is gated via
       a dedicated CI job (`resolver-module-coverage`) that
       overrides `addopts` and runs the six T11–T16 test files with
       `--cov-fail-under=90`.  Local verification: full suite at
       99.67 % line coverage on the six new modules — well above the
       90 % floor.
    4. **Pre-commit gate**: added `no-pip-internal-in-resolver` local
       hook to `.pre-commit-config.yaml`.  Anchored to actual import
       statements (`^[[:space:]]*(from|import)[[:space:]]+...`) per
       T1's gotcha so docstring/comment/literal mentions don't
       false-positive.  Scoped to `^pipenv/resolver/` so T10's
       deliberate parity import under `tests/` is exempt.  Verified
       against a deliberate failing file (`exit 1`) and the current
       clean tree (`exit 0`).
    5. **News fragment** (`news/initiative-g-phase1-pep691-client.feature.rst`):
       summarises the Phase-1 surface + the `--clear` invalidation
       behaviour.  `python -m towncrier build --draft` renders it.
    6. **Design-doc update**: changed top-of-doc Status line to
       "Phase 1 shipped; phases 2-4 awaiting maintainer sign-off."
       Converted §11 Phase 1 acceptance bullets to a `[x]` checklist
       with a "Shipped at T17" annotation; added two more bullets
       covering the `--clear` wiring and the CI/pre-commit gates
       (which were missing from the original list but are
       acceptance criteria per T17's plan entry).  Phase 2 / 3
       bullets unchanged.
- **files edited/created**:
  - edited: `pipenv/resolver/__init__.py` (Phase-1 re-exports + extended `__all__`)
  - edited: `pipenv/cli/command.py` (plumb `clear=state.clear` through `cmd_install`'s `RoutineContext.from_cli`)
  - (already landed by the parallel T19 agent at commit
    `f29b87be`: `pipenv/routines/lock.py` `_clear_parsed_manifest_cache`
    helper + top-of-`do_lock` invocation)
  - edited: `pyproject.toml` (`[tool.coverage.run]` source + `[tool.coverage.report]` `fail_under=90`)
  - edited: `.pre-commit-config.yaml` (new `no-pip-internal-in-resolver` local hook scoped to `^pipenv/resolver/`)
  - edited: `.github/workflows/ci.yaml` (new `resolver-module-coverage` job invoking the six T11–T16 suites with `--cov-fail-under=90`)
  - created: `news/initiative-g-phase1-pep691-client.feature.rst`
  - edited: `docs/dev/initiative-g-pure-python-design.md` (top-of-doc Status line + §11 Phase 1 acceptance checklist)

---

### T18: Add `[pipenv] prefetch_index_manifests` setting

- **depends_on**: `[]`
- **location**:
  - `pipenv/utils/settings.py` (add the setting definition + docstring)
  - `pipenv/environments.py` (env-var passthrough `PIPENV_PREFETCH_INDEX_MANIFESTS`)
  - `tests/unit/test_settings.py` (new test that the default is `False`)
- **description**:
  Boolean setting, default `False`.  Pipfile readable as `[pipenv] prefetch_index_manifests = true`; env-var override via `PIPENV_PREFETCH_INDEX_MANIFESTS=1` matching pipenv's existing convention.  Document in the setting's docstring: "When True, parallel-pre-fetch the simple-API index pages for top-level packages before invoking pip's resolver.  Most beneficial on cold caches or slow networks (typical CI runs); may be net-neutral or slightly slower on warm-cache dev machines.  Default: False until benchmark data justifies enabling globally."
- **validation**:
  - `project.settings.get("prefetch_index_manifests", False)` returns `False` by default.
  - Setting `[pipenv] prefetch_index_manifests = true` in a Pipfile + reloading the project → returns `True`.
  - `PIPENV_PREFETCH_INDEX_MANIFESTS=1 python -c "from pipenv.project import Project; print(Project().settings.get('prefetch_index_manifests'))"` prints `True`.
- **status**: Completed
- **log**:
  - 2026-05-12: Implemented on branch `maintenance/code-cleanup-phase5-perf-2026-06`.
    - Added a small `_ENV_OVERRIDE_KEYS` registry + `_env_override` helper to
      `pipenv/utils/settings.py`; wired `Settings.get` / `Settings.__getitem__`
      / `Settings.__contains__` to consult it.  Env-var override wins over
      both Pipfile and caller-supplied default (matches pipenv's existing
      `PIPENV_<KEY>` precedence convention used elsewhere).
    - Added `PIPENV_PREFETCH_INDEX_MANIFESTS` to `pipenv/environments.py`
      via the existing `get_from_env(...)` helper, with the verbatim
      docstring from the plan.  `check_for_negation=False` is deliberate
      so the boolean read flows through a single conventional env name.
    - Added 4 unit tests (default-False, Pipfile=true, env=1 override,
      env=0 override) in `tests/unit/test_settings.py` under a new
      T18 section.  TDD: RED on the two env-var-override cases (the
      default-False and Pipfile-true cases happened to pass before the
      helper was wired because they just go through `_table().get`),
      GREEN after implementation.
    - Manually verified the three plan acceptance one-liners against
      `/tmp/test-prefetch-default/Pipfile` (no `[pipenv]` section) and
      `/tmp/test-prefetch-setting/Pipfile` (`prefetch_index_manifests = true`):
      - default-False: `Project().settings.get('prefetch_index_manifests', False)` -> `False`.
      - Pipfile=true: same call against the second Pipfile -> `True`.
      - `PIPENV_PREFETCH_INDEX_MANIFESTS=1 python -c "..."` -> `True`.
    - Full unit suite green: 872 passed, 9 Windows-only skips, 0 failures.
- **files edited/created**:
  - edited: `pipenv/utils/settings.py` (env-override helper + wiring into `get` / `__getitem__` / `__contains__`)
  - edited: `pipenv/environments.py` (new `PIPENV_PREFETCH_INDEX_MANIFESTS` setting)
  - edited: `tests/unit/test_settings.py` (4 new tests covering the three plan acceptance criteria + the falsy-env-override case)

---

### T19: Wire `ParallelFetcher` into `do_lock` (gated by T18) — pip-Session-backed, no SafeFileCache write

- **depends_on**: `[T9, T18]`
- **location**:
  - `pipenv/routines/lock.py` (new helper `_prefetch_index_manifests_if_enabled` invoked between the empty-category fast path and the per-category loop)
- **description**:
  In `do_lock`, after collecting `lockfile_categories` and before the resolve loop, check `project.settings.get("prefetch_index_manifests", False)`.  When `True` (and `not clear`):
  - Walk Pipfile categories, collect top-level package names.
  - Resolve `project.sources.pipfile_sources()` to a list of `(index_url, verify_ssl)` dicts.
  - Construct a `PEP691Client` whose underlying transport is **pip's own `PipSession`** (`pipenv.utils.internet.get_requests_session`).  Critical: this means pip's `SafeFileCache` writes happen automatically as a side effect of our GETs (because we're sharing the same Session class and cache dir).  We do **not** write to pip's `SafeFileCache` directly — that would require reverse-engineering pip's CacheControl on-disk format and would silently break on pip refactors.  By using `PipSession`, the cache compatibility is guaranteed at runtime by pip's own code.
  - Per-source: `verify_ssl=False` → set `session.verify = False`.  Read `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY` via the standard `requests`/`urllib3` env-var paths (which `PipSession` already respects).
  - Call `fetcher.populate(targets)` for the cross product (each top-level pkg × each source).
  - Log a single "prefetched N packages in M.MMs" line at info level when verbose.  **Do not log URLs** — they may contain credentials before stripping.
  - Errors are swallowed (best-effort).  Lock continues unchanged on prefetch failure.

  **Note on the perf hypothesis**: Phase 2's expected gain comes from parallelism — pip's `PipSession` fetches sequentially inside the resolver subprocess; pre-warming it concurrently from the parent process amortises latency on cold caches.  This is the same hypothesis as the failed phase-5 experiment, but with two differences: (a) we now also populate our own parsed cache for Phase 3 reuse, and (b) the setting is opt-in so dev-box users who measured neutral-or-worse in phase-5 are not affected.  If T21's CI measurement shows no cold-cache improvement, T22 documents the negative result and the setting stays off-by-default permanently.
- **validation**:
  - `pipenv lock --verbose` with `[pipenv] prefetch_index_manifests = true` prints "prefetched N packages in ..." and produces an identical lockfile (same hash) to a `pipenv lock` with the setting `false`.
  - No URL appears in any log line at any verbosity level (verify by grepping the captured stderr in the integration test).
  - Setting unset → zero change from current behaviour (verified by unchanged unit tests under T20 with setting absent).
  - Mocking `ParallelFetcher.populate` to raise → `do_lock` still completes successfully (best-effort path).
  - With `verify_ssl=False` on a source, the prefetch does not fail TLS validation against a self-signed cert (test via `tests/pytest-pypi/` local index with a self-signed cert).
- **status**: Completed
- **log**:
  - 2026-05-12: Implemented on branch `maintenance/code-cleanup-phase5-perf-2026-06`.  Added `_prefetch_index_manifests_if_enabled(project, lockfile_categories, *, clear)` to `pipenv/routines/lock.py` and invoked it inside `do_lock` immediately after the lockfile-pruning loop and before the use-default-constraints block (between the empty-category fast path's call site and the per-category resolve loop).  Design highlights:
    - **Zero new top-level imports**: `time`, `pathlib`, the three `pipenv.resolver.*` modules, and `get_requests_session` are imported lazily inside the helper so the cost when the setting is disabled (the common case) is one `dict.get` lookup.
    - **PipSession transport**: uses `pipenv.utils.internet.get_requests_session(verify_ssl=...)` which returns a `PipSession` already wired to pip's `SafeFileCache` and `PIP_CLIENT_CERT`.  Compatibility with pip's on-disk cache format is therefore guaranteed at runtime by pip's own code — we never reverse-engineer the CacheControl layout.
    - **Per-verify session grouping**: sources are bucketed by `verify_ssl` into at most two sessions.  Phase-2 dispatches via whichever verify policy is more common (defaulting to `True` when both exist).  Mixed-verify projects are documented as a known phase-3 follow-up; the minority sources simply miss the prefetch and fall through to pip's normal cold fetch (best-effort is the contract).
    - **Best-effort failure mode**: every step is wrapped in `try / except Exception: return` (or an inner pass for the log-emit step) so `do_lock` continues unchanged on any prefetch failure (offline network, unreadable cache dir, T7/T8/T9 modules absent, populate raises, etc.).  `--clear` short-circuits the helper entirely.
    - **No URL logging**: only a single `Prefetched N package indexes in M.MMs.` line is emitted, gated on `is_verbose() and not is_quiet()`.  Verified in smoke that no host, scheme, or credential string reaches the captured output at any verbosity.
    - Insertion point: `pipenv/routines/lock.py` line 227 (the call site sits between the lockfile-pruning loop and the `use_default_constraints` block, satisfying the plan's "between the empty-category fast path and the per-category loop" criterion since the empty-category logic itself is inside the per-category loop).
  - 2026-05-12: GREEN smoke evidence (7 inline scenarios via `unittest.mock`):
    - setting `False` (default) → helper returns before importing T7/T8/T9 or constructing a session.
    - `clear=True` even with setting `True` → short-circuits before session ctor.
    - setting `True` + `get_requests_session` raises → swallowed; no propagation.
    - setting `True` + `ParallelFetcher.populate` raises → swallowed; `populate` was called exactly once.
    - setting `True` + normal path → `populate(targets)` called with the cross product (2 pkgs × 2 sources = 4 tuples, both expected ones present).
    - empty Pipfile → no session ctor call.
    - empty sources list → no session ctor call.
    - Verbose-output captured: `'[dim]Prefetched 1 package indexes in 0.00s.[/dim]'` — zero leak of `USER`, `SECRET`, `pypi.org`, `private.example.com`, `https://`, or `http://`.
    - Verbose OFF → silent (no `err.print` calls).
  - 2026-05-12: Regression check: `pytest tests/unit/test_lock_sync_uninstall_context_routing.py tests/unit/test_locking_no_mutation.py tests/unit/test_lockfile.py` → 41 passed.  No new top-level imports in `pipenv/routines/lock.py` (verified by `grep -n "^import\|^from" pipenv/routines/lock.py`: 3 stdlib + 2 pipenv imports, identical to pre-T19).
  - 2026-05-12: Acceptance items verified locally: #2 (no URL/credential in stderr at verbose), #3 (setting unset → behaviour unchanged; smoke A passes), #4 (`populate` raises → `do_lock` completes; smoke D passes).  Items #1 (identical lockfile hash) and #5 (self-signed cert via `verify_ssl=False`) require a real Pipfile + network — they're owned by T20's integration suite.
- **files edited/created**:
  - `pipenv/routines/lock.py` — added `_prefetch_index_manifests_if_enabled` helper (~140 lines incl. docstring) and a 7-line invocation site inside `do_lock`.

---

### T20: Integration test — prefetch enabled produces correct lockfile

- **depends_on**: `[T19]`
- **location**:
  - `tests/integration/test_prefetch_manifest.py` (new)
- **description**:
  - Setup: small Pipfile (3-5 packages, mix of pure-python and platform wheels).
  - Run `pipenv lock` with setting off → record lockfile hash `H1`.
  - Run `pipenv lock` with setting on → record lockfile hash `H2`.
  - Assert `H1 == H2` (resolution result identical).
  - Edge case: simulate a 404 for one package from one source via a mock pypi-server fixture → setting on should still produce the same lockfile (pip's resolver hits the other source successfully).
  - Edge case: simulate a transient network error during prefetch → setting on still completes the lock (best-effort path).
- **validation**: `pytest tests/integration/test_prefetch_manifest.py` passes.
- **status**: Completed
- **log**:
  - 2026-05-12: Created `tests/integration/test_prefetch_manifest.py` with five active tests + one documented skip:
    - `test_prefetch_does_not_change_resolution_result` — locks once with the setting off (default), deletes `Pipfile.lock`, re-locks with `PIPENV_PREFETCH_INDEX_MANIFESTS=1`; asserts identical `_meta.hash.sha256` AND identical per-section `{name: version}` pins (the robust hash-comparison gotcha from the plan instructions).
    - `test_prefetch_swallows_populate_failure` — sitecustomize-injected `ParallelFetcher.populate` that raises `RuntimeError("simulated network failure")`; asserts `pipenv lock` still exits 0 and writes a valid lockfile.
    - `test_prefetch_skipped_under_clear` — sitecustomize-injected populate that appends to a marker file; runs `pipenv lock --clear`; asserts marker missing (T19's `--clear` short-circuit).
    - `test_prefetch_verbose_output_no_url_leak` — runs `pipenv lock --verbose`, scans stderr lines containing "Prefetched"; asserts the canonical wording is present and that those specific lines carry no `https://`, no `http://`, no `/six/` / `/click/` / `/tablib/` URL fragments, no `@pypi.org` host, and no `:***@` credential placeholder.  Scoped to the helper's own line because pip's resolver legitimately logs `LinkCandidate('https://...')` lines at INFO — out of T19's scope.
    - `test_clear_invalidates_parsed_manifest_cache` — seeds `<PIPENV_CACHE_DIR>/manifests-v1/stale-marker` directly, runs `pipenv lock --clear`, asserts the marker is wiped.  Seed-and-clear keeps the test hermetic (proves T17's wiring, not a side-effect of T19 prefetch behaviour).
    - `test_prefetch_with_self_signed_source` — explicit `pytest.skip(...)` with docstring; no self-signed-cert fixture exists in the repo, so the `verify_ssl=False` branch of T19's session-per-policy code stays unproven end-to-end.  Gap documented inline.
  - 2026-05-12: Subprocess-realm mocking gotcha: `_PipenvInstance.run_command` shells out to a child `pipenv` process, so in-process `mock.patch` is invisible.  Tests 2 + 3 instead write a `sitecustomize.py` into a per-test dir, prepend that dir to `PYTHONPATH`, and rebind `pipenv.resolver.fetcher.ParallelFetcher.populate` via an import hook that fires the moment the helper's lazy `from pipenv.resolver.fetcher import ParallelFetcher` resolves.  Validated locally that the patched attribute is `_T20_PATCHED = True` after the import.
  - 2026-05-12: T17 / T19 path mismatch surfaced during Test 5 design: `_prefetch_index_manifests_if_enabled` writes to `<PIPENV_CACHE_DIR>/pipenv-manifests/manifests-v1/` (cache root = `PIPENV_CACHE_DIR / "pipenv-manifests"`) while `_clear_parsed_manifest_cache` wipes `<PIPENV_CACHE_DIR>/manifests-v1/` (cache root = `PIPENV_CACHE_DIR` directly).  Test 5 sidesteps this by seeding the path `--clear` actually wipes (asserting only on T17's wiring per the plan note "verifies T17's `--clear` wiring touched our cache").  Production code NOT modified per task instructions; flagging the divergence for a Phase-3 follow-up — if both surfaces are meant to share a root, one of them needs to move.
  - 2026-05-12: `pytest tests/integration/test_prefetch_manifest.py -v --override-ini="addopts=-ra"` → 5 passed, 1 skipped, 0 failed, ~9 s.
- **files edited/created**:
  - `tests/integration/test_prefetch_manifest.py` — new (~420 lines incl. docstrings + sitecustomize injection helpers).

---

### T21: CI bench measurement — confirm cold-cache improvement, no warm regression (multi-run, statistically guarded)

- **depends_on**: `[T19, T20]`
- **location**:
  - `benchmarks/benchmark.py` (add a `--runs N` flag if it doesn't already support multi-run; default 1 today)
  - `.github/workflows/ci.yaml` (extend the benchmark job to run **3× with setting off** and **3× with setting on**; store both stats.csv sets as artifacts)
  - Commit message + PR description record the before/after numbers and the run-to-run variance
- **description**:
  Run the benchmark harness on CI for the same commit, 3 iterations per configuration:
  - **Run A** (3 iterations): existing baseline (setting off).  Compute median + standard deviation per stat.
  - **Run B** (3 iterations): setting on (`PIPENV_PREFETCH_INDEX_MANIFESTS=1`).  Same statistics.
  Acceptance:
  - `lock-cold` and `install-cold` median improves by ≥ max(10 %, 2σ_A) — i.e., the improvement must be both ≥10 % AND statistically distinct from baseline noise.
  - `lock-warm` and `install-warm` median may differ by at most max(±2 %, ±1σ_A) — no regression beyond noise.
  - `import` (install-cold + lock) improves roughly proportionally.
  If the warm-path regresses meaningfully, the setting stays `default = False` and the doc records the tradeoff explicitly.  If both improve, we have evidence for a future Phase 4 promotion to default.

  **Why 3 runs:** single-sample measurements on CI shared runners can swing ±15 % between identical commits.  The phase-5 work taught us not to trust single-sample numbers.  3 runs is the minimum that lets us reason about variance without massively slowing CI; if 3 is still too noisy, T21 may be re-spec'd to 5.
- **validation**:
  - Both artifacts exist; both have 3 iterations of every stat.
  - Median + standard deviation computed for each stat and recorded in the PR description.
  - lock-cold improvement ≥ max(10 %, 2σ_A).
  - lock-warm regression ≤ max(2 %, 1σ_A).
  - The recorded numbers are in the Phase 2 commit message AND in the design doc's §11 for future reference.
- **status**: Skipped (per maintainer)
- **log**:
  - 2026-05-12: Skipped per maintainer scoping decision — no appetite for a multi-run statistically-guarded CI bench step at this time.  T22 ships Phase-2 docs with the Phase-2 perf claim explicitly labelled theoretical (see §11a sign-off note in `docs/dev/initiative-g-pure-python-design.md`).  The setting ships opt-in (default `false`) so warm-cache dev machines are unaffected.  Future maintainer interest can re-spec the bench gate.
- **files edited/created**:

---

### T22: Phase-2 ship-tasks: user docs, news fragment, design-doc Phase-2 status update

- **depends_on**: `[T19, T20, T21]`
- **location**:
  - `docs/advanced.rst` (or wherever the `[pipenv]` settings reference lives — confirm with `grep -l "allow_prereleases" docs/`) — add an entry for `prefetch_index_manifests`: default, semantics, when to enable, when not to.
  - `news/initiative-g-phase2-prefetch-bridge.feature.rst` (new)
  - `docs/dev/initiative-g-pure-python-design.md` (Phase 2 acceptance bullets flipped to checked, with the actual measured delta from T21)
- **description**:
  Three tasks bundled:
  1. **User-facing doc** for the new `[pipenv] prefetch_index_manifests` setting.  Without this the setting is undiscoverable; users won't know it exists until they read the source.
  2. **News fragment** in repo convention.
  3. **Design-doc status update** — Phase 2 acceptance bullets checked off, T21's measured deltas recorded inline so future maintainers can see whether the experiment paid off.
- **validation**:
  - News fragment validates per repo convention.
  - `grep -A 3 prefetch_index_manifests docs/` returns the new user-facing documentation block.
  - Design doc shows Phase 2 row updated with concrete numbers.
- **status**: Completed
- **log**:
  - 2026-05-12: Shipped Phase-2 docs at commit `69e821bd` on branch `maintenance/code-cleanup-phase5-perf-2026-06`.  Three deliverables:
    1. **User-facing doc** added under `docs/pipfile.md` in the `[pipenv]` directives section (matched the style of the existing `cool-down-period` block — `####` heading, prose paragraphs for what/when/when-not/`--clear`/security, TOML example).  Captured all six required bullets from the task spec: what it does, when to enable, when not to (cites the phase-5 empirical result), env-var override, default false, `--clear` short-circuit, security (PipSession auth inheritance + no-URL-logging guarantee).
    2. **News fragment** at `news/initiative-g-phase2-prefetch-bridge.feature.rst` in towncrier `.feature.rst` convention; matched the terminal-prose style of the existing `+cool-down-period.feature.rst` and `initiative-g-phase1-pep691-client.feature.rst` fragments (no markdown headers, double-backtick code spans, ~70-char soft wrap).  `python -m towncrier build --draft --version 9999.9.9` confirms the fragment is detected and rendered under `Features`.
    3. **Design-doc updates** to `docs/dev/initiative-g-pure-python-design.md`: (a) top-of-file status line flipped to `Phases 1 + 2 shipped (Phase 2 with CI bench gate skipped); phases 3-4 awaiting maintainer sign-off`; (b) §11 Phase 2 acceptance bullets — items 1 + 3 flipped to `[x]`, item 2 (CI bench gate) kept as `[ ]` with inline `(deferred — maintainer scoped Phase-2 to ship without a CI bench gate; see note below)` annotation; (c) new §11a "Phase 2a — sign-off note (T22)" sub-section explicitly documenting that the Phase-2 perf claim is theoretical not measured, the underlying parallelism hypothesis is sound but unverified on the current CI bench, and the setting ships opt-in specifically because the phase-5 experiment was empirically net-harmful on warm-cache dev machines.  Tone choice: honest "claim is theoretical" framing rather than hand-waving; explicit invitation for future maintainer-driven bench data to re-spec the criteria.
    4. **Plan housekeeping**: T21 marked Skipped (per maintainer) with a one-line log entry referencing the user's scoping decision; T22 marked Completed (this entry).
  - 2026-05-12: Verifications recorded: `python -m towncrier --version` → `25.8.0`; `grep -A 5 prefetch_index_manifests docs/` returns the new entry in `docs/pipfile.md`; `grep -A 6 "^### Phase 2$" docs/dev/initiative-g-pure-python-design.md` confirms the deferred-T21 annotation and §11a anchor.  Pre-commit hooks (ruff, news-fragment, NEWS, eof-fix, large-file, case-conflict, etc.) all skipped or passed on the docs+news commit; zero code changes per task constraint.
- **files edited/created**:
  - `docs/pipfile.md` (modified — added `prefetch_index_manifests` to the inline `[pipenv]` example and a new `####` section after `cool-down-period`).
  - `news/initiative-g-phase2-prefetch-bridge.feature.rst` (created).
  - `docs/dev/initiative-g-pure-python-design.md` (modified — top-of-file status line + §11 Phase 2 acceptance bullets + new §11a sign-off note).
  - `initiative-g-phase1-2-plan.md` (working-tree-only — T21 marked Skipped, T22 marked Completed).

---

## Parallel Execution Groups

Reconciled to per-task `depends_on` (review items 2 + 3 fixed):

| Wave | Tasks (can run concurrently) | Can Start When |
|------|-------|----------------|
| 1 | T1, T2, T6, T18 | Immediately (no deps) |
| 2 | T3, T7, T11, T16 | T1 done (T3, T7, T11); T6 done (T16) |
| 3 | T4, T5 | T1, T2, T3 done |
| 4 | T8, T10, T12, T14 | T4, T5, T6 done (T8); T4, T5 done (T10); T2, T4, T5 done (T12); T7 done (T14) |
| 5 | T9, T13 | T7, T8 done (T9); T8 done (T13) |
| 6 | T15, T17, T19 | T9 done (T15, T17, T19); T1, T7, T8 also done (T17); T18 also done (T19) |
| 7 | T20 | T19 done |
| 8 | T21 | T19, T20 done |
| 9 | T22 | T19, T20, T21 done |

**Maximum parallelism**:
- Wave 1 supports 4 concurrent agents.
- Wave 2 supports 4 concurrent agents.
- Wave 4 supports 4 concurrent agents.
- Wave 6 supports 3 concurrent agents.

Total: 22 tasks, conservatively executable across 9 sequential waves with parallelism inside each wave.

---

## Testing Strategy

- **Unit tests run continuously** alongside implementation tasks (T11–T16 each pin to their respective implementation task).
- **Integration tests** (T10, T20) gated behind `@pytest.mark.network` where they require real PyPI; CI runs them, dev-box opt-in.
- **Parity test** (T10) is the load-bearing Phase-1 acceptance criterion.  Any divergence from pip must be explicitly documented in `tests/integration/test_pep691_parity_known_diffs.md` with a justification.
- **Benchmark measurement** (T21) is the load-bearing Phase-2 acceptance criterion.  Numbers must come from CI, not dev-box.  Local-dev measurements caught us out in phase-5 and that lesson is baked into the criterion.
- **Coverage targets** in the validation sections of T11–T15 are floors, not ceilings.  Reviewer should fail the task if coverage drops below the listed threshold.

---

## Risks & Mitigations

| Risk | Mitigation |
|---|---|
| PEP 691 parser misses an edge case pip handles silently | T10 fixture-parity test against pip's own parser on the curated fixture set; divergences justified in writing.  Live-PyPI parity audit deferred to Phase 3 per design §7.4. |
| Cache schema changes break existing users | `schema_version` field + version-mismatch invalidation in T7. |
| Prefetch is net-harmful on warm caches (replays phase-5 failure mode) | T21 measures warm-path explicitly with **3 runs each side and 2σ guards**; setting defaults `False`; ship off-by-default until CI bench shows wins outside noise. |
| Auth path leaks credentials to subprocess argv or logs | T6 + T13 strip URL credentials before request; T19 forbids logging URLs at any verbosity; T20 asserts no creds appear in captured stderr. |
| `ParallelFetcher` connection-pool exhaustion under high worker count | `max_workers=16` cap matches urllib3's default pool size; T15 verifies parallelism via dispatch-order instrumentation (not wall-time). |
| Phase-2 fetcher writes corrupt entries to pip's SafeFileCache, breaking pip's resolver | **Removed via T19 redesign**: the prefetcher now drives pip's own `PipSession`, so SafeFileCache writes happen through pip's own code path. We never reverse-engineer pip's CacheControl on-disk format. Compatibility is guaranteed at runtime by pip. |
| Coverage claims in T11–T15 silently regress | T17 wires `--cov-fail-under` so missing coverage fails CI rather than living as aspirational text. |
| `pip._internal` imports creep back into `pipenv/resolver/*` over time | T17 ships a pre-commit hook scoped to `pipenv/resolver/` that fails any commit reintroducing them. |
| Hostile / compromised index returns absolute URLs pointing off-host (cache poisoning) | **Accepted risk** — pip has the same exposure and we don't fix it in Phase 1. Phase 3's parity audit revisits whether to add cross-host link rejection. Documented here so it's a known surface, not a surprise. |
| Per-source `verify_ssl=False` or proxy env vars not honored, breaking private-index users | T19 explicitly threads `verify_ssl` into the `PipSession` and relies on `PipSession`'s existing proxy handling (`HTTPS_PROXY` / `NO_PROXY`); T20 includes a self-signed-cert integration scenario. |
| Single-sample Phase-2 perf claim is noise | T21 requires 3 runs each side, median + 2σ guard. No single-run pass-or-fail. |
| Real PyPI fixture changes cause T10 parity test to flake | Phase 1 parity is fixture-only (offline); Phase 3 owns the live-PyPI audit with its own provenance pinning. |
| Initiative-G code drifts from Initiative-F backend abstraction during Phase 3 prep | Defer Phase 3 to its own plan; Phase 1+2 explicitly ship without `Backend` integration. |

---

## Out of Scope (this plan only)

- The full `pure-python` backend (`PurePythonProvider` etc.) — that's Phase 3, separate plan.
- HTTP/2 transport (httpx).
- PEP 658 metadata pre-fetching (deferred to Phase 3).
- Keyring auth backend (deferred to Phase 3).
- Removing the pip backend (Phase 4).
- Cross-platform lockfiles (orthogonal).

---

## Phase-3 follow-ups landed during plan execution

Three items originally flagged as "Phase 3 follow-ups" were addressed during plan execution and landed on this same branch, ahead of Phase 3's formal scoping:

### FU1 — `ParsedManifestCache.peek_etag()` + fetcher stale-etag short-circuit
- **Commit**: `91c1e4e9`
- **Trigger**: T9's agent flagged that `ParsedManifestCache.get()` returns `None` past expiry without exposing the on-disk etag, so `ParallelFetcher` couldn't send `If-None-Match` for stale entries and the option-a TTL refresh was unreachable dead code.
- **Resolution**: Added `peek_etag(index_url, package_name) -> str | None` reading on-disk regardless of expiry. `ParallelFetcher.populate` now calls it on cache misses and threads the result through as `if_none_match`. T9's `status="not-modified"` branch now actually fires.
- **Tests**: 15 new cache tests + 5 new fetcher tests. Coverage stays at 100% on both modules.

### FU2 — Per-source `verify_ssl` fan-out in the prefetcher
- **Commit**: `4a0ff8a1`
- **Trigger**: T19's agent flagged that the prefetcher used a single "majority verify policy wins" `PipSession` for all targets, so mixed-policy projects (e.g., self-signed private index alongside public PyPI) silently fell through to pip's cold fetch on the minority sources.
- **Resolution**: Refactored `_prefetch_index_manifests_if_enabled` to build one `ParallelFetcher` per unique `verify_ssl` value; targets now carry `(url, pkg, verify)` 3-tuples and are grouped by policy at dispatch time. Single-policy projects (common case) are regression-tested for zero-overhead.
- **Tests**: 4 new unit tests (`tests/unit/test_prefetch_fan_out.py`) + 1 new integration test (`test_prefetch_routes_per_source_verify_ssl`).

### FU3 — Per-request TLS material in `PEP691Client.fetch`
- **Commit**: `0047a2e3`
- **Trigger**: T8's agent flagged that `cert` and `verify` constructor kwargs were stored on `self` but never threaded into the actual `session.request(...)` call — making FU2's per-source verify routing a no-op at the request layer.
- **Resolution**: Pass `verify=self._verify, cert=self._cert` to `session.request(...)`. PipSession (the production transport, a `requests.Session` subclass) honours per-request TLS material.
- **Tests**: 10 new tests in `TestPerRequestTLSMaterial`. Coverage stays at 100%.

### Remaining Phase-3 work (not addressed in this branch)
- Self-signed-cert integration test fixture (T20's skipped scenario) — needs `tests/pytest-pypi/` work.
- The full `pure_python.Provider` and `pure-python` resolver backend (Phase 3's main deliverable, per design doc §7.4).
