From e52ba2921c7cdcb5b2f66a149ce756ae35e164f2 Mon Sep 17 00:00:00 2001 From: Vigilio Desto Date: Tue, 7 Apr 2026 23:17:39 +0000 Subject: [PATCH] Fix recommend.js: include allowed_warning in provider selection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit allowed_warning providers can serve requests — only the budget is approaching its limit. Previously they were excluded from both Phase 1 and Phase 2 selection, causing unnecessary escalation to shelley-proxy emergency fallback when team-vigilio was at 79% 7d (allowed_warning) and team-ludo was showing invalid_key in the health-pulse cache. Now: - Phase 1: first provider under threshold with status allowed or allowed_warning - Phase 2: lowest-utilization provider with either status; reason notes warning Effect: next wake picks team-vigilio (79% 7d, warning) instead of shelley-proxy. Shelley-proxy is now a true last resort again. --- recommend.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/recommend.js b/recommend.js index 8615dfa..d582465 100644 --- a/recommend.js +++ b/recommend.js @@ -6,10 +6,10 @@ * Returns the best Teams provider considering 7d budget utilization. * * Selection rules: - * 1. status must be "allowed" + * 1. status must be "allowed" or "allowed_warning" (both can serve requests) * 2. Scan chain in order; take first with utilization_7d < SWITCH_THRESHOLD - * 3. If none under threshold, take lowest-utilization allowed provider - * 4. If no allowed Teams providers, return emergency=true (shelley-proxy) + * 3. If none under threshold, take lowest-utilization allowed/allowed_warning provider + * 4. If no usable Teams providers, return emergency=true (shelley-proxy) * * Usage: * node recommend.js # JSON output @@ -90,10 +90,11 @@ async function main() { .filter(name => providers[name] && providers[name].type === 'teams-direct') .map(name => ({ name, ...providers[name] })); - // Phase 1: first provider under threshold with status=allowed + // Phase 1: first provider under threshold with status=allowed or allowed_warning + // Both statuses can serve requests; allowed_warning just means approaching limit let best = null; for (const p of candidates) { - if (p.status === 'allowed') { + if (p.status === 'allowed' || p.status === 'allowed_warning') { const util7d = p.utilization_7d ?? 0; if (util7d < SWITCH_THRESHOLD) { best = { @@ -105,12 +106,13 @@ async function main() { } } - // Phase 2: all over threshold — pick lowest 7d utilization that's still allowed + // Phase 2: all over threshold — pick lowest 7d utilization (allowed or allowed_warning) + // Prefer allowed over allowed_warning; both beat emergency fallback if (!best) { let lowestUtil = Infinity; let lowestCandidate = null; for (const p of candidates) { - if (p.status === 'allowed') { + if (p.status === 'allowed' || p.status === 'allowed_warning') { const util7d = p.utilization_7d ?? 0; if (util7d < lowestUtil) { lowestUtil = util7d; @@ -119,9 +121,10 @@ async function main() { } } if (lowestCandidate) { + const warningTag = lowestCandidate.status === 'allowed_warning' ? ', warning' : ''; best = { name: lowestCandidate.name, - reason: `all over threshold — best available at ${pct(lowestUtil)} 7d (data: ${source})`, + reason: `all over threshold — best available at ${pct(lowestUtil)} 7d${warningTag} (data: ${source})`, }; } }