Skip to content

Commit 3f1932b

Browse files
terafinclaude
andauthored
fix(noise-classifier): stop rejecting 007/Bond games as numbered slot artifact
The noise classifier in system_detection.go used the regex `^\s*[0-9]{1,3}\s*-\s*[A-Za-z]` to detect emulator save-slot filenames like "1 - Save Slot", "02 - Backup". This was over-broad — it also matched real game titles that happen to start with 1-3 digits followed by " - ", most notably the entire James Bond franchise: 007 - Everything or Nothing 007 - The World Is Not Enough 007 - GoldenEye 007 - Agent Under Fire ... Repro from a live deploy (2026-06-07): SGM-Helper on a Steam Deck attempting to upload `007 - Everything or Nothing (USA, Europe).srm` gets HTTP 422 with body: {"error":"Unprocessable Entity", "message":"unsupported or unrecognized save format; only known consoles/arcade are allowed", "reason":"numbered mission/save-slot artifact"} The .srm has a valid `3500DNOB` EA save header and 103 non-zero bytes of real save data — definitely not a slot artifact. Fix: scope the regex to require a slot-like keyword after the dash (autosave|save|slot|auto|backup|mission|player|file|state|memory|sp\d*), matching the actual naming patterns emulators (PCSX2, mGBA, etc) use for their numbered slot files while leaving real game titles alone. Adds a comprehensive test covering 12 true-positives (slot artifacts the classifier MUST keep rejecting) and 10 false-positives (real game titles the classifier MUST now accept), including the full 007 series and other digit-prefixed real games like "3 - Count Bout", "4 - Wheel Thunder". Co-authored-by: terafin <terafin@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent fa689a8 commit 3f1932b

2 files changed

Lines changed: 53 additions & 1 deletion

File tree

backend/cmd/server/system_detection.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ type saveSystemDetectionResult struct {
194194
Evidence saveDetectionEvidence
195195
}
196196

197-
var numberedSaveSlotTitlePattern = regexp.MustCompile(`^\s*[0-9]{1,3}\s*-\s*[A-Za-z]`)
197+
var numberedSaveSlotTitlePattern = regexp.MustCompile(`(?i)^\s*[0-9]{1,3}\s*-\s*(autosave|save|slot|auto|backup|mission|player|file|state|memory|sp[0-9]*)\b`)
198198

199199
func allSupportedSystems() []system {
200200
out := make([]system, 0, len(supportedSystemsBySlug))
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package main
2+
3+
import "testing"
4+
5+
func TestNumberedSaveSlotTitlePattern(t *testing.T) {
6+
// True positives — real numbered slot/mission artifacts that we DO want to drop.
7+
wantNoise := []string{
8+
"1 - Save",
9+
"01 - Save Slot",
10+
"02 - Backup",
11+
"3 - Mission",
12+
"4 - Auto",
13+
"05 - Autosave",
14+
"7 - Player Slot",
15+
"008 - File 8",
16+
"01 - State",
17+
"02 - Memory Card",
18+
"01 - SP0",
19+
"02 - SP12",
20+
}
21+
for _, title := range wantNoise {
22+
t.Run("noise/"+title, func(t *testing.T) {
23+
if !numberedSaveSlotTitlePattern.MatchString(title) {
24+
t.Errorf("expected %q to match numberedSaveSlotTitlePattern (real slot artifact)", title)
25+
}
26+
})
27+
}
28+
29+
// False positives — real game titles that previously got rejected and shouldn't.
30+
// See: 2026-06-07 burn where Deck saves for 007 - Everything or Nothing (GBA)
31+
// were rejected with status=422 because the title prefix "007 - " matched the
32+
// over-broad original pattern `^\s*[0-9]{1,3}\s*-\s*[A-Za-z]`.
33+
wantGame := []string{
34+
"007 - Everything or Nothing",
35+
"007 - The World Is Not Enough",
36+
"007 - GoldenEye",
37+
"007 - Agent Under Fire",
38+
"3 - Count Bout", // real arcade game, no dash
39+
"19 - Centerfold", // real game
40+
"99 - The Last Homeland", // hypothetical real game
41+
"4 - Wheel Thunder", // SNK Dreamcast game
42+
"007 - James Bond: Quantum of Solace", // real game
43+
"42 - The Answer", // hypothetical, real-game-looking
44+
}
45+
for _, title := range wantGame {
46+
t.Run("game/"+title, func(t *testing.T) {
47+
if numberedSaveSlotTitlePattern.MatchString(title) {
48+
t.Errorf("expected %q to NOT match numberedSaveSlotTitlePattern (real game title)", title)
49+
}
50+
})
51+
}
52+
}

0 commit comments

Comments
 (0)