You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TRANSCRIBE_DESIGN_HANDOFF.md opens the design work as seven
items. This issue takes the provisioning half of them — handoff open item 3 (the
environment layout, concretely), the transport half of item 2 (how a stage runs in an
environment that is not the tool's own), and everything audio packages needs to exist. #12 takes the backend half.
The seam between the two is stated below and is worth reading before either starts.
This one comes first because it can invalidate the other's layout, not because it is
foundational in the abstract. The handoff's suggested sequence puts one uv resolution
attempt at step 1 for exactly that reason.
Question: can vibevoice-asr-7b, qwen3-forcedaligner, and firered-asr2s share one
locked Python environment? VOCABULARY declares three provisioned environments as the floor
and marks this joint resolution unverified. If it fails, torch splits, three
environments becomes four, and the plan's environment-spanning claims change.
The evidence already on this machine says the answer is probably no, and says why. The three
source checkouts each carry their own .venv, and those venvs — which are what actually
produced every recorded figure — disagree on a major version:
Checkout
torch
transformers
huggingface_hub
numpy
model_tests/firered/FireRedASR2S/.venv
2.10.0
5.1.0
1.27.0
2.4.2
model_tests/forced_aligner/.venv
2.13.0
4.57.6
0.36.2
2.4.6
model_tests/vibevoice/VibeVoice/.venv
2.13.0
4.57.6
0.36.2
2.4.6
(Read off *.dist-info under each venv's site-packages. The aligner venv also carries qwen_asr==0.0.6 and qwen_omni_utils==0.0.9; VibeVoice carries diffusers==0.39.0.)
model_tests/vibevoice/VibeVoice/pyproject.toml declares transformers>=4.51.3,<5.0.0, so
FireRed's as-built transformers is outside VibeVoice's declared range. A single lock
satisfying both requires one of them to move, and moving either means re-running its recorded
evidence before any figure in model_tests/ still describes it.
Also worth knowing before trusting any tracked requirement file: model_tests/firered/FireRedASR2S/requirements.txt pins torch==2.1.0+cu118 and transformers==4.51.3 against a CUDA index. Neither can be what ran on Apple Silicon, and
neither matches the venv above. The as-built venvs are the artifact; the upstream
requirement files are not. Do not lock from them.
So the experiment is not "attempt one resolution" but three questions in order:
Does a joint uv resolution of the three as-built dependency sets exist at all? Resolve
only; do not install.
If it does not, is the conflict genuinely FireRed's transformers 5.x requirement, or did
the venv drift upward from a floor that 4.57.6 also satisfies? This decides whether the
split is forced or incidental, and a forced split is a different registry than an
incidental one.
If FireRed genuinely needs transformers 5.x, what is the smallest split — torch plus torch-firered, or something that groups differently — and what does that do to environments_spanned for a firered + word_timestamps request?
Record the outcome as an evidence artifact, whichever way it goes, and cite the resolver
output. "They conflict" without the resolver's own message is not a result. A negative
result here is the cheap one; discovering it during implementation is not.
What is already decided
Do not redesign these; they have stated reasons in VOCABULARY and the contract.
One root, AUDIO_PROCESSING_MODEL_CACHE when set, else the per-platform cache dir src/audio_cli/vad.py already resolves. Nothing provisioned beside a source checkout.
registry.json, models/, envs/{mlx,torch,swift}/ is the declared tree.
Locks live in this repository.pull materializes a lock; it never resolves "latest".
This is what keeps the mlx-audio private batched API at the one version the source-hash
guard expects.
Provisioning is explicit and fails closed.pull is the only thing that downloads
weights, builds the Swift product, or applies the VibeVoice patch. A transcription request
never triggers any of them — it exits 3 with the pull line that would fix it.
Only small hash-pinned artifacts auto-fetch, the way vad.py already does, digest
check and atomic rename included.
remove is reference-counted; purge reads the registry rather than shell history and
reports reclaimable bytes first; both state that the HF cache may be shared.
Hub weights stay in the HF cache. The registry records which revisions this tool
materialized, rather than duplicating a snapshot.
fix is a runnable command wherever a configuration exists that would work.
Design questions this issue answers
registry.json's schema. Unspecified today. It has to serve five readers with
different needs: list (bytes, license, used_by_stacks), verify (digests, the mlx-audio source hash, applied patches, whether the Swift product runs), remove
(reference counts, materialized Hub revisions), purge (everything, from a session with
no history), and run's exit-3 check (is this package present, at this revision). Decide
whether it is one document or a document plus per-package sidecars, and how it survives a
crashed pull — a half-written registry that reads as provisioned is the failure mode
that turns exit 3 into exit 1.
How an environment is created and located.uv venv from a tracked lock is the
obvious mechanism (and this repo's convention), but the questions are: one lock file per
environment or one project with extras; how the tool finds each environment's interpreter
afterwards; what happens when the tool is installed via uv tool install and therefore
has no project checkout to read locks from — the locks must ship in the wheel or be
fetched, and pyproject.toml's [tool.hatch.build] include list currently ships neither.
Non-Python environments.swift has no interpreter: it is a build product plus one
Core ML package, and swift may be absent (doctor reports that, does not fail). speaker-diarization-coreml is a separate package from fluidaudio in the declared
tables and must stay one.
The VibeVoice patch.logits_to_keep is tracked under model_tests/benchmark/patches/. Decide where a patch lives in the shipped tool, how verify knows it is applied, and what --repair does when it is not.
Cross-environment invocation. Not the stage ordering — that is #12 — but the mechanism a torch stage is invoked through from a core-environment process, and what crosses that
boundary. The recorded evidence used fresh subprocesses per stage
(run_interview_pipeline.py), so a persistent worker is outside what was measured and
needs its own justification. Whatever it is, it has to make "no two model stages resident
at once" enforceable rather than aspirational.
audio doctor. Tool version and path, ffmpeg/ffprobe/swift/uv presence, platform,
memory, disk, resolved root, per-environment and per-package state.
Testing this without downloading 32 GB. The registry, reference counting, purge --dry-run, exit 3, and the integrity-failure path are all testable against a fabricated
root. Decide what the fake root looks like and which parts genuinely require a live pull
(live marker already exists in pyproject.toml).
One table, two owners, and it must not become two tables.
This issue owns the package/environment columns: package id, environment, kind,
revision, digest, bytes, license, requires_tool, and the (stack, want) → package set
mapping that pull --stack S --want ... and run's exit-3 check both consume.
The torch resolution question is answered by a recorded resolver run, and VOCABULARY's
environment table plus its "unverified" note are updated to match — including the split and
its reason if it splits.
registry.json has a written schema, and a crashed pull cannot leave a root that reads
as provisioned.
audio packages list | pull | verify | remove | purge | path and audio doctor produce the
key sets in TRANSCRIBE_HAPPY_PATH §1.3 and §5 — including environments_kept_reason, hub_cache_note, unsized_packages, and the non-blocking license_unreviewed warning.
remove on vibevoice-asr-7b keeps torch while the aligner and firered-asr2s need it,
and says so.
run before pull exits 3 with packages_not_provisioned and a fix line that, run
verbatim, provisions exactly the missing set. A digest/size/revision mismatch exits 3 with package_integrity_failed and --repair.
uv run --extra dev pytest stays green; the enhance render-from-original path is not
regressed.
Traps
A claim about a package must cite the artifact, not a summary. The handoff's process
rule exists because three documents agreed on something no artifact supported. The
requirements-file-versus-venv divergence above is the same failure shape, one layer down.
Do not add registry structure nobody dispatches on. The capabilities report shed four
nested objects for exactly this reason. If no command branches on a field, it is a note.
Do not describe machinery that does not exist. If verify declares it checks applied
patches, write the test that fails when a patch is reverted, and check that it can fail.
Context
TRANSCRIBE_DESIGN_HANDOFF.md opens the design work as seven
items. This issue takes the provisioning half of them — handoff open item 3 (the
environment layout, concretely), the transport half of item 2 (how a stage runs in an
environment that is not the tool's own), and everything
audio packagesneeds to exist.#12 takes the backend half.
The seam between the two is stated below and is worth reading before either starts.
This one comes first because it can invalidate the other's layout, not because it is
foundational in the abstract. The handoff's suggested sequence puts one
uvresolutionattempt at step 1 for exactly that reason.
The naming contract is VOCABULARY.md §"Packages and environments"; the
command surface is TRANSCRIBE_CONTRACT.md §0 and §6; the output
shapes to diff against are TRANSCRIBE_HAPPY_PATH.md §1.3 and §5.
None of it is built.
The experiment that comes first
Question: can
vibevoice-asr-7b,qwen3-forcedaligner, andfirered-asr2sshare onelocked Python environment? VOCABULARY declares three provisioned environments as the floor
and marks this joint resolution unverified. If it fails,
torchsplits, threeenvironments becomes four, and the plan's environment-spanning claims change.
The evidence already on this machine says the answer is probably no, and says why. The three
source checkouts each carry their own
.venv, and those venvs — which are what actuallyproduced every recorded figure — disagree on a major version:
model_tests/firered/FireRedASR2S/.venvmodel_tests/forced_aligner/.venvmodel_tests/vibevoice/VibeVoice/.venv(Read off
*.dist-infounder each venv'ssite-packages. The aligner venv also carriesqwen_asr==0.0.6andqwen_omni_utils==0.0.9; VibeVoice carriesdiffusers==0.39.0.)model_tests/vibevoice/VibeVoice/pyproject.tomldeclarestransformers>=4.51.3,<5.0.0, soFireRed's as-built transformers is outside VibeVoice's declared range. A single lock
satisfying both requires one of them to move, and moving either means re-running its recorded
evidence before any figure in
model_tests/still describes it.Also worth knowing before trusting any tracked requirement file:
model_tests/firered/FireRedASR2S/requirements.txtpinstorch==2.1.0+cu118andtransformers==4.51.3against a CUDA index. Neither can be what ran on Apple Silicon, andneither matches the venv above. The as-built venvs are the artifact; the upstream
requirement files are not. Do not lock from them.
So the experiment is not "attempt one resolution" but three questions in order:
uvresolution of the three as-built dependency sets exist at all? Resolveonly; do not install.
the venv drift upward from a floor that 4.57.6 also satisfies? This decides whether the
split is forced or incidental, and a forced split is a different registry than an
incidental one.
torchplustorch-firered, or something that groups differently — and what does that do toenvironments_spannedfor afirered+word_timestampsrequest?Record the outcome as an evidence artifact, whichever way it goes, and cite the resolver
output. "They conflict" without the resolver's own message is not a result. A negative
result here is the cheap one; discovering it during implementation is not.
What is already decided
Do not redesign these; they have stated reasons in VOCABULARY and the contract.
AUDIO_PROCESSING_MODEL_CACHEwhen set, else the per-platform cache dirsrc/audio_cli/vad.pyalready resolves. Nothing provisioned beside a source checkout.registry.json,models/,envs/{mlx,torch,swift}/is the declared tree.pullmaterializes a lock; it never resolves "latest".This is what keeps the
mlx-audioprivate batched API at the one version the source-hashguard expects.
pullis the only thing that downloadsweights, builds the Swift product, or applies the VibeVoice patch. A transcription request
never triggers any of them — it exits 3 with the
pullline that would fix it.vad.pyalready does, digestcheck and atomic rename included.
removeis reference-counted;purgereads the registry rather than shell history andreports reclaimable bytes first; both state that the HF cache may be shared.
materialized, rather than duplicating a snapshot.
fixis a runnable command wherever a configuration exists that would work.Design questions this issue answers
registry.json's schema. Unspecified today. It has to serve five readers withdifferent needs:
list(bytes, license,used_by_stacks),verify(digests, themlx-audiosource hash, applied patches, whether the Swift product runs),remove(reference counts, materialized Hub revisions),
purge(everything, from a session withno history), and
run's exit-3 check (is this package present, at this revision). Decidewhether it is one document or a document plus per-package sidecars, and how it survives a
crashed
pull— a half-written registry that reads as provisioned is the failure modethat turns exit 3 into exit 1.
uv venvfrom a tracked lock is theobvious mechanism (and this repo's convention), but the questions are: one lock file per
environment or one project with extras; how the tool finds each environment's interpreter
afterwards; what happens when the tool is installed via
uv tool installand thereforehas no project checkout to read locks from — the locks must ship in the wheel or be
fetched, and
pyproject.toml's[tool.hatch.build]include list currently ships neither.swifthas no interpreter: it is a build product plus oneCore ML package, and
swiftmay be absent (doctorreports that, does not fail).speaker-diarization-coremlis a separate package fromfluidaudioin the declaredtables and must stay one.
logits_to_keepis tracked undermodel_tests/benchmark/patches/. Decide where a patch lives in the shipped tool, howverifyknows it is applied, and what--repairdoes when it is not.#12 — but the mechanism a
torchstage is invoked through from a core-environment process, and what crosses thatboundary. The recorded evidence used fresh subprocesses per stage
(
run_interview_pipeline.py), so a persistent worker is outside what was measured andneeds its own justification. Whatever it is, it has to make "no two model stages resident
at once" enforceable rather than aspirational.
audio doctor. Tool version and path, ffmpeg/ffprobe/swift/uv presence, platform,memory, disk, resolved root, per-environment and per-package state.
purge --dry-run, exit 3, and the integrity-failure path are all testable against a fabricatedroot. Decide what the fake root looks like and which parts genuinely require a live pull
(
livemarker already exists inpyproject.toml).The seam with #12
One table, two owners, and it must not become two tables.
kind,revision, digest, bytes, license,
requires_tool, and the(stack, want) → package setmapping that
pull --stack S --want ...andrun's exit-3 check both consume.planner that reads them.
(stack, requirements) → [package]. transcribe backend: adapter contract, result schema, stack table, planner, orchestration #12 produces it, thisissue consumes it. Until transcribe backend: adapter contract, result schema, stack table, planner, orchestration #12 lands, stub it — but stub it as the real function signature,
not as a hardcoded list inside the packages command, or the two will disagree exactly once
and silently.
Acceptance
torchresolution question is answered by a recorded resolver run, and VOCABULARY'senvironment table plus its "unverified" note are updated to match — including the split and
its reason if it splits.
registry.jsonhas a written schema, and a crashedpullcannot leave a root that readsas provisioned.
audio packages list | pull | verify | remove | purge | pathandaudio doctorproduce thekey sets in TRANSCRIBE_HAPPY_PATH §1.3 and §5 — including
environments_kept_reason,hub_cache_note,unsized_packages, and the non-blockinglicense_unreviewedwarning.removeonvibevoice-asr-7bkeepstorchwhile the aligner andfirered-asr2sneed it,and says so.
runbeforepullexits 3 withpackages_not_provisionedand afixline that, runverbatim, provisions exactly the missing set. A digest/size/revision mismatch exits 3 with
package_integrity_failedand--repair.uv run --extra dev pyteststays green; theenhancerender-from-original path is notregressed.
Traps
rule exists because three documents agreed on something no artifact supported. The
requirements-file-versus-venv divergence above is the same failure shape, one layer down.
nested objects for exactly this reason. If no command branches on a field, it is a note.
verifydeclares it checks appliedpatches, write the test that fails when a patch is reverted, and check that it can fail.