From 0f1b04bd7976ea416f11d83e4b7b78cf537bfdf3 Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Sun, 28 Jun 2026 13:18:25 +0900 Subject: feat(concurrency): configurable cooldown + adaptive-headroom auto-reduce banner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consumes the backend's concurrency-fixes (commit 2d27666) — additive to transport-contract@0.23.0, NO version bump. ConcurrencyStatusEntry gains cooldownMs / autoReduced / autoReducedFrom? / notice?; new ConcurrencyCooldownResponse / SetConcurrencyCooldownRequest + GET/PUT /concurrency/cooldown/:providerId. FE: - Pure core (logic/view-model.ts): DEFAULT_COOLDOWN_MS; parseCooldownInput (non-negative int — 0 is valid, unlike the limit); normalizeCooldown; cooldownLabel ("350ms"/"1.2s"/"0ms (off)"); viewConcurrencyStatus extended (cooldown + autoReduce fields; auto-reduce → warning badge, not busy); viewAutoReduce/autoReduceNotices (banner view — prefers backend notice, synthesizes a fallback); summarizeStatus "N auto-reduced" fragment; normalizeConcurrencyStatus coerces new fields (immutable readonly build; autoReducedFrom/notice only when autoReduced===true); normalizeConcurrencyCooldown. - Types (logic/types.ts): re-export the 2 new contract types + ConcurrencyCooldownResult + Get/SaveConcurrencyCooldown ports. - UI: ConcurrencyCooldownRow.svelte (inline-edit cooldown + Save → PUT, seeded via the ChatLimitField pattern); AutoReduceBanner.svelte (dismissible banner — backend notice + "Was N, now M." + "Restore to N"); ConcurrencyView renders the cooldown per status card + the banner section. The banner persists while autoReduced===true, is dismissible, and clears automatically once a poll shows autoReduced===false after a restore PUT. - Store (store.svelte.ts): getConcurrencyCooldown + setConcurrencyCooldown (surface 400/404/503 as ok:false; normalize at the seam) + interface decls. - App.svelte: saveConcurrencyCooldown adapter → ConcurrencyView. - Tests: +47 (view-model cooldown/auto-reduce/normalizers; component banner render, cooldown PUT, restore clears banner, dismiss; store cooldown GET/PUT). Re-synced the file: dep (bun install) + re-mirrored .dispatch/transport-contract. reference.md. typecheck 0/0, 1048 tests green (run twice), biome clean, build OK. Worktree env: an untracked dispatch-backend → backend symlink was created in the worktree parent so the canonical file:../dispatch-backend/... paths resolve (NOT committed — per the §2d/§2j worktree convention). --- src/app/App.svelte | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/app/App.svelte') diff --git a/src/app/App.svelte b/src/app/App.svelte index 59f949c..3e7d05a 100644 --- a/src/app/App.svelte +++ b/src/app/App.svelte @@ -76,6 +76,7 @@ type DeleteConcurrencyLimit, type LoadConcurrencyLimits, type LoadConcurrencyStatus, + type SaveConcurrencyCooldown, type SaveConcurrencyLimit, } from "../features/concurrency"; import type { ChatStore } from "../features/chat"; @@ -491,6 +492,8 @@ const deleteConcurrencyLimit: DeleteConcurrencyLimit = (providerId) => store.deleteConcurrencyLimit(providerId); const loadConcurrencyStatus: LoadConcurrencyStatus = () => store.concurrencyStatus(); + const saveConcurrencyCooldown: SaveConcurrencyCooldown = (providerId, cooldownMs) => + store.setConcurrencyCooldown(providerId, cooldownMs);
@@ -841,6 +844,7 @@ saveLimit={saveConcurrencyLimit} deleteLimit={deleteConcurrencyLimit} loadStatus={loadConcurrencyStatus} + saveCooldown={saveConcurrencyCooldown} /> {/if} {/snippet} -- cgit v1.2.3