KittenTTS 0.8.1 requires misaki[en]>=0.9.4. PyPI's misaki 0.9.4 caps
Python below 3.13. NousResearch/misaki f03fd2be73 is upstream main with
the cap raised to <3.15 and no code change, so the extra declares
misaki[en] at that commit and uv resolves KittenTTS's requirement to it.
misaki[en] also lists spacy-curated-transformers. The newest release
that fits spacy 3.8, 0.3.1, caps Python below 3.14, so PM's `uv pip
check` rejected every 3.14 venv that carried it. Nothing imports it:
only transformer spaCy models use it, and KittenTTS never builds a
misaki G2P. A never-true override marker removes it from the lock.
torch, transformers, spaCy and num2words stay: misaki.en imports them
at module level and KittenTTS imports misaki.en.
Native bundles sync with --all-extras, so un-gating the extra would put
KittenTTS, torch and spaCy in every payload. [tool.hermes] opt-in-extras
names the extras that only an explicit selection installs. PM adds
--no-extra for each of them to an all-extras sync, so bundles and their
recorded feature list leave kittentts out, and setup still installs it
through sync_venv when the user picks it.
The extra is gated on platform instead of Python: onnxruntime has no
Intel macOS wheel and torch has no Windows ARM64 wheel. Nix gets the
hatchling build backend the misaki git source needs.
Verified on macOS arm64 with CPython 3.14.7 and uv 0.12.3:
uv sync --frozen --extra kittentts from this lock, then uv pip check
reports all 126 packages compatible. _generate_kittentts writes a
3.87 s 24 kHz WAV (RMS 3661), and no spaCy model is downloaded. A dry-run
all-extras sync with --no-extra kittentts selects none of kittentts,
misaki, torch, transformers or spaCy. The new build test fails without
the environment change and passes with it.
The builder's imported modules could mark an empty dependency tree as
installed. Probe every anchor in one isolated target process. Require
all anchors for a multi-module extra, and process only the selected
tree's .pth files so editable packages retain their launch behavior.
The native builder passes its staged Python explicitly and stops before
manifest publication when the inventory fails. Correct the Hindsight
and Teams anchors using the namespaces in their locked wheels.
Verified: 31 tests passed, 3 host skips. A real target child records its
identity, and a caller mutation back to the builder Python fails the
regression. Both downloaded SDK wheels match uv.lock and pass inventory
and availability checks. Ruff and added-comment checks passed.
No full native package or signing run is claimed.
Lazy installs OFF is now a real contract, not just 'refuse
everything': the bundle's EXACT extras list is the frozen feature set
(settled 2026-09-02 plan, task 5).
- pm/features.py: write_features()/read_features() over
enabled-features.json at the payload root (bundle-written, beside
manifest.json; at runtime store_root().parent — the same relative
location on both install kinds). installed_extras() records what
`uv sync --all-extras` ACTUALLY installed on the target: every
declared extra whose pm anchor resolves in the staged venv —
marker-gated extras show up as missing anchors, the honest
per-platform record.
- cmd_bundle: after the staged venv sync, write the features file into
the payload ('✓ enabled-features.json (N extras recorded)').
- sync_venv: when security.allow_lazy_installs is false and the file
exists, requested extras OUTSIDE the frozen set are refused with a
message naming the policy.
- Venv.apply: plugin members + lazy-off + frozen file = loud InstallError
(the bundle IS the install; never union plugin deps into it).
tests/pm/test_features.py: 7 tests (roundtrip, absent/garbage reads,
payload-root path, anchor-truth installed_extras, frozen refusal,
in-set pass). tests/pm: 162 passed 0 failed.