From b68343dae1db2f2877dcdac39b276552fd7955dd Mon Sep 17 00:00:00 2001 From: ragnarok-clanker Date: Wed, 28 Oct 2026 16:33:19 +0100 Subject: [PATCH] Skipped functions are not regressions when their annotation is removed Once a function's no-decompile claim is accepted (task state skipped), removing its old annotation or changing its code no longer counts as breaking a function that matched before. This lets a maintenance batch clean up an annotation that was stacked on the wrong definition. --- src/server/verify.js | 3 ++- test/server.test.js | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/server/verify.js b/src/server/verify.js index d378628..b7c0fea 100644 --- a/src/server/verify.js +++ b/src/server/verify.js @@ -458,7 +458,8 @@ export class Verifier { const results = new Map([...bestResults(check)].map(([id, b]) => [id, b.f])); const compiledFiles = new Set(check.units.map((u) => u.file)); const out = []; - for (const row of this.store.all(`SELECT * FROM func_status WHERE status IN ('exact', 'match_unresolved') OR confirmed = 1`)) { + // An accepted "no decompile" claim retires the function: removing its old annotation is the fix. + for (const row of this.store.all(`SELECT * FROM func_status WHERE (status IN ('exact', 'match_unresolved') OR confirmed = 1) AND symbol_id NOT IN (SELECT symbol_id FROM tasks WHERE state = 'skipped')`)) { if (!compiledFiles.has(row.file)) continue; const now_ = results.get(row.symbol_id); // Exact and match_unresolved are the same bytes: whether a referenced label resolves depends diff --git a/test/server.test.js b/test/server.test.js index 9a64c7a..7441675 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -221,6 +221,20 @@ test('regressions are detected against func_status', () => { ctx.store.run(`DELETE FROM func_status`); }); +test('removing the annotation of a skipped function is not a regression', () => { + ctx.store.run(`INSERT INTO func_status (symbol_id, file, status, score, confirmed, commit_hash, updated_at) VALUES ('fn_00002', 'src/a.cpp', 'mismatch', 0.7, 1, 'x', 0)`); + const check = { annotationErrors: [], units: [{ file: 'src/a.cpp', compiled: true, lintErrors: [], functions: [{ id: 'fn_00002', status: STATUS.MISMATCH, score: 0.4 }], unannotated: [] }] }; + const task = ctx.store.get(`SELECT id, state FROM tasks WHERE symbol_id = 'fn_00002'`); + try { + assert.deepEqual(ctx.verifier.regressions(check).map((r) => r.id), ['fn_00002'], 'a confirmed function moving away from the original is a regression'); + ctx.store.run(`UPDATE tasks SET state = 'skipped' WHERE id = ?`, task.id); + assert.deepEqual(ctx.verifier.regressions(check), [], 'not once it is skipped as needing no decompilation'); + } finally { + ctx.store.run(`UPDATE tasks SET state = ? WHERE id = ?`, task.state, task.id); + ctx.store.run(`DELETE FROM func_status`); + } +}); + test('expired leases return to the pool', async () => { const token = await activeUser('dave'); const c = (await leaseTask(token)).body;