fix: guard dismissal honesty, one threshold source, EXIF proven (#39 r1)

r1-M1: dismissal (Escape/scrim/Cancel) while the reduce or keep-original
flow is executing no longer races the decision — hp-close is ignored while
busy, and every flow re-checks it still owns the guard before applying, so
a force-cleared dialog can never silently install its stale result. The
smoke now drives both: hp-close during a hanging decode leaves the busy
dialog up, and a force-cleared guard ends with clean staging, no toast, no
planFile. A new registry mutant removes the busy gate and is killed.

r1-M2: the hard-dialog text takes its limit from the imported
HARD_DIMENSION instead of a literal — recalibration stays a one-file
change, as the spec promises.

r1-M3: AC8 is now proven end to end, not plausible: the smoke splices a
real EXIF APP1 (orientation 6) into a canvas-encoded 8200×4100 JPEG,
asserts the header probe reads the unrotated SOF, that the decode call
carries imageOrientation:'from-image' (captured on the hook), and that the
reduced copy comes out portrait 2048×4096. TESTING.md names the scenario.

Issue: #39
User-Visible: no
This commit is contained in:
Codex
2026-08-29 10:13:09 +03:00
parent e84af749aa
commit 7c45372c32
22 changed files with 252 additions and 142 deletions
+17
View File
@@ -726,6 +726,7 @@ const MUTANT_DEFINITIONS = [
find: ' } catch {\n'
+ ' // Honest phase 2 (spec §UX): no silent fallback to the original the\n'
+ ' // user just declined — staging stays clean, the toast says what happened.\n'
+ ' if (!stillCurrent()) return;\n'
+ ' close();\n'
+ " host._showToast(host._t('backdrop.downscale_failed'));\n"
+ ' }',
@@ -736,6 +737,22 @@ const MUTANT_DEFINITIONS = [
+ ' }',
}],
},
{
id: 'backdrop-busy-dismiss-races-decision',
guard: 'node demo/smoke_backdrop_guard.mjs',
because: 'Escape/scrim during the running reduce must not race the decision — the dialog '
+ 'stays up while busy, and a force-cleared guard never applies a stale result (#39 r1-M1)',
patches: [{
file: 'src/backdrop-pick.ts',
find: ' const dismiss = (): void => {\n'
+ ' if (host._backdropGuard?.busy) return;\n'
+ ' close();\n'
+ ' };',
replace: ' const dismiss = (): void => {\n'
+ ' close();\n'
+ ' };',
}],
},
{
id: 'cold-view-vacuum-mapid-delegated',
guard: 'node demo/smoke_cold_view_vacuum.mjs',