diff --git a/manifold/ONBOARDING.md b/manifold/ONBOARDING.md
index ac4696d..abcda42 100644
--- a/manifold/ONBOARDING.md
+++ b/manifold/ONBOARDING.md
@@ -122,7 +122,8 @@ double-click exits). `OutputStage.tsx` is the output columns; drag a bar to set
defines `OUTPUT_MODES` = **particles** (default) / midi / osc / cv / synth / editor, each mapping to a
`BackendId`. `DEFAULT_OUTPUT_MODE='particles'`. `outputDisplayCount()` is the shared presentation
boundary for the stage and routing rows: MIDI uses its configured CC count, while backends without
- a separate count present the full mode parameter set. This does not reshape the MLP or clear examples.
+ a separate count present the full mode parameter set. The condensed Outputs panel reports this as an
+ `N outputs` chip. This does not reshape the MLP or clear examples.
- `src/console/output-mode.ts`, `types.ts`, `model.ts` are the shared vocabulary — read these first
when touching anything cross-cutting:
- `types.ts`: `Focus`, `OutputMode`, `DrawerKey`, `DrawerDepth`, `FeedbackModeUI`, `SoloMode`,
diff --git a/manifold/src/console/Drawers.tsx b/manifold/src/console/Drawers.tsx
index b8fed14..bfe8112 100644
--- a/manifold/src/console/Drawers.tsx
+++ b/manifold/src/console/Drawers.tsx
@@ -624,6 +624,7 @@ function RoutingDrawer(ctx: ConsoleCtx, depth: DrawerDepth) {
<>
{modeDesc.label}
+ {activeParams.length} output{activeParams.length === 1 ? '' : 's'}
live {counts.live || 0}
fixed {counts.fixed || 0}
off {counts.off || 0}
diff --git a/manifold/tests/e2e/output-display-count.spec.ts b/manifold/tests/e2e/output-display-count.spec.ts
index cef0785..fea7fbd 100644
--- a/manifold/tests/e2e/output-display-count.spec.ts
+++ b/manifold/tests/e2e/output-display-count.spec.ts
@@ -11,18 +11,25 @@ declare global {
test('output sliders follow the active backend output count', async ({ page }) => {
await loadProbe(page);
- await page.evaluate(() => {
- window.__mf!.setOutputMode('midi');
- window.__mf!.setMidiCcCount(3);
- });
- await expect(page.getByTestId('output-stage')).toHaveAttribute('data-output-count', '3');
+ // Drive the real UI path: output target → Outputs drawer → expanded MIDI
+ // config. This deliberately does not use the debug setters; the regression
+ // covers the same interaction an operator performs.
+ await page.getByTitle(/^Mode:/).click();
+ await page.getByRole('button', { name: 'MIDI', exact: true }).click();
+ await page.getByTitle('Outputs').click();
+ await page.getByTitle('Expand').click();
+ await page.getByLabel('CCs').fill('2');
- await page.evaluate(() => window.__mf!.setMidiCcCount(7));
- await expect(page.getByTestId('output-stage')).toHaveAttribute('data-output-count', '7');
+ // The count remains visible in condensed chrome after leaving the advanced
+ // config, and closing the drawer reveals the same number of stage columns.
+ await page.getByTitle('Condense').click();
+ await expect(page.getByText('2 outputs', { exact: true })).toBeVisible();
+ await page.getByTitle('Close').click();
+ await expect(page.getByTestId('output-stage')).toHaveAttribute('data-output-count', '2');
- const fullCount = await page.evaluate(() => {
- window.__mf!.setOutputMode('osc');
- return window.__mf!.paramCount();
- });
+ // Backends without a configured count present the full mode output set.
+ const fullCount = await page.evaluate(() => window.__mf!.paramCount());
+ await page.getByTitle(/^Mode:/).click();
+ await page.getByRole('button', { name: 'OSC', exact: true }).click();
await expect(page.getByTestId('output-stage')).toHaveAttribute('data-output-count', String(fullCount));
});