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
The capability half of the stack table is prose in two documents today. It has to become data,
and that data is what capabilities and plan serialise. This phase is pure functions over that
table plus a metadata probe, so it ships two commands and every refusal without a single model
being loaded.
Scope
stacks.json, stacks.py, planner.py, refusals.py, catalog.py, plan.py, and audio transcribe capabilities / audio transcribe plan. Neither reads media beyond probe_media, neither provisions anything, neither touches the network.
The table
One cell per (stack, capability), resolving to exactly one of five values, plus the per-stack
columns the catalog needs: family, environment, the roles sentence, unit and unit-count rule,
failure recovery, cost.proved with its rate and fixture, the language vocabulary, and the
one-line characterization stack_required prints.
Cell
Catalog availability
Plan satisfaction
Requesting it
native
native
native
adds nothing
native_stage:<stage>
native
native with stage
adds cost, not composition
add_on:<package>
requires_add_on
derived with backend
adds a role and a package
unsatisfiable_on_stack
impossible + reason
—
exit 2, allowed non-empty
unsupported
impossible + reason
—
exit 2, allowed: []
Both refusal codes fall out of whether allowed is empty, which is what keeps them from being
rendered alike. VOCABULARY's derivation table stays the source of truth and the test is
parametrized over it.
stacks.json is a separate file from manifest.json, deliberately: environments/__init__.py
states in its own docstring that the capability half is not there, and two files with a
cross-check test — every stack named in one appears in the other, every package a cell names
exists — is two owners of one table rather than two tables.
--language
Accepted on the two Qwen stacks only, as a closed enum of the 30 declared names, matched
case-insensitively, passed through verbatim. _build_prompt falls back to the raw string when a
name misses (mlx-audio 0.4.5 qwen3_asr.py:926-929, guarded by the manifest's source hash), so --language EN would interpolate language EN<asr_text> into the prompt with no error — a flag
that parses and silently changes the decode. The accepted set is closed and readable from config.json:support_languages, identical in both pinned checkpoints, so refusing needs no
translation table and no alias map. did_you_mean turns EN into a suggestion the caller
retypes, never a silent resolution.
The hint never reaches the aligner; see the Qwen phase issue for why.
Refusals
All thirteen, printed bare on stderr as the documents show. The shipped commands keep their {"error": …} envelope; the divergence is recorded in the plan document rather than silently
becoming a third convention.
Out of scope
run, the transport, adapters, any stage script. sample_output comes from the phase-A
serializer; if that is not merged, this phase is blocked on it.
Acceptance
capabilities and plan output diffed key-for-key against HAPPY_PATH §1.1, §1.2, §2.1 and
§3.1 using the shape() comparison tests/test_shipped_commands_match_the_document.py already
defines, extended to the new commands. That comparison is the one that caught three shipped
commands drifting from the document.
Every refusal in HAPPY_PATH §4.1–4.7 and CONTRACT §5 reproduced field-for-field against the
per-code table, with capability_unknown, capability_unsatisfiable_on_stack, and capability_unsupported distinct rather than collapsed.
--language EN returns option_value_unsupported with did_you_mean: "English"; --language english resolves to English; --language Cantonese on vibevoice returns option_unsupported_on_stack with stacks_accepting: ["qwen-1.7b", "qwen-0.6b"].
The 30 names equal the provisioned checkpoint's support_languages when the mlx
environment exists, and the test skips when it does not — the pattern the private-API hash and
the byte counts already use.
A plan carries no outcomes, and an omitted --want resolves the floors-only request
rather than the menu.
Exit 2 for every request error happens before any provisioning check, so no refusal can
reach exit 3 first.
uv run --extra dev pytest green.
Depends on
#18 — option_value_unsupported, the 30 accepted language names, and the corrected §3.1 residency all land there.
#19 — sample_output is that serializer; this phase cannot ship without it.
Context
Decomposes #12, phase B of
TRANSCRIBE_IMPLEMENTATION_PLAN.md.
The capability half of the stack table is prose in two documents today. It has to become data,
and that data is what
capabilitiesandplanserialise. This phase is pure functions over thattable plus a metadata probe, so it ships two commands and every refusal without a single model
being loaded.
Scope
stacks.json,stacks.py,planner.py,refusals.py,catalog.py,plan.py, andaudio transcribe capabilities/audio transcribe plan. Neither reads media beyondprobe_media, neither provisions anything, neither touches the network.The table
One cell per (stack, capability), resolving to exactly one of five values, plus the per-stack
columns the catalog needs: family, environment, the
rolessentence, unit and unit-count rule,failure recovery,
cost.provedwith its rate and fixture, the language vocabulary, and theone-line characterization
stack_requiredprints.availabilitysatisfactionnativenativenativenative_stage:<stage>nativenativewithstageadd_on:<package>requires_add_onderivedwithbackendunsatisfiable_on_stackimpossible+reasonallowednon-emptyunsupportedimpossible+reasonallowed: []Both refusal codes fall out of whether
allowedis empty, which is what keeps them from beingrendered alike. VOCABULARY's derivation table stays the source of truth and the test is
parametrized over it.
stacks.jsonis a separate file frommanifest.json, deliberately:environments/__init__.pystates in its own docstring that the capability half is not there, and two files with a
cross-check test — every stack named in one appears in the other, every package a cell names
exists — is two owners of one table rather than two tables.
--languageAccepted on the two Qwen stacks only, as a closed enum of the 30 declared names, matched
case-insensitively, passed through verbatim.
_build_promptfalls back to the raw string when aname misses (
mlx-audio 0.4.5 qwen3_asr.py:926-929, guarded by the manifest's source hash), so--language ENwould interpolatelanguage EN<asr_text>into the prompt with no error — a flagthat parses and silently changes the decode. The accepted set is closed and readable from
config.json:support_languages, identical in both pinned checkpoints, so refusing needs notranslation table and no alias map.
did_you_meanturnsENinto a suggestion the callerretypes, never a silent resolution.
The hint never reaches the aligner; see the Qwen phase issue for why.
Refusals
All thirteen, printed bare on stderr as the documents show. The shipped commands keep their
{"error": …}envelope; the divergence is recorded in the plan document rather than silentlybecoming a third convention.
Out of scope
run, the transport, adapters, any stage script.sample_outputcomes from the phase-Aserializer; if that is not merged, this phase is blocked on it.
Acceptance
capabilitiesandplanoutput diffed key-for-key against HAPPY_PATH §1.1, §1.2, §2.1 and§3.1 using the
shape()comparisontests/test_shipped_commands_match_the_document.pyalreadydefines, extended to the new commands. That comparison is the one that caught three shipped
commands drifting from the document.
per-code table, with
capability_unknown,capability_unsatisfiable_on_stack, andcapability_unsupporteddistinct rather than collapsed.--language ENreturnsoption_value_unsupportedwithdid_you_mean: "English";--language englishresolves toEnglish;--language Cantoneseonvibevoicereturnsoption_unsupported_on_stackwithstacks_accepting: ["qwen-1.7b", "qwen-0.6b"].support_languageswhen themlxenvironment exists, and the test skips when it does not — the pattern the private-API hash and
the byte counts already use.
outcomes, and an omitted--wantresolves the floors-only requestrather than the menu.
reach exit 3 first.
uv run --extra dev pytestgreen.Depends on
option_value_unsupported, the 30 accepted language names, and the corrected §3.1 residency all land there.sample_outputis that serializer; this phase cannot ship without it.