Skip to content

W3-02d: installer adapter (exit/UAC/sharing classification, no retry) - #348

Merged
mergify[bot] merged 14 commits into
mainfrom
claude/w3-02d-installer-adapter
Oct 4, 2026
Merged

mergify[bot] merged 14 commits into
mainfrom
claude/w3-02d-installer-adapter

Conversation

@Cuarroc

@Cuarroc Cuarroc commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Dieses Paket baut den Teil des Windows-Updaters, der den Installer genau einmal startet und das Ergebnis einordnet.
Der Branch wurde mit dem aktuellen main abgeglichen; die Installer-Logik blieb dabei unverändert.
Die abschließende Linux-Prüfung ist grün, während die Windows-Ausführung weiterhin in der Merge-Queue offen ist.

Report

What changed

  • Installer adapter in src-tauri/src/delivery_recovery/installer.rs: verified staging bytes are launched exactly once, exit/UAC/sharing results are classified, and unclear failures never trigger a retry.
  • Merge conflict resolution: git merge origin/main completed with exit 0 and no conflict hunks; the merge added only current-main changes outside the installer adapter. The generated HQ snapshot was refreshed as required by the merge hook.
  • No installer runtime logic was changed while resolving the conflict.

Evidence

  • npm run dev:start-check -> exit 0 (12.2 GB free, one Cargo build at check time).
  • bash scripts/ci/gates.sh lane precommit -> exit 0.
  • bash scripts/ci/gates.sh lane prepush -> exit 0 (all 12 gates green; 80 frontend files / 475 tests passed; 1,761 Rust tests passed, 7 skipped).
  • bash scripts/ci/red-first.sh --plan -> exit 0 (count=4, specs=4).
  • git push -u origin claude/w3-02d-installer-adapter -> exit 0; the push hook also completed the prepush lane successfully.
  • git ls-remote origin refs/heads/claude/w3-02d-installer-adapter -> exit 0; remote head f724e8f5f93ad7e4bff0dbd0f7b25c75d69e3343 matches local HEAD.
  • node scripts/ci/architecture-drift.mjs check -> exit 0 (no new findings; no allowlist entry added).

Architecture drift

No new architecture-drift finding was reported after the merge. No allowlist entry was needed.

NICHT ABGEDECKT

  • Linux does not compile or run the #[cfg(windows)] ProcessLauncher; the Windows lane runs in the merge queue.
  • Real NSIS execution, UAC interaction, sharing violation against a real file, and application relaunch require the PC drill.
  • The adapter remains intentionally unwired from the real recovery effects; wiring is a later package.
  • Reboot-required exit codes remain classified as failed until proven otherwise.

Review disposition

Review: ausstehend (Stufe A), orchestrator-owned.

The previously recorded independent review remains applicable to the installer commit. Its open findings are unchanged: R-B1 (high, updater/UAC launch contract) and R-B2 (medium, held-image/path replacement contract). They are not guessed into this conflict-resolution package and remain open.

Queue disposition: @Mergifyio queue requested because the conflict resolution changed no code logic. The open review findings remain visible to the orchestrator.

Open point: resolve R-B1 and R-B2 with Windows contract/race tests and the PC drill.
Reviewer 2: GLM 5.2 (observed model: glm-5.2, requested glm-5.2:cloud), candidate 0182206b8b24135ea64258257d66bff2ba48aa41

  • F1 (medium) — src-tauri/src/delivery_recovery/installer.rs:102: held remains alive through Command::status() until process exit, while the comment says it is held until process start. Disposition proposal: follow-up documentation fix; behavior is conservative and not a correctness bug.
  • F2 (low) — src-tauri/src/delivery_recovery/installer.rs:98: a VerifiedImage::open failure becomes StartFailed(None)/Unclear, losing the digest-mismatch OS detail. Disposition proposal: follow-up diagnostic improvement; no-retry safety contract remains intact.
  • F3 (low, unconfirmed) — src-tauri/src/delivery_recovery/installer.rs:83: the Windows-only crate::proc::command path could not be verified from the diff alone. Disposition proposal: follow-up confirmation in the Windows merge-queue lane; no confirmed defect from this review.
  • F4 (low) — .pa/ACTIVITY.md:33: the new 20:36 entry appears before a later 19:37 entry, violating chronological append order. Disposition proposal: follow-up ordering correction; no runtime impact.

Verdict: approve with conditions; no HIGH finding. Windows compilation and the listed follow-ups remain open.

Review fixes (round 1, Review B findings)

  • R-B1 (high, accepted) — 8c963f7: installer.rs now launches the NSIS installer with /P /UPDATE, the argument set tauri-plugin-updater 2.13.0 builds for installMode: "passive" (src/updater.rs updater_parameters, src/config.rs nsis_args; contract in tauri.conf.json plugins.updater.windows.installMode). No /R//ARGS relaunch (recovery validates the candidate itself). Error 740 is now its own outcome ElevationRequired (not "UAC refused"; only 1223 maps to UacRefused). The trait takes the args, so FakeProcess records them. Follow-up (not invented here): the updater uses ShellExecuteW, which elevates via the installer manifest; this launcher uses CreateProcess, so an installer demanding elevation ends in 740. If recovery must elevate, a canonical elevation helper is needed (none exists in the repo).
  • R-B2 (medium, accepted) — same commit: the file is written with FILE_SHARE_READ only, then held through the existing projecta_capture::windows_image::VerifiedImage::open(path, sha256) (re-hashed against the verified digest) until the process has started.
  • New tests (red at the merge base): launcher_receives_the_updater_passive_nsis_arguments, elevation_required_is_named_and_not_reported_as_a_refusal; exit_uac_and_sharing_codes_are_classified updated for 740.
  • Evidence: bash scripts/ci/gates.sh lane prepush at final HEAD 0182206 (includes merge of origin/main d6c287d): exit 0; red-first.sh --plan exit 0; cargo test delivery_recovery::installer 6 passed. Fix diff in src-tauri: +64/-10.
  • NICHT ABGEDECKT: the #[cfg(windows)] ProcessLauncher (share modes, VerifiedImage hold, real 740) does not compile/run on Linux; the Windows lane runs in the merge queue. No real installer run.
  • Review: pending (coordinator).

Round 2 (delta, Codex, 0182206)

  • R-B1 (high, fixed — 8c963f7): run_installer now passes ["/P", "/UPDATE"] to the launcher, and launcher_receives_the_updater_passive_nsis_arguments observes the complete ordered argument vector. Windows error 740 now maps to ElevationRequired, while only 1223 maps to UacRefused; elevation_required_is_named_and_not_reported_as_a_refusal observes the user-facing distinction. Locked-source check: tauri-plugin-updater 2.13.0 returns /P for passive mode in src/config.rs:41-48 and appends /UPDATE in src/updater.rs:982-990. Precision: those are exactly the two arguments only when restart-after-install is disabled and no extra installer arguments exist; the crate default enables restart and would additionally append /R /ARGS ... (src/updater.rs:228,993-1009). Recovery intentionally omits relaunch arguments because candidate validation/relaunch is owned by the recovery flow.
  • R-B2 (medium, fixed — 8c963f7): after writing and syncing, ProcessLauncher reopens and re-hashes the installer through VerifiedImage::open, whose live handle permits read sharing only and therefore denies write/delete sharing. The held value remains in scope across Command::status() and is dropped only afterward, so it is held through process creation (and, more conservatively, until process exit).
  • Evidence: bash scripts/ci/red-first.sh --plan -> exit 0 (count=6, specs=6); CARGO_TARGET_DIR=<build-slot> CARGO_BUILD_JOBS=1 cargo test --manifest-path src-tauri/Cargo.toml delivery_recovery::installer -- --nocapture -> exit 0 (6 passed).
  • NICHT ABGEDECKT: Linux does not compile or execute the #[cfg(windows)] launcher, native sharing behavior, real UAC interaction, or a real NSIS installer. The Windows lane runs in the merge queue; the real installer/UAC behavior remains for the PC drill.
  • New high findings: none in the reviewed delta.
  • Review: Round 2 delta complete (Codex, tier A). Both Round-1 findings are fixed at 8c963f7; no additional code change was reviewed beyond the fix delta.

Conflict resolution update

  • git merge origin/main -> exit 0; merge commit e6e5c9c resolved PR W3-02d: installer adapter (exit/UAC/sharing classification, no retry) #348 against the current origin/main.
  • The merge introduced no installer-adapter logic changes; only the required generated HQ snapshot was refreshed and committed separately.
  • bash scripts/ci/gates.sh lane prepush -> exit 0 after the merge.
  • git push -u origin claude/w3-02d-installer-adapter -> exit 0; git ls-remote confirms the remote head is e6e5c9c9db7de806da00b431364fdf202a8bf96d.

Queue disposition: @Mergifyio queue requested; no installer runtime logic changed during conflict resolution.

  • Final handoff commit: dab9f1d records the required activity note; its precommit and prepush gates both exited 0.

  • Final remote verification: git ls-remote origin refs/heads/claude/w3-02d-installer-adapter reports dab9f1d952dd25eba09a816134e26b902abda6c3.

  • Final current-main merge: git merge origin/main -> exit 0; merge commit a0672d2 included the main update that arrived during the first push.

  • Generated HQ snapshot commit: 0bc4519; final prepush and push-hook both exited 0.

  • Final remote verification reports 0bc4519cdf29c1dae97ef37811cd31a072f5952a.

Queue disposition: @Mergifyio queue requested after the current-main merge; installer runtime logic remained unchanged.

Runs exactly the verified staged installer bytes once through a launcher
trait, classifies exit code, UAC refusal and sharing violation, and turns
anything unproven into an error so the persisted Installing phase never
leads to a second launch. Windows glue is a minimal proc::command launcher.

Test-First: src-tauri/src/delivery_recovery/installer.rs::delivery_recovery::installer::tests::failed_or_unclear_start_keeps_installing_and_never_launches_twice
Test-First: src-tauri/src/delivery_recovery/installer.rs::delivery_recovery::installer::tests::installer_swapped_after_verification_is_never_launched
Test-First: src-tauri/src/delivery_recovery/installer.rs::delivery_recovery::installer::tests::exit_uac_and_sharing_codes_are_classified
Test-First: src-tauri/src/delivery_recovery/installer.rs::delivery_recovery::installer::tests::launcher_receives_exactly_the_verified_installer_bytes

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@Cuarroc
Cuarroc marked this pull request as ready for review October 4, 2026 18:15
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-04T18:22:04.302472Z eb68a36 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eb68a36574

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +98 to +102
.and_then(|mut file| file.write_all(installer).and_then(|()| file.sync_all()));
if let Err(error) = written {
return LaunchResult::StartFailed(error.raw_os_error());
}
match crate::proc::command(&self.target).status() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sperre die verifizierte Kopie bis zum Prozessstart

Wenn ein anderer Prozess in das Zielverzeichnis schreiben kann, wird der Dateihandle nach sync_all geschlossen und die EXE anschließend erneut nur über ihren Pfad geöffnet. In diesem Fenster kann etwa ein gleichberechtigter Worker die geprüfte Kopie ersetzen oder verändern, sodass der privilegierte Recovery-Helper fremde, nie verifizierte Bytes ausführt. Halte die Datei deshalb mit einer Share-Mode-Sperre gegen Schreiben/Löschen bis nach der Prozesserzeugung offen und sichere zusätzlich das Zielverzeichnis beziehungsweise die ACL ab.

Useful? React with 👍 / 👎.

if let Err(error) = written {
return LaunchResult::StartFailed(error.raw_os_error());
}
match crate::proc::command(&self.target).status() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Übergib dem NSIS-Installer den Update-Vertrag

Für das von ProjectA erzeugte NSIS-Artefakt startet dieser Aufruf die EXE ohne Argumente, obwohl tauri.conf.json den passiven Update-Modus konfiguriert. Die offizielle Tauri-Updater-Implementierung übergibt dafür /P, zwingend /UPDATE sowie gegebenenfalls /R /ARGS und beendet danach den alten Prozess. Ohne diese Parameter läuft der normale Installerpfad statt des vorgesehenen passiven Update-/Relaunch-Protokolls; der Launcher muss die äquivalenten NSIS-Argumente und die Beendigung des alten Prozesses sicherstellen.

Useful? React with 👍 / 👎.

Comment on lines +102 to +106
match crate::proc::command(&self.target).status() {
Ok(status) => status
.code()
.map_or(LaunchResult::NoExitCode, LaunchResult::Exited),
Err(error) => LaunchResult::StartFailed(error.raw_os_error()),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Löse UAC aus, statt Fehler 740 als Ablehnung zu melden

Wenn der Installer tatsächlich erhöhte Rechte verlangt und der Helper nicht bereits erhöht läuft, verwendet proc::command(...).status() den CreateProcess-Pfad: Dieser zeigt keinen UAC-Dialog, sondern liefert ERROR_ELEVATION_REQUIRED (740), das classify anschließend fälschlich als vom Nutzer abgelehnte Erhöhung meldet. Microsoft beschreibt, dass dafür ShellExecute beziehungsweise runas nötig ist, um den Dialog auszulösen (Microsoft Learn). In diesem Szenario kann der Nutzer die Installation derzeit gar nicht genehmigen.

Useful? React with 👍 / 👎.

Comment on lines +71 to +72
let outcome = staging
.install(|payload| classify(launcher.launch(&payload.installer)))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Halte nicht alle Staging-Artefakte während der Installation im RAM

Bei einem echten Update liest VerifiedStaging::install Manifest, Installer, Programmdatei und die vollständige Datenbank nacheinander in vier Vec<u8> ein, obwohl diese Closure ausschließlich payload.installer verwendet; die übrigen großen Puffer bleiben zudem erhalten, solange der blockierende Prozesslauncher auf den Installer wartet. Eine gewachsene ProjectA-Datenbank kann dadurch den Recovery-Helper gerade während der Wartung aus dem Speicher drängen und das Journal in Installing zurücklassen. Prüfe die ungenutzten Artefakte streamend und behalte für diesen Adapter nur die Installer-Bytes im Speicher.

Useful? React with 👍 / 👎.

@mergify

mergify Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Queued — the merge queue status continues in this comment ↓.

@Cuarroc

Cuarroc commented Oct 4, 2026

Copy link
Copy Markdown
Owner Author

@Mergifyio queue

@mergify mergify Bot added the queued label Oct 4, 2026
@mergify

mergify Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Merge Queue Status

  • ✅ Entered queue — 2026-10-04 19:00 UTC · Rule: default · triggered by @Cuarroc with the @mergifyio queue command
  • 🚫 Left the queue — 2026-10-04 19:04 UTC · at e31f65a2657f12ac435d5e934bb0043b3d3ec7f2

This pull request spent 3 minutes 41 seconds in the queue, with no time running CI.

Reason

The pull request conflicts with the base branch

Hint

You should update or rebase your pull request.

If you want to requeue this pull request, you can post a @mergifyio queue comment.

Requeued — the merge queue status continues in this comment ↓.

@mergify mergify Bot added dequeued and removed queued labels Oct 4, 2026
@Cuarroc Cuarroc added the do-not-merge Keep this pull request out of the merge queue label Oct 4, 2026
@mergify mergify Bot removed the dequeued label Oct 4, 2026
@Cuarroc

Cuarroc commented Oct 4, 2026

Copy link
Copy Markdown
Owner Author

@Mergifyio queue

Cuarroc and others added 3 commits October 4, 2026 21:28
Pass the updater's passive NSIS arguments (/P /UPDATE), name Windows
error 740 as ElevationRequired instead of a UAC refusal, and keep the
written installer held via VerifiedImage until the process has started.

Test-First: src-tauri/src/delivery_recovery/installer.rs::delivery_recovery::installer::tests::launcher_receives_the_updater_passive_nsis_arguments
Test-First: src-tauri/src/delivery_recovery/installer.rs::delivery_recovery::installer::tests::elevation_required_is_named_and_not_reported_as_a_refusal

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
No-Test: generated HQ snapshot, no runtime code

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@Cuarroc

Cuarroc commented Oct 4, 2026

Copy link
Copy Markdown
Owner Author

Coordinator disposition (round 2 complete, tier A with two vendors): Codex round 1 R-B1 (high) and R-B2 (medium) fixed in 8c963f7, Codex delta review DELTA-OK at 0182206; GLM 5.2 approve with conditions, no high finding. GLM F1-F4 (medium/low) accepted as follow-ups, not blocking: F1 comment wording (handle held until exit), F2 keep digest-mismatch detail, F3 confirmed by the Windows merge-queue lane, F4 ACTIVITY.md order (moot after CI-CONFLICT-01 #370). Removing do-not-merge.

@Cuarroc Cuarroc removed the do-not-merge Keep this pull request out of the merge queue label Oct 4, 2026
@mergify mergify Bot added the queued label Oct 4, 2026
@mergify

mergify Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Merge Queue Status

  • ✅ Entered queue — 2026-10-04 20:36 UTC · Rule: default · triggered by @Cuarroc with the @mergifyio queue command
  • 🚫 Left the queue — 2026-10-04 20:40 UTC · at 0182206b8b24135ea64258257d66bff2ba48aa41

This pull request spent 4 minutes 40 seconds in the queue, with no time running CI.

Reason

Pull request #348 has been dequeued

Queue conditions are not satisfied:

  • -conflict [📌 queue requirement]
  • any of: [🔀 queue conditions]
    • all of: [📌 queue conditions of queue rule default]
      • -conflict

Hint

You should look at the reason for the failure and decide if the pull request needs to be fixed or if you want to requeue it.
If you do update this pull request, it will automatically be requeued once the queue conditions match again.
If you think this was a flaky issue instead, you can requeue the pull request, without updating it, by posting a @mergifyio queue comment.

Requeued — the merge queue status continues in this comment ↓.

@mergify mergify Bot added dequeued and removed queued labels Oct 4, 2026
@Cuarroc

Cuarroc commented Oct 4, 2026

Copy link
Copy Markdown
Owner Author

@Mergifyio queue

@mergify mergify Bot removed the dequeued label Oct 4, 2026
@Cuarroc

Cuarroc commented Oct 4, 2026

Copy link
Copy Markdown
Owner Author

@Mergifyio queue

@mergify

mergify Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

queue

☑️ Command queue ignored because it is already running from a previous command.

@mergify mergify Bot added the queued label Oct 4, 2026
@mergify
mergify Bot merged commit 6598807 into main Oct 4, 2026
11 checks passed
@mergify
mergify Bot deleted the claude/w3-02d-installer-adapter branch October 4, 2026 21:33
@mergify

mergify Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Merge Queue Status

This pull request spent 15 minutes 17 seconds in the queue, including 13 minutes 33 seconds running CI.

Required conditions to merge

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant