--- title: "1.15.14 — Deploys stop dying over PyPI cache lag; fail-closed test isolation" description: "The framework install becomes a convergence that fails open: the target is confirmed against PyPI's JSON API, installed with cache-bypassing retries on every pip version, and a target that still will not install files a deploy warning and deploys on the installed framework instead of killing a teste…" date: "2026-08-21" tags: ["release"] canonical: "https://django-mojo.sitesmojo.com/changelog/1-15-14/" --- The framework install becomes a convergence that fails open: the target is confirmed against PyPI's JSON API, installed with cache-bypassing retries on every pip version, and a target that still will not install files a deploy warning and deploys on the installed framework instead of killing a tested API's deploy — while publish.py no longer reports "released" until the world can actually install the release. Also ships the fail-closed default-tier test isolation enforcement and a WebApp upload retry. ### Added - Test runner: fail-closed default-tier isolation enforcement. Every repository test package declares its state in `TESTIT` (`default_core: True` for clean packages, or a nonempty `requires_extra` for opt-in ones, with `serial` mandatory when the scan finds shared mutation); an AST audit over the whole repository tree runs before any worker starts, and `-t`/direct-file runs do not bypass it. Consumer and application test roots are exempt. - `publish.py` polls after the upload until the new version answers on both the PyPI JSON API and the Simple index — the endpoint pip actually resolves through — before reporting released; a timeout warns and says to hold deploys. ### Changed - The deploy's framework install is a convergence, never a one-shot veto: the target (fleet pin, or the JSON API's latest on bare runs) is confirmed to exist against PyPI's JSON API, then installed with bounded retries whose retries pass `--no-cache-dir` — defeating every pip cache on every pip version, replacing the feature-detected `--refresh-package` flag that was a silent no-op on pips without it. - A pin the JSON API 404s, or one that exhausts its retries, **fails open**: the deploy continues on the framework already installed and files a `framework` deploy warning naming both versions. The only fatal framework state is a node with no django-mojo at all. - Bare deploys no longer run blind `pip install --upgrade django-mojo` — through a stale index that silently installed the previous version and reported success; they converge onto the JSON API's latest as an exact pin. - Provisioning's version pin (stage1) retries through the caches the same way; a fresh node has nothing to fall back on, so exhaustion there stays fatal. - Default-tier tests now use dependency seams (`reporter=`, `publisher=`, `send_email=`, `loader=`) instead of patching shared incident/jobs/settings/paths surfaces — the recorded parallel-suite races those patches caused are gone. ### Fixed - The WebApp deploy GitHub action retries transient upload disconnects instead of failing the deploy. ### Upgrade notes - The deploy-side robustness rides in `post_deploy.sh`, which runs from the *installed* release — it protects every deploy after the first one that carries it. - Repositories built on django-mojo's own test tree must add `TESTIT` declarations to their repo test packages; consumer/application test roots are unaffected.