Part 3 of the capacity simplification, and the half that removes the
**knob**. Enforcement (shared semaphore, runtime wiring) went in #2509;
this removes everything an operator or API client can still see, so
nothing is left readable-but-ignored.
## Deleted
Settings key + schema default · CentralCore’s
`getGlobalConcurrencyState` / `updateGlobalConcurrency` /
`acquireGlobalSlot` / `releaseGlobalSlot` and the `concurrency:changed`
event · the whole Global Concurrency block in `async-central-core` ·
`PUT /api/global-concurrency` · the Scheduling · Global settings section
· the footer and Command Center global sliders · the dead
`getGlobalConcurrencyLimit` reader whose only caller went in #2509.
## Kept, deliberately
**`GET /api/global-concurrency` survives as telemetry only** — live
`currentlyActive` / `projectsActive` from CentralCore’s side-effect-safe
source. “How busy is this machine?” is still a real question once the
cap that used to answer it is gone. It no longer reports
`globalMaxConcurrent`/`queuedCount`: those came from the deleted cap and
from slot bookkeeping production code never incremented, so publishing
them was publishing zeros dressed as state.
**`useGlobalConcurrency` becomes read-only.** Everything that existed to
*persist* went with the cap — the 500 ms debounce, the save-state
machine, the commit-on-close/unmount flush, the slider clamp, the
`interactive` gate. The module-level shared store is **kept**: its
original justification (two mounted consumers drift apart with private
copies) holds for a polled read exactly as it did for a cap, and one
fetch now serves both.
The live “N running (all projects)” readout survives in both surfaces,
moved onto the per-project row.
## Two sections become one
Scheduling · Global existed to host exactly one control. With it deleted
the section renders an empty pane, so the Global/Project pair merges
back into **“Scheduling”**. An empty nav entry is a promise of settings
that are not there.
## One real fix found on the way
`SchedulingSection`’s `concurrencyLoading` gated the **project**
concurrency inputs on the **global**-concurrency fetch — never the right
source, since `maxConcurrent` and `maxWorktrees` come from the settings
form. It is repointed at the form’s own load, preserving the invariant
it existed for: a concurrency input stays disabled until its live value
arrives, so an operator cannot overwrite a resolved limit with a blank
fallback.
## Migration
A stored `globalMaxConcurrent` is **ignored** — it is a project-blob key
nothing reads, so dropping it needs no schema change. The
`central.global_concurrency` **table** is dropped in a follow-up; this
slice stops seeding and reading it first, so that drop has no live
writer to race.
## Verification, and how the wider suite was controlled
`pnpm lint` clean · core/engine/dashboard `tsc` clean · `pnpm test:gate`
green (309 + 10 + 71) · dashboard settings/footer/command-center/hooks
**2237/2237** · core `central-core-backend` 9/9.
The broader dashboard suite shows failures, and I checked rather than
assumed: running the suspect files on **clean main** reproduces
`api-git` (49), `TaskDetailModal.rendering` (28) and `settings-mobile`
(17) identically. Two were genuinely mine —
`SettingsModal.scheduling-merge` (0 on main, 17 on this branch: my nav
rename) and one `settings-mobile` picker case asserting `scheduling` is
a scoped pair — and both are fixed.
Tests for deleted behaviour are removed with it (footer
confirm/cancel/flush/dedupe, global marker geometry, the hook’s PUT
case, the CentralCore slot cases), each carrying a note on what it
guarded and where the surviving **project-side** equivalent lives.
Fixture-only references were updated, not deleted.
Nothing booted.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Double-commit race: null pendingValueRef synchronously at commit() start
so the close-flush + unmount-cleanup guards can't both fire and send a
duplicate PUT for the same value.
- Clamp ceiling: clamp setValue against max(SLIDER_BASE_MAX, currentValue)
instead of Number(raw) (which had no effective upper bound for
programmatic callers).
- Out-of-band staleness: force-revalidate on each activate so a Settings-modal
save (which writes globalMaxConcurrent directly, bypassing this store) is
reflected when a slider surface reopens.
- Load-error label: add an explicit error branch in both EngineControlMenu
and CommandCenterControls so a failed initial load shows "Load failed"
instead of "Ready" while the slider is disabled.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a "Global Max Concurrent" slider to the footer engine menu and the
Command Center Concurrency card, and group the Scheduling settings by
Global vs Project scope so the global cap isn't mistaken for a per-project
setting (clearer on mobile).
Both sliders are backed by a single shared `useGlobalConcurrency` hook
(module-level store) so they read/write one source of truth and revalidate
after every PUT /api/global-concurrency — fixing the last-writer-wins and
stale-clobber races a per-component cache would cause. The hook treats a
fetch error as non-interactive (slider disabled, not stuck at 1), surfaces
a save-state indicator, and flushes a pending edit on menu close / unmount
so a quick drag-then-dismiss is never silently dropped.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Raise the footer engine control sliders to support higher concurrency without hiding persisted values.
- Increase max tasks, max triage, and max worktree slider baselines to 50.
- Preserve value-aware slider maxima for persisted settings above the new baseline.
- Add coverage for in-range 50 limits, above-range persisted values, and saving a value of 50.
Files changed:
.../dashboard/app/components/EngineControlMenu.tsx | 9 ++--
.../__tests__/EngineControlMenu.test.tsx | 50 +++++++++++++++++++---
2 files changed, 49 insertions(+), 10 deletions(-)
Fusion-Task-Id: FN-6863
Fusion-Task-Lineage: f42373fc-322b-4d20-9b8b-2d0c63254e79
Keep the footer engine controls popover opaque by using valid dashboard CSS tokens.
- Replace the undefined elevated surface token with the solid card surface.
- Align disabled action opacity, range accent, and mobile height CSS with defined tokens/values.
- Add a CSS token regression test for the engine control menu popover.
Files changed:
.../dashboard/app/components/EngineControlMenu.css | 8 +-
.../dashboard/app/components/EngineControlMenu.tsx | 3 +
.../__tests__/EngineControlMenu.css.test.ts | 99 ++++++++++++++++++++++
3 files changed, 106 insertions(+), 4 deletions(-)
Fusion-Task-Id: FN-6862
Fusion-Task-Lineage: 5e41177a-6fc5-48d0-861d-fefbcfca7060