Refuse a source with more than two channels #1681
Description
Refuse a source with more than two channels
Owner answer (2026-10-15): sources are mono or stereo only. Decision 17 programme. Code anchors verified on 4be10cd67 (PR #1680). Issue number: assigned at filing (written here as #1681). Feature sprint mode (owner, 2026-10-13): one implementer, normal tests, a batch review.
Product outcome
A session whose source declares more than 2 channels is refused at session load with a typed
error, the same on both hosts, and the browser's source staging is sized for 2 channels (about
1 KB instead of the 130 KB the grammar's 255-channel domain needs).
Context
session::SOURCE_CHANNELS_MAXIMUMisu8::MAX(crates/session/src/model.rs:217), the
domain ofSource::channels; nothing narrower is validated.- #1562b's review fold (B1) sized the browser's source PCM and plane staging by that maximum
(hosts/host-web/src/lib.rs,project_buffers), so a rebuild can add any valid source. - Product rule (dual mono by source file): only mono source files get dual-mono processing; stereo
files stay stereo. No source has more than 2 channels.
Decisions frozen for this slice
- D1. One rule in the session validator:
channelsis 1 or 2; any other value is refused with a
typed code (for examplesession.source.channels), at load and in a structural edit's
validation, so both hosts refuse it the same way. - D2. A launch constant (for example
SOURCE_CHANNELS_LAUNCH_MAXIMUM = 2) in the session crate
is the one source of the limit; the browser staging, the C ABI's source checks and the SDK
builder read it (the SDK through its generated constants). - D3. The browser's staging is sized by D2.
Objective gates
- A session with a 3-channel source is refused at load on both hosts with the typed code; a
structural edit that adds one is refused before commit; a parity row. - Mono and stereo sources load, feed and render as today (existing tests green).
- Browser
sourcePcmStagingBytesfalls back to the 2-channel size;expected.jsonand
retained_ceilings.rsre-pinned from measurements; the boot budget passes. cargo test --locked -p session -p session-validator -p capi -p host-web --features host-web/test-support; SDK checks; the author-session skill andSESSION_SCHEMA_V1.mdstate
the limit.
Authorized paths
crates/session/src/{model.rs,validate.rs,diagnostic.rs},crates/session/tests/,
hosts/host-web/src/lib.rs(the staging size), host-web tests andexpected.jsonrows,
crates/capitests,sdk/generated constants and evals,docs/SESSION_SCHEMA_V1.md,
.claude/skills/author-session/SKILL.md, this spec's record.
Dependencies
Size
Half a day.
Decision record: refuse a source with more than two channels (batch 8)
Shipped.
- D1. One rule in
session::validate:Source::channelsis 1 or 2. Zero and more than two are one
rule with one new typed code,DiagnosticCode::SourceChannelsUnsupported
(source.channels.unsupported, path$.sources[i].channels). It replaces the old
capacity.zeroforchannels == 0(frames keepcapacity.zero). Every load and every
structural edit already validates the candidate model through this function, so both hosts
refuse alike. - D2.
session::SOURCE_CHANNELS_LAUNCH_MAXIMUM = 2replacesSOURCE_CHANNELS_MAXIMUM(255,
deleted: nothing else read it). Readers: the validator, the browser's staging
(hosts/host-web/src/lib.rsproject_buffers), the C ABI's source submission
(crates/capi/src/ffi.rs: the plane-count bound and the stack plane array, 255 to 2, through
host_core::SOURCE_CHANNELS_LAUNCH_MAXIMUM, a re-export besideLAUNCH_SAMPLE_RATES) and the
SDK (new ABI-layout constantmaximumSourceChannels, written byparameter-metadata, read by
the session builder). - D3. The browser's source PCM and plane staging are sized by the constant:
sourcePcmStagingBytes
130,560 to 1,024. The ID staging keeps its 127 bytes (#1562b B1).
Departure. HostPrepareCaps::maximum_source_channels stays None everywhere: the validator is
the one rule, a second per-host cap would be a second place to drift.
C ABI. A chunk with three or more planes is now RESULT_INVALID_ARGUMENT at the boundary
(it was source.channels.mismatch from the facade); the row "three planes for a stereo source" in
source_rejections_reach_the_c_host_as_their_own_diagnostic became "one plane for a stereo source"
and a separate assertion covers three planes.
Fixtures and corpora. No fixture under fixtures/ declares a source of more than two channels.
Tests that did: host-web three_source_session (alpha, 4 channels, now 1) and
plan_exchange_threads (a six-channel source, now a stereo one; renamed
a_rebuild_adds_the_longest_id_and_feeds_the_new_source; #1562's record still names the old test
and is history). hosts/host-web/tests/stem-pump-v1.mjs pumps a 3-channel PCM file through the
stem pump: it is the pump's own channel-generic test and builds no session, so it stays.
Gates.
control_parity::both_hosts_refuse_a_source_of_more_than_two_channels_alike: a three-channel
source is refused at load by the C ABI compile and the browser boot with identical diagnostics
containingsource.channels.unsupported; mono and stereo load; a structuralUpsertSourceof
three channels is refused on both with identical reply bytes, the code
source.channels.unsupportedand the revision unmoved (four rates). Session crate:
diagnostic_parity(source-channels-zero,source-channels-three) andinvalid_matrix
(source_identity_and_shape_category_has_22_distinct_cases).- Mono and stereo unchanged: the existing host-web, capi, control-plane and conformance suites.
- Browser
expected.json:sourcePcmStagingBytes1,024,bridgeRetainedBytesceiling 1,812,352
to 1,667,648 (measured 1,611,330 on the simd128 module; 562,754 beyond the capture, plus 10 %,
rounded up to 64), the threememoryBytes3,014,656 to 2,883,584.retained_ceilings.rs
bridge_retained_bytes787,968 to 641,024 (measured 1,631,294 x86-64, 582,718 beyond the
capture). Boot high-water growth (check-web-boot-budget.mjs): 20,447,232 bytes (before:
20,447,232). - SDK:
builder-evals"a source of other than one or two channels is refused at its own path";
sdk/src/generated/abi.tsandsdk/assets/miso-engine-v1-abi-layout.jsonregenerated;
scripts/check-abi-layout-v1.pyand its self-test fixture knowmaximumSourceChannels.
docs/SESSION_SCHEMA_V1.mdand the author-session skill state the limit.
Found in review of callers. crates/host-core/src/limiter_linked_session.rs (a #[cfg(test)]
unit test, the 64-track linked-limiter corpus) widened its one source to 128 channels. It now
declares 64 stereo sources (stream-NN), track k reads source k's two channels and the same
noise streams 2k and 2k + 1 feed them; maximum_sources 16 to 64. The PIN digest is
unchanged and the three width tests pass, so no rendered bit moved.
Activity
Merge pull request #1684 from misofm/codex/controls-batch-13a