fix(v2): WAITING_*/blocked classes never enter Top10 regardless of contextChanged
Real bug confirmed in the prior cycle's Run A: WAITING_VENDOR tickets
entered the Top10 solely because contextChanged=true (a fresh AI call
happened this run) -- contextChanged should only decide whether to
reanalyze (logic/cache.js), never whether an already-still-blocked
ticket can compete.
logic/cooldown.js: removed the `&& !contextChanged` condition from the
BLOCKING_CLASSIFICATIONS check in computeCooldown -- a ticket whose
CURRENT classification (fresh or cached) is in BLOCKING_CLASSIFICATIONS
is now excluded unconditionally. Also expanded BLOCKING_CLASSIFICATIONS
to the full list required this cycle: WAITING_REQUESTER, WAITING_VENDOR,
WAITING_APPROVAL, SCHEDULED, DEPENDENCY_BLOCKED, PROJECT_LONG_TERM,
DUPLICATE_OR_OBSOLETE (WAITING_APPROVAL/PROJECT_LONG_TERM/
DUPLICATE_OR_OBSOLETE previously only had a score penalty, not exclusion
-- WAITING_APPROVAL has positive base score points, so it could
genuinely win a Top10 slot before this fix). The critical-override escape
hatch (SLA vencido + prioridade alta, etc.) is untouched -- still the
only way a blocked classification can surface.
logic/cooldown.test.js: rewrote the one test that was actually asserting
the buggy behavior ("mudanca de contexto real desbloqueia mesmo em
estado de espera" -> now proves the opposite, with a comment explaining
why); added tests for the chronology-driven unblock case (ticket moves
out of a blocking classification entirely) and the two newly-blocking
classes.
logic/score.test.js: added end-to-end tests through computeScore()
proving WAITING_VENDOR and WAITING_APPROVAL are excludedFromTop10 even
with contextChanged=true.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
482b7ef48e
commit
e0f4b9a5d0
+13
-2
@@ -1,7 +1,12 @@
|
||||
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
|
||||
|
||||
// Classes que NUNCA disputam o Top10 operacional -- bloqueadas por definicao, nao por cache.
|
||||
// Lista expandida na missao "finalizacao para producao" (secao 10): WAITING_APPROVAL,
|
||||
// PROJECT_LONG_TERM e DUPLICATE_OR_OBSOLETE entraram aqui (antes so tinham penalidade de
|
||||
// score, o que deixava WAITING_APPROVAL -- pontuacao base positiva -- competir de verdade).
|
||||
const BLOCKING_CLASSIFICATIONS = new Set([
|
||||
'WAITING_REQUESTER', 'WAITING_VENDOR', 'SCHEDULED', 'DEPENDENCY_BLOCKED',
|
||||
'WAITING_REQUESTER', 'WAITING_VENDOR', 'WAITING_APPROVAL', 'SCHEDULED',
|
||||
'DEPENDENCY_BLOCKED', 'PROJECT_LONG_TERM', 'DUPLICATE_OR_OBSOLETE',
|
||||
]);
|
||||
|
||||
// Regra centralizada de "isso e critico o bastante pra furar uma exclusao normal" --
|
||||
@@ -53,7 +58,13 @@ function computeCooldown({ ticket, classification, contextChanged, memory, now,
|
||||
} else if (nextReviewAt != null && now < nextReviewAt && !contextChanged) {
|
||||
blocked = true;
|
||||
reason = 'waiting_next_review';
|
||||
} else if (BLOCKING_CLASSIFICATIONS.has(classification) && !contextChanged) {
|
||||
} else if (BLOCKING_CLASSIFICATIONS.has(classification)) {
|
||||
// BUG REAL comprovado na Run A anterior: WAITING_VENDOR entrou no Top10 so porque o
|
||||
// contexto tinha mudado nesta execucao (contextChanged=true). contextChanged decide SE
|
||||
// reanalisamos (logic/cache.js) -- nunca decide se um chamado que CONTINUA classificado
|
||||
// numa classe bloqueada pode competir. Por isso este bloqueio e incondicional aqui: se a
|
||||
// classificacao atual (fresca ou cacheada, tanto faz) esta em BLOCKING_CLASSIFICATIONS,
|
||||
// fica fora do Top10, ponto. Missao "finalizacao para producao", secao 10.
|
||||
blocked = true;
|
||||
reason = `blocked_${classification.toLowerCase()}`;
|
||||
}
|
||||
|
||||
+28
-1
@@ -23,12 +23,39 @@ test('enviado ontem sem mudanca recebe penalidade crescente', () => {
|
||||
assert.equal(result.blocked, false);
|
||||
});
|
||||
|
||||
test('mudanca de contexto real desbloqueia mesmo em estado de espera', () => {
|
||||
test('BUG REAL corrigido: WAITING_VENDOR continua bloqueado do Top10 mesmo com contextChanged=true -- missao "finalizacao para producao" secao 10', () => {
|
||||
// Antes deste ciclo, contextChanged=true desbloqueava um chamado que CONTINUAVA
|
||||
// classificado como WAITING_VENDOR apos a reanalise -- foi exatamente o que aconteceu na
|
||||
// Run A anterior. contextChanged so deve decidir SE reanalisamos (logic/cache.js), nunca
|
||||
// SE um chamado ainda bloqueado pode competir no Top10.
|
||||
const memory = { consecutive_days_sent: 2, last_sent_at: NOW - ONE_DAY, next_review_at: NOW + ONE_DAY };
|
||||
const result = computeCooldown({ ticket: ticket({}), classification: 'WAITING_VENDOR', contextChanged: true, memory, now: NOW, config: CONFIG });
|
||||
assert.equal(result.blocked, true);
|
||||
assert.equal(result.reason, 'blocked_waiting_vendor');
|
||||
});
|
||||
|
||||
test('chamado sai de WAITING_VENDOR para ACTIONABLE_NOW apos evento real (cronologia) -- deixa de ser bloqueado', () => {
|
||||
// A reclassificacao em si (peca chegou, virou ACTIONABLE_NOW) e responsabilidade do prompt/
|
||||
// IA; aqui validamos que, uma vez que a classificacao ja NAO esta mais em
|
||||
// BLOCKING_CLASSIFICATIONS, o cooldown para de bloquear -- sem depender de contextChanged.
|
||||
const memory = { consecutive_days_sent: 2, last_sent_at: NOW - ONE_DAY, next_review_at: NOW + ONE_DAY };
|
||||
const result = computeCooldown({ ticket: ticket({}), classification: 'ACTIONABLE_NOW', contextChanged: true, memory, now: NOW, config: CONFIG });
|
||||
assert.equal(result.blocked, false);
|
||||
});
|
||||
|
||||
test('WAITING_APPROVAL tambem fica fora do Top10 incondicionalmente -- classe adicionada nesta missao', () => {
|
||||
const result = computeCooldown({ ticket: ticket({}), classification: 'WAITING_APPROVAL', contextChanged: true, memory: null, now: NOW, config: CONFIG });
|
||||
assert.equal(result.blocked, true);
|
||||
assert.equal(result.reason, 'blocked_waiting_approval');
|
||||
});
|
||||
|
||||
test('PROJECT_LONG_TERM e DUPLICATE_OR_OBSOLETE tambem ficam fora do Top10 incondicionalmente', () => {
|
||||
const r1 = computeCooldown({ ticket: ticket({}), classification: 'PROJECT_LONG_TERM', contextChanged: true, memory: null, now: NOW, config: CONFIG });
|
||||
const r2 = computeCooldown({ ticket: ticket({}), classification: 'DUPLICATE_OR_OBSOLETE', contextChanged: true, memory: null, now: NOW, config: CONFIG });
|
||||
assert.equal(r1.blocked, true);
|
||||
assert.equal(r2.blocked, true);
|
||||
});
|
||||
|
||||
test('NEEDS_COORDINATION sempre bloqueado para o Top 10 diario', () => {
|
||||
const result = computeCooldown({ ticket: ticket({}), classification: 'NEEDS_COORDINATION', contextChanged: false, memory: null, now: NOW, config: CONFIG });
|
||||
assert.equal(result.blocked, true);
|
||||
|
||||
@@ -89,3 +89,14 @@ test('INSUFFICIENT_CONTEXT critico entra somente pela excecao definida', () => {
|
||||
assert.equal(result.excludedFromTop10, false);
|
||||
assert.equal(result.decisionReason, 'insufficient_context_critical_override');
|
||||
});
|
||||
|
||||
test('BUG REAL comprovado na Run A anterior: WAITING_VENDOR fica fora do Top10 mesmo com contextChanged=true -- missao "finalizacao para producao" secao 10', () => {
|
||||
const result = computeScore({ ticket: ticket({}), aiClassification: ai({ classification: 'WAITING_VENDOR', actionable_today: false, confidence: 0.8 }), memory: null, contextChanged: true, config: CONFIG, now: NOW });
|
||||
assert.equal(result.excludedFromTop10, true);
|
||||
assert.equal(result.exclusionReason, 'blocked_waiting_vendor');
|
||||
});
|
||||
|
||||
test('WAITING_APPROVAL (pontuacao base positiva) tambem fica fora do Top10 -- antes so tinha penalidade de score, nao exclusao', () => {
|
||||
const result = computeScore({ ticket: ticket({}), aiClassification: ai({ classification: 'WAITING_APPROVAL', actionable_today: false, confidence: 0.8 }), memory: null, contextChanged: true, config: CONFIG, now: NOW });
|
||||
assert.equal(result.excludedFromTop10, true);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user