fix(dialogs): wait for the confirmation before deleting or removing the key #465

Merged
maximus merged 2 commits from issue-421-confirm-dialog into main 2026-10-18 18:31:43 +00:00
Owner

Refs #421

Cause

On desktop, tauri-plugin-dialog 2.6.0 runs an init script, init-iife.js. Read in the crate source, it contains window.confirm=async function(i){return await invoke("plugin:dialog|confirm",...)}. confirm(...) therefore returns a Promise, which is always truthy, so every if (!confirm(...)) return; let the action run before the user answered. Deleting a secondary profile removed its database even on "Cancel". The plugin also replaces window.alert with a function that does not block.

Changes

  • confirmAction(message) (src/services/confirmService.ts) calls ask from @tauri-apps/plugin-dialog with kind: "warning". The buttons are common.confirm / common.cancel (both keys already exist in FR and EN), taken from the shared i18next instance, so they follow the app language. A failed call resolves false and goes through logWarn (fail closed). This is the only service that translates; its doc comment and architecture.md say so.

  • The six sites await it: ProfileFormModal, CategoryDetailPanel, AdjustmentDetailPanel, TemplateActions and AccountsPage (handleDeleteCategory became async, with the linked-accounts check kept before the dialog), plus both confirmations of performRemoveLicense. RemoveLicenseUi.confirm is now (message) => Promise<boolean>, and LicenseCard passes confirmAction.

  • @tauri-apps/plugin-dialog 2.6.0, the version of the crate, as for the other plugins (opener, process, sql and updater are each at the version of their crate). The lock gains one key, node_modules/@tauri-apps/plugin-dialog; the only other change in the lock is the root entry. Its integrity matches npm view. A clean-room npm ci with npm 11 passes and does not rewrite the lock. The package's code (dist-js/index.js, 197 lines) only calls invoke('plugin:dialog|…'): it has no install script and no network call. Its only dependency, @tauri-apps/api ^2.8.0, is already in the lock at 2.10.1. The dialog:default permission already includes allow-ask (crate's permissions/default.toml): nothing changes on the Rust side.

  • Guard src/__integration__/native-dialog-guard.test.ts scans the non-test sources of src/, with comments removed and line numbers kept. It fails on any native confirm/alert call, bare or through window, globalThis or self. It also fails on any confirmAction( or <x>.confirm( call not directly preceded by await. The first form is not covered by TypeScript, since the DOM type of confirm stays boolean; the second is not either, since !promise is not a TS error (mutation M1: tsc green). Canaries:

    • the scan covers more than 200 files and names three of them;
    • 7 native forms and 4 unawaited forms are flagged;
    • 9 correct forms pass.

    Run before the fix, the guard reported exactly the six sites (eight lines).

  • CHANGELOG FR/EN: a Fixed entry. docs/architecture.md: the service (counts 26 to 27 and 20 to 21) and the tauri-plugin-dialog plugin row.

prompt(...) (PIN entry in ProfileFormModal, renaming in AccountsPage) is out of scope, as the issue says. The plugin does not replace it.

Tests

  • Reproduction (test: reproduces #421): in useLicenseMachines.test.ts, the confirmations now resolve asynchronously, as ask does. On the old code, 6 tests are red: both refusals, the first and the second (the second once per retry code). They are green after the fix.
  • New:
    • confirmService.test.ts, 5 tests: true, false, rejection giving false plus logWarn, French labels, English labels after changeLanguage("en");
    • ProfileFormModal.delete.test.ts, 2 tests on the hook harness: a click on the trash can with a refused confirmation does not call deleteProfile, and with an accepted one it is called with the profile's id;
    • the guard, 22 tests.
  • npm run build is green. npm test: 137 files, 2285 tests (2256 before). Probe first: a deliberately red test exits 1.

Mutations, one at a time on the committed tree, restored by git; the table counts red tests out of 113 (guard, service, license, profile):

Mutation tsc red
M1 await dropped in CategoryDetailPanel green 1 (guard)
M2 confirmAction replaced by window.confirm in AccountsPage red (unused import) 1 (guard)
M3 ask rejection resolved true green 1
M4 await dropped on the 1st license confirmation green 3
M5 await dropped on the 2nd license confirmation green 5
M6 await dropped in ProfileFormModal green 2 (guard and profile test)
M7 OK and Cancel labels swapped green 2
M8 LicenseCard back to window.confirm (wrapped in async) red 1 (guard)

Post-merge checklist (hence Refs, not a closing keyword)

  1. Under Linux, on a build of this branch or of the next release:
    • Settings, Profiles: delete a secondary profile, then Cancel. The profile and its .db file under app_config_dir() are still there.
    • Delete again, then Confirm. Both are removed.
    • Check that the buttons read « Confirmer » / « Annuler » in French and "Confirm" / "Cancel" in English.
    • Observe the PIN prompt() while there. Open a separate issue if the entry fails.
  2. Same check on another site: category or budget template. On Windows too, if possible.
  3. #421 is closed by hand once 1 and 2 are done, with the evidence.

Review level

High per REVIEW.md: package.json adds a dependency and package-lock.json gains a node_modules/<name> key (zone Dépendances). The license files touched (useLicenseMachines.ts, LicenseCard.tsx) are not in the High table.

🤖 Generated with Claude Code

Refs #421 ## Cause On desktop, tauri-plugin-dialog 2.6.0 runs an init script, `init-iife.js`. Read in the crate source, it contains `window.confirm=async function(i){return await invoke("plugin:dialog|confirm",...)}`. `confirm(...)` therefore returns a Promise, which is always truthy, so every `if (!confirm(...)) return;` let the action run before the user answered. Deleting a secondary profile removed its database even on "Cancel". The plugin also replaces `window.alert` with a function that does not block. ## Changes - **`confirmAction(message)`** (`src/services/confirmService.ts`) calls `ask` from `@tauri-apps/plugin-dialog` with `kind: "warning"`. The buttons are `common.confirm` / `common.cancel` (both keys already exist in FR and EN), taken from the shared i18next instance, so they follow the app language. A failed call resolves `false` and goes through `logWarn` (fail closed). This is the only service that translates; its doc comment and `architecture.md` say so. - **The six sites await it**: `ProfileFormModal`, `CategoryDetailPanel`, `AdjustmentDetailPanel`, `TemplateActions` and `AccountsPage` (`handleDeleteCategory` became `async`, with the linked-accounts check kept before the dialog), plus both confirmations of `performRemoveLicense`. `RemoveLicenseUi.confirm` is now `(message) => Promise<boolean>`, and `LicenseCard` passes `confirmAction`. - **`@tauri-apps/plugin-dialog` 2.6.0**, the version of the crate, as for the other plugins (opener, process, sql and updater are each at the version of their crate). The lock gains one key, `node_modules/@tauri-apps/plugin-dialog`; the only other change in the lock is the root entry. Its `integrity` matches `npm view`. A clean-room `npm ci` with npm 11 passes and does not rewrite the lock. The package's code (`dist-js/index.js`, 197 lines) only calls `invoke('plugin:dialog|…')`: it has no install script and no network call. Its only dependency, `@tauri-apps/api ^2.8.0`, is already in the lock at 2.10.1. The `dialog:default` permission already includes `allow-ask` (crate's `permissions/default.toml`): nothing changes on the Rust side. - **Guard `src/__integration__/native-dialog-guard.test.ts`** scans the non-test sources of `src/`, with comments removed and line numbers kept. It fails on any native `confirm`/`alert` call, bare or through `window`, `globalThis` or `self`. It also fails on any `confirmAction(` or `<x>.confirm(` call not directly preceded by `await`. The first form is not covered by TypeScript, since the DOM type of `confirm` stays `boolean`; the second is not either, since `!promise` is not a TS error (mutation M1: `tsc` green). Canaries: - the scan covers more than 200 files and names three of them; - 7 native forms and 4 unawaited forms are flagged; - 9 correct forms pass. Run before the fix, the guard reported exactly the six sites (eight lines). - CHANGELOG FR/EN: a Fixed entry. `docs/architecture.md`: the service (counts 26 to 27 and 20 to 21) and the `tauri-plugin-dialog` plugin row. `prompt(...)` (PIN entry in `ProfileFormModal`, renaming in `AccountsPage`) is out of scope, as the issue says. The plugin does not replace it. ## Tests - **Reproduction** (test: reproduces #421): in `useLicenseMachines.test.ts`, the confirmations now resolve asynchronously, as `ask` does. On the old code, 6 tests are red: both refusals, the first and the second (the second once per retry code). They are green after the fix. - **New**: - `confirmService.test.ts`, 5 tests: true, false, rejection giving false plus `logWarn`, French labels, English labels after `changeLanguage("en")`; - `ProfileFormModal.delete.test.ts`, 2 tests on the hook harness: a click on the trash can with a refused confirmation does not call `deleteProfile`, and with an accepted one it is called with the profile's id; - the guard, 22 tests. - `npm run build` is green. `npm test`: 137 files, **2285 tests** (2256 before). Probe first: a deliberately red test exits 1. Mutations, one at a time on the committed tree, restored by git; the table counts red tests out of 113 (guard, service, license, profile): | Mutation | tsc | red | |---|---|---| | M1 `await` dropped in `CategoryDetailPanel` | green | 1 (guard) | | M2 `confirmAction` replaced by `window.confirm` in `AccountsPage` | red (unused import) | 1 (guard) | | M3 `ask` rejection resolved `true` | green | 1 | | M4 `await` dropped on the 1st license confirmation | green | 3 | | M5 `await` dropped on the 2nd license confirmation | green | 5 | | M6 `await` dropped in `ProfileFormModal` | green | 2 (guard and profile test) | | M7 OK and Cancel labels swapped | green | 2 | | M8 `LicenseCard` back to `window.confirm` (wrapped in `async`) | red | 1 (guard) | ## Post-merge checklist (hence `Refs`, not a closing keyword) 1. Under Linux, on a build of this branch or of the next release: - Settings, Profiles: delete a secondary profile, then Cancel. The profile and its `.db` file under `app_config_dir()` are still there. - Delete again, then Confirm. Both are removed. - Check that the buttons read « Confirmer » / « Annuler » in French and "Confirm" / "Cancel" in English. - Observe the PIN `prompt()` while there. Open a separate issue if the entry fails. 2. Same check on another site: category or budget template. On Windows too, if possible. 3. #421 is closed by hand once 1 and 2 are done, with the evidence. ## Review level High per `REVIEW.md`: `package.json` adds a dependency and `package-lock.json` gains a `node_modules/<name>` key (zone Dépendances). The license files touched (`useLicenseMachines.ts`, `LicenseCard.tsx`) are not in the High table. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
maximus added 1 commit 2026-10-18 17:48:05 +00:00
fix(dialogs): wait for the confirmation before deleting or removing the key
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 1m59s
155722775a
On desktop, tauri-plugin-dialog 2.6.0 replaces window.confirm with an async
function: confirm(...) returned a Promise, always truthy, so every guard
`if (!confirm(...)) return;` let the action run before the user answered.
Deleting a secondary profile removed its database even on "Cancel".

- confirmAction() (src/services/confirmService.ts) calls the plugin's ask()
  with a warning icon and buttons labelled in the app language
  (common.confirm / common.cancel); a failed call resolves false and is
  logged (fail closed). @tauri-apps/plugin-dialog 2.6.0 added, the crate's
  version.
- The six sites await it: profile, category, adjustment, budget template and
  Balance category deletions, and both confirmations of the license key
  removal (performRemoveLicense takes an async confirm).
- native-dialog-guard.test.ts fails on any native confirm/alert in src/ and
  on any confirmation not awaited (TypeScript does not flag `!promise`).
- Tests: confirmService, the deletion of a profile in ProfileFormModal, and
  the license removal tests with an async confirm (test: reproduces #421:
  six of them were red on the old code).

Refs #421

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Owner

Niveau : haut — package.json et package-lock.json (Dépendances : ajout de @tauri-apps/plugin-dialog, nouvelle clé node_modules/@tauri-apps/plugin-dialog)
Max doit lire avant le merge.

Verdict : APPROVE

Le correctif est juste et prouvé. Les six sites attendent confirmAction, qui passe par ask du plugin et échoue fermé. La cause annoncée se vérifie dans la source du crate : le init-iife.js de tauri-plugin-dialog 2.6.0 remplace bien window.confirm par une fonction async et window.alert par une fonction qui n'attend rien.

Vérifications rejouées (tête 1557227)

  • Copie hors dépôt (git archive de la tête, node_modules copié) : npm run build vert. Sonde : un test volontairement rouge sort en 1. npm test : 137 fichiers, 2285 tests verts, comme annoncé.
  • CI : check-frontend run 566 vert. Son journal indique 137 fichiers, 2285 tests et found 0 vulnerabilities au npm ci. check-rust n'avait pas à tourner : aucun fichier sous src-tauri/.
  • Plugin, côté Rust (~/.cargo/registry/.../tauri-plugin-dialog-2.6.0) :
    • ask construit OkCancelCustom(yes, no) avec builder.parent(&window) sous #[cfg(desktop)]. La boîte est donc modale pour la fenêtre, et un second clic pendant l'attente n'est pas possible.
    • show rend true seulement si le bouton cliqué porte le libellé OK. Les libellés FR (Confirmer/Annuler) et EN (Confirm/Cancel) sont distincts, et un refus ou une fermeture donne false.
    • permissions/default.toml contient allow-ask, et capabilities/default.json a déjà dialog:default.
  • Paquet npm : la version 2.6.0 est celle du crate dans Cargo.lock. L'integrity du lock est identique à npm view @tauri-apps/plugin-dialog@2.6.0 dist.integrity. Seul script : build: rollup -c, aucun script d'installation. Les dist-js ne font que des invoke('plugin:dialog|…'), sans fetch ni require. Le diff du lock fait 10 lignes : la racine et la nouvelle clé.
  • Comptes de architecture.md : 27 services dans src/services/, 21 lignes dans la section « Services TypeScript ». « Seul service qui traduit » est exact : licenseService.ts ne cite i18n que dans des commentaires et des noms de champ.
  • Fermeture : la timeline de #421 ne porte qu'un pull_ref avec ref_action: "none". Aucun closes, ce qui est cohérent avec Refs #421 et la fermeture manuelle après la liste post-merge.

Mutations du relecteur

Une mutation à la fois, sur la copie. On compte les tests rouges parmi les 113 des quatre fichiers concernés (garde, service, licence, profil).

Mutation tsc rouges
R1 TemplateActions : if (confirmAction(…)) sans await rouge (TS « condition toujours vraie ») 1 (garde)
R3 confirmService : catch qui rend true vert 1
R4 confirmService : return ask(…) sans await, donc le rejet échappe au try vert 1
R5 libellé OK codé en dur ("Confirmer") vert 1 (libellés EN)
R6 garde : AWAITED accepte tout vert 4
R7 garde : seul window. détecté (forme nue, globalThis et self ignorés) vert 5
R8 ProfileFormModal : alias const ask = confirmAction; if (!ask(…)) vert 1 (test du profil seulement, la garde ne voit rien)
R9 AccountsPage : même alias vert 0, survivante
R10 licence : seconde confirmation retirée rouge 8

R4 mérite d'être nommée : c'est le return await dans le try qui rend l'échec fermé réel, et le test du rejet l'épingle bien.

Passe sécurité (niveau haut)

  • §1 secrets : aucun.
  • §3 motifs dangereux : aucun. Pas d'eval, pas d'HTML injecté. Le message passe au dialogue natif en texte.
  • §4 configuration : capabilities inchangées, CSP inchangée.
  • §2 dépendances : voir ci-dessus. L'intégrité correspond, il n'y a aucun script d'installation, et le code est exécuté dans la webview, pas au build.
  • Zones de REVIEW.md :
    • Vie privée : aucun appel réseau ajouté, le dialogue passe par l'IPC.
    • Dépendances : une version montée exécute son code au build. Ici, le paquet n'est que bundlé par vite.
    • Jetons et licence : useLicenseMachines.ts et LicenseCard.tsx ne sont pas dans le tableau Haut, et le diff ne touche ni jeton ni édition.
    • i18n : common.confirm et common.cancel existent dans les deux langues, et le test EN le prouve.
    • CHANGELOG : FR et EN présents.

Aucun constat.

Suggestions (non bloquantes)

  1. La garde ne voit pas un alias (R9 survit, R8 n'est prise que par le test du profil). Un appel c(m) où c vaut confirmAction échappe aux deux règles. Cette forme ne se rencontre que si on l'écrit exprès, mais une troisième règle fermerait le trou : toute référence à confirmAction qui n'est ni un appel attendu, ni l'import, ni la déclaration, ni confirm: confirmAction serait refusée. Même logique pour window["confirm"](…), confirm?.(…) et const { confirm } = window, que NATIVE_CALL ne voit pas.
  2. Couverture de comportement : seuls le profil et la licence ont un test de comportement. Les quatre autres sites (catégorie, ajustement, gabarit, catégorie du Bilan) ne sont tenus que par la garde. C'est acceptable vu la garde ; à étendre si l'un d'eux gagne de la logique avant ou après la confirmation, comme le pré-contrôle des comptes liés d'AccountsPage.

Actions faites

  • Copie hors dépôt supprimée en fin de revue (scratchpad/pr465).
  • Dépôt non modifié.
Niveau : haut — `package.json` et `package-lock.json` (Dépendances : ajout de `@tauri-apps/plugin-dialog`, nouvelle clé `node_modules/@tauri-apps/plugin-dialog`) Max doit lire avant le merge. ## Verdict : APPROVE Le correctif est juste et prouvé. Les six sites attendent `confirmAction`, qui passe par `ask` du plugin et échoue fermé. La cause annoncée se vérifie dans la source du crate : le `init-iife.js` de `tauri-plugin-dialog` 2.6.0 remplace bien `window.confirm` par une fonction `async` et `window.alert` par une fonction qui n'attend rien. ## Vérifications rejouées (tête `1557227`) - **Copie hors dépôt** (`git archive` de la tête, `node_modules` copié) : `npm run build` vert. Sonde : un test volontairement rouge sort en 1. `npm test` : 137 fichiers, **2285 tests** verts, comme annoncé. - **CI** : `check-frontend` run 566 vert. Son journal indique 137 fichiers, 2285 tests et `found 0 vulnerabilities` au `npm ci`. `check-rust` n'avait pas à tourner : aucun fichier sous `src-tauri/`. - **Plugin, côté Rust** (`~/.cargo/registry/.../tauri-plugin-dialog-2.6.0`) : - `ask` construit `OkCancelCustom(yes, no)` avec `builder.parent(&window)` sous `#[cfg(desktop)]`. La boîte est donc modale pour la fenêtre, et un second clic pendant l'attente n'est pas possible. - `show` rend `true` seulement si le bouton cliqué porte le libellé OK. Les libellés FR (Confirmer/Annuler) et EN (Confirm/Cancel) sont distincts, et un refus ou une fermeture donne `false`. - `permissions/default.toml` contient `allow-ask`, et `capabilities/default.json` a déjà `dialog:default`. - **Paquet npm** : la version 2.6.0 est celle du crate dans `Cargo.lock`. L'`integrity` du lock est identique à `npm view @tauri-apps/plugin-dialog@2.6.0 dist.integrity`. Seul script : `build: rollup -c`, aucun script d'installation. Les `dist-js` ne font que des `invoke('plugin:dialog|…')`, sans `fetch` ni `require`. Le diff du lock fait 10 lignes : la racine et la nouvelle clé. - **Comptes de `architecture.md`** : 27 services dans `src/services/`, 21 lignes dans la section « Services TypeScript ». « Seul service qui traduit » est exact : `licenseService.ts` ne cite i18n que dans des commentaires et des noms de champ. - **Fermeture** : la timeline de #421 ne porte qu'un `pull_ref` avec `ref_action: "none"`. Aucun `closes`, ce qui est cohérent avec `Refs #421` et la fermeture manuelle après la liste post-merge. ## Mutations du relecteur Une mutation à la fois, sur la copie. On compte les tests rouges parmi les 113 des quatre fichiers concernés (garde, service, licence, profil). | Mutation | tsc | rouges | |---|---|---| | R1 `TemplateActions` : `if (confirmAction(…))` sans `await` | rouge (TS « condition toujours vraie ») | 1 (garde) | | R3 `confirmService` : `catch` qui rend `true` | vert | 1 | | R4 `confirmService` : `return ask(…)` sans `await`, donc le rejet échappe au `try` | vert | 1 | | R5 libellé OK codé en dur (`"Confirmer"`) | vert | 1 (libellés EN) | | R6 garde : `AWAITED` accepte tout | vert | 4 | | R7 garde : seul `window.` détecté (forme nue, `globalThis` et `self` ignorés) | vert | 5 | | R8 `ProfileFormModal` : alias `const ask = confirmAction; if (!ask(…))` | vert | 1 (test du profil seulement, la garde ne voit rien) | | R9 `AccountsPage` : même alias | vert | **0, survivante** | | R10 licence : seconde confirmation retirée | rouge | 8 | R4 mérite d'être nommée : c'est le `return await` dans le `try` qui rend l'échec fermé réel, et le test du rejet l'épingle bien. ## Passe sécurité (niveau haut) - §1 secrets : aucun. - §3 motifs dangereux : aucun. Pas d'`eval`, pas d'HTML injecté. Le message passe au dialogue natif en texte. - §4 configuration : capabilities inchangées, CSP inchangée. - §2 dépendances : voir ci-dessus. L'intégrité correspond, il n'y a aucun script d'installation, et le code est exécuté dans la webview, pas au build. - Zones de `REVIEW.md` : - **Vie privée** : aucun appel réseau ajouté, le dialogue passe par l'IPC. - **Dépendances** : une version montée exécute son code au build. Ici, le paquet n'est que bundlé par vite. - **Jetons et licence** : `useLicenseMachines.ts` et `LicenseCard.tsx` ne sont pas dans le tableau Haut, et le diff ne touche ni jeton ni édition. - **i18n** : `common.confirm` et `common.cancel` existent dans les deux langues, et le test EN le prouve. - **CHANGELOG** : FR et EN présents. Aucun constat. ## Suggestions (non bloquantes) 1. **La garde ne voit pas un alias** (R9 survit, R8 n'est prise que par le test du profil). Un appel `c(m)` où `c` vaut `confirmAction` échappe aux deux règles. Cette forme ne se rencontre que si on l'écrit exprès, mais une troisième règle fermerait le trou : toute référence à `confirmAction` qui n'est ni un appel attendu, ni l'import, ni la déclaration, ni `confirm: confirmAction` serait refusée. Même logique pour `window["confirm"](…)`, `confirm?.(…)` et `const { confirm } = window`, que `NATIVE_CALL` ne voit pas. 2. **Couverture de comportement** : seuls le profil et la licence ont un test de comportement. Les quatre autres sites (catégorie, ajustement, gabarit, catégorie du Bilan) ne sont tenus que par la garde. C'est acceptable vu la garde ; à étendre si l'un d'eux gagne de la logique avant ou après la confirmation, comme le pré-contrôle des comptes liés d'`AccountsPage`. ## Actions faites - Copie hors dépôt supprimée en fin de revue (`scratchpad/pr465`). - Dépôt non modifié.
maximus added 1 commit 2026-10-18 18:26:42 +00:00
test(dialogs): guard against aliases of confirmAction and other native forms
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 2m2s
f75fcee12b
Review of PR #465 (comment 24953), suggestion 1: an alias
(`const c = confirmAction; if (!c(...))`) called confirmAction out of reach
of the "awaited" rule, and window["confirm"](...), confirm?.(...) and
`const { confirm } = window` escaped the native-call rule. Any mention of
confirmAction other than the awaited call, its declaration, a plain named
import or `confirm: confirmAction` is now refused; the three native forms
are flagged. Samples added for each.

Refs #421

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
maximus force-pushed issue-421-confirm-dialog from f75fcee12b to ff9656d015 2026-10-18 18:29:35 +00:00 Compare
maximus merged commit ff9656d015 into main 2026-10-18 18:31:43 +00:00
maximus deleted branch issue-421-confirm-dialog 2026-10-18 18:31:43 +00:00
Author
Owner

Merged by fast-forward through the API on 2026-10-18 (main ff9656d), on Max's request (« corrige 1 et /merge-stack »).

Added after the review (comment 24953), not reviewed: ff9656d (f75fcee before the rebase), test-only. It applies suggestion 1: any mention of confirmAction other than the awaited call, its declaration, a plain named import or confirm: confirmAction is refused, and window["confirm"](…), confirm?.(…) and const { confirm } = window are flagged. Guard 22 to 34 tests. The reviewer's surviving mutation (const c = confirmAction; if (!c(...)) in AccountsPage) is now red (1 test), as are the three native forms (1 each). No production code changed.

Rebase: main had moved to 5222329 (PR #450, .claude/skills/release/SKILL.md only, no file in common). Both commits were rebased onto it; the full suite was rerun locally (2297 tests) and CI ran on the rebased head (check-frontend green, 2297 tests).

Suggestion 2 (behavioural tests for the four other sites) goes to a follow-up issue. #421 stays open (Refs) until Max's manual check (post-merge checklist of the PR body).

Merged by fast-forward through the API on 2026-10-18 (`main` `ff9656d`), on Max's request (« corrige 1 et /merge-stack »). **Added after the review (comment 24953), not reviewed:** `ff9656d` (`f75fcee` before the rebase), test-only. It applies suggestion 1: any mention of `confirmAction` other than the awaited call, its declaration, a plain named import or `confirm: confirmAction` is refused, and `window["confirm"](…)`, `confirm?.(…)` and `const { confirm } = window` are flagged. Guard 22 to 34 tests. The reviewer's surviving mutation (`const c = confirmAction; if (!c(...))` in `AccountsPage`) is now red (1 test), as are the three native forms (1 each). No production code changed. **Rebase:** `main` had moved to `5222329` (PR #450, `.claude/skills/release/SKILL.md` only, no file in common). Both commits were rebased onto it; the full suite was rerun locally (2297 tests) and CI ran on the rebased head (`check-frontend` green, 2297 tests). Suggestion 2 (behavioural tests for the four other sites) goes to a follow-up issue. #421 stays open (`Refs`) until Max's manual check (post-merge checklist of the PR body).
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: maximus/Simpl-Resultat#465
No description provided.