feat(v2): add safe AI fallback and cache/reanalysis decision

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Gustavo
2026-08-24 15:15:50 -03:00
co-authored by Claude Sonnet 5
parent c445e00fc5
commit efba7792fa
4 changed files with 68 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
function needsReanalysis({ memory, currentHash, now }) {
if (!memory) return { needsAi: true, reason: 'no_cache' };
if (memory.last_context_hash !== currentHash) return { needsAi: true, reason: 'context_changed' };
if (memory.next_review_at != null && now >= memory.next_review_at) return { needsAi: true, reason: 'review_due' };
return { needsAi: false, reason: 'cache_valid' };
}
module.exports = { needsReanalysis };
+30
View File
@@ -0,0 +1,30 @@
const { test } = require('node:test');
const assert = require('node:assert/strict');
const { needsReanalysis } = require('./cache');
test('sem memoria previa, sempre precisa de IA', () => {
const result = needsReanalysis({ memory: null, currentHash: 'abc', now: 1000 });
assert.equal(result.needsAi, true);
assert.equal(result.reason, 'no_cache');
});
test('hash igual e sem next_review_at vencido usa cache', () => {
const memory = { last_context_hash: 'abc', next_review_at: null };
const result = needsReanalysis({ memory, currentHash: 'abc', now: 1000 });
assert.equal(result.needsAi, false);
assert.equal(result.reason, 'cache_valid');
});
test('hash diferente forca reavaliacao (mudanca significativa)', () => {
const memory = { last_context_hash: 'abc', next_review_at: null };
const result = needsReanalysis({ memory, currentHash: 'xyz', now: 1000 });
assert.equal(result.needsAi, true);
assert.equal(result.reason, 'context_changed');
});
test('next_review_at vencido forca reavaliacao mesmo com hash igual', () => {
const memory = { last_context_hash: 'abc', next_review_at: 500 };
const result = needsReanalysis({ memory, currentHash: 'abc', now: 1000 });
assert.equal(result.needsAi, true);
assert.equal(result.reason, 'review_due');
});
+18
View File
@@ -0,0 +1,18 @@
function buildFallbackClassification(reason) {
return {
classification: 'ACTIONABLE_NOW',
actionable_today: true,
confidence: 0,
blocker_type: null,
waiting_for: null,
next_action: 'Revisar chamado manualmente',
suggested_action_type: 'manual_review',
business_risk: 'medium',
reason: `Fallback deterministico: ${reason}`,
evidence: [],
is_fallback: true,
fallback_reason: reason,
};
}
module.exports = { buildFallbackClassification };
+12
View File
@@ -0,0 +1,12 @@
const { test } = require('node:test');
const assert = require('node:assert/strict');
const { buildFallbackClassification } = require('./fallback');
test('fallback e sempre seguro e marcado como tal', () => {
const result = buildFallbackClassification('ai_timeout');
assert.equal(result.is_fallback, true);
assert.equal(result.fallback_reason, 'ai_timeout');
assert.equal(result.confidence, 0);
assert.equal(result.classification, 'ACTIONABLE_NOW');
assert.ok(result.reason.includes('Fallback'));
});