diff --git a/PLAN.md b/PLAN.md index 8e2b1ff..9008adc 100644 --- a/PLAN.md +++ b/PLAN.md @@ -126,7 +126,7 @@ Wichtig: - track - present - record -- Gemeinsame Arbitration-/Budget-Sicht für refinement/record/decode vorbereiten +- Gemeinsame Arbitration-/Budget-Sicht für refinement/record/decode zentralisieren (Admission + Queue/Hold + Debug-Surface) ### F. Dokumentierte Betriebsprofile - initiale Profile definieren, z. B.: diff --git a/README.md b/README.md index 57b2567..a9bbeb1 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ Edit `config.yaml` (autosave goes to `config.autosave.yaml`). - `resources.max_refinement_jobs` — processing budget hint - `resources.max_recording_streams` — recording/streaming budget hint - `resources.max_decode_jobs` — decode budget hint -- `resources.decision_hold_ms` — hold time for queue slots before churn +- `resources.decision_hold_ms` — baseline hold time for queue slots before churn (arbitration scales per profile/strategy) - `profiles[]` — named operating profiles/intent metadata In phase 1, the engine stays backward compatible, but the config model now reflects the intended separation between: @@ -95,6 +95,8 @@ In phase 1, the engine stays backward compatible, but the config model now refle - presentation - operator goals / future autonomous intent +Refinement plans now rank candidates, while a shared arbitration step admits refinement/record/decode work based on budgets and hold policy. + The long-term target is that you describe *what the system should do* (for example broad-span monitoring intent, preferred signal families, auto-record/decode priorities), while the engine decides *how* to allocate surveillance, refinement and decoding budgets. **CFAR modes:** `OFF`, `CA`, `OS`, `GOSCA`, `CASO` @@ -159,7 +161,7 @@ go build -tags sdrplay ./cmd/sdrd - `GET /api/gpu` - `GET /api/pipeline/policy` - `GET /api/pipeline/recommendations` -- `GET /api/refinement` → latest refinement plan/windows snapshot (includes `window_stats`, `queue_stats`, `decision_summary`, `decision_items`, `arbitration`, levels, request/context/budgets/work_items) +- `GET /api/refinement` → latest refinement plan/windows snapshot (includes `window_stats`, levels, request/context/work_items, plus `arbitration` with budgets/hold policy/refinement admission/queue/decision summary) ### Signals / Events - `GET /api/signals` → current live signals diff --git a/web/app.js b/web/app.js index 2a2f6c9..2f5c649 100644 --- a/web/app.js +++ b/web/app.js @@ -757,7 +757,7 @@ async function loadRefinement() { if (!res.ok) return; refinementInfo = await res.json(); decisionIndex = new Map(); - const items = Array.isArray(refinementInfo.decision_items) ? refinementInfo.decision_items : []; + const items = Array.isArray(refinementInfo.arbitration?.decision_items) ? refinementInfo.arbitration.decision_items : []; items.forEach(item => { if (item && item.id != null) decisionIndex.set(String(item.id), item); }); @@ -864,13 +864,13 @@ function updateHeroMetrics() { healthFps.textContent = `${renderFps.toFixed(0)} fps`; if (healthRefinePlan) { const plan = refinementInfo.plan || {}; - const decisionSummary = refinementInfo.decision_summary || {}; + const decisionSummary = refinementInfo.arbitration?.decision_summary || {}; const recOn = decisionSummary.record_enabled ?? 0; const decOn = decisionSummary.decode_enabled ?? 0; const reasonCounts = decisionSummary.reasons || {}; const topReason = Object.entries(reasonCounts).sort((a, b) => b[1] - a[1])[0]; const reasonText = topReason ? `· ${topReason[0]}` : ''; - const queueStats = refinementInfo.queue_stats || {}; + const queueStats = refinementInfo.arbitration?.queue || {}; const queueText = (queueStats.record_queued || queueStats.decode_queued) ? `· q ${queueStats.record_queued || 0}/${queueStats.decode_queued || 0}` : '';