Conversation
An install that failed left nothing to read. setup.ps1 wrote no log, and before it can fail it emits winget output, a 3.4 GB model pull, uv sync, two model downloads, and up to 180 readiness dots -- so the one line explaining the failure had scrolled out of the console buffer by the time the window paused. Install.cmd also paused only when no arguments were passed, so an argument-bearing run closed outright. setup.ps1 now records a transcript to install.log, ACL-locked like the other runtime logs because its header names this machine and account, and a trap reprints the failure and the log path last, after everything that scrolls. Install.cmd holds the window open on any failure and names the log -- or says plainly that setup stopped before one existed, which is what a Group Policy execution policy overriding -ExecutionPolicy Bypass produces. Auditing that path turned up two runtime faults. winget answers "already installed" and "no applicable update" with a non-zero exit code. Install-WingetPackage treated any non-zero code as fatal, so a machine that already had ffmpeg or Ollama aborted the whole install over a dependency that was present. The code is now recorded and reported, and the executable probe that already followed every call is what decides. `ollama show` writes to stderr for a model that is merely absent -- the expected answer during a first install. The call redirected its streams with *> $null, and Windows PowerShell turns redirected native stderr into an error record that $ErrorActionPreference = "Stop" makes terminating. That sat immediately before the step which downloads the model. Those calls now go through Invoke-Native, which reads the exit code instead. None of this had ever executed. windows-smoke.yml only parsed setup.ps1, and parsed it under pwsh while users run Windows PowerShell 5.1, where StrictMode and native-command error handling both differ. It now runs the paths that change nothing under 5.1: --help, the dry-run --uninstall, three rejection cases, and a failing --verify asserted to print the banner and write the log. install.log joins .gitignore, the Windows bundle's forbidden-entry list, and the uninstaller's personal-file inventory, so it is never packaged and never removed unless --remove-personal-data asks for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
The step ends by proving that a failing --verify prints the banner and writes install.log, which means the last native call deliberately exits non-zero. GitHub appends "exit $LASTEXITCODE" to a powershell step, so the job failed on that leftover code with every assertion having passed and no message explaining it -- the same class of invisible failure this branch is about. It now exits 0 explicitly and names each check as it passes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
setup.sh had the identical gap: no install log at all. Homebrew, a multi-gigabyte model download, and the readiness waits emit far more output than a Terminal window keeps, so a failure's explanation had already scrolled away by the time it stopped. Everything after the argument parse is now copied to install.log, created 0600 because it records this machine's paths, and fail() names it. Opening it is deliberately non-fatal: the likeliest reason for that to fail is a read-only checkout, and confirm_writable_checkout has a far better message waiting a few steps later. An uninstall is excluded for the same reason it is on Windows -- it prints a short plan that never scrolls, and an open log would be one more file it then had to remove. Install.command now holds the window open on any failure rather than only on a bare double-click, and names the log, or says plainly that setup stopped before one existed. install.log joins the uninstaller's personal-file inventory on both platforms, so PERSONAL_FILES in tests/test_installers.py now covers it and enforces that neither installer can remove it without --remove-personal-data. Verified live on macOS: ./setup.sh --verify writes a 0600 install.log holding the whole run and names it on failure; ./setup.sh --uninstall writes none and lists it as an inventory item. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A Windows install failed and left nothing to read. This makes a failing install
legible on both platforms, and fixes two Windows runtime faults found while
auditing that path.
Why the error was invisible. Neither installer wrote a log. Before either can
fail it emits package-manager output, a multi-gigabyte model download, and long
readiness waits -- far more than a console or Terminal window keeps -- so the one
line explaining the failure had scrolled out of the buffer by the time the window
paused. Both shims also paused only when no arguments were passed, so an
argument-bearing run closed outright.
Both installers now record the whole run to
install.log, created private to thecurrent user (0600 on macOS, a single-ACE ACL on Windows) because it records this
machine's paths and, on Windows, its account name.
setup.ps1adds atrapthatreprints the failure and the log path last, after everything that scrolls;
setup.shnames the log fromfail().Install.cmdandInstall.commandholdthe window open on any failure and name the log -- or say plainly that setup
stopped before one existed, which on Windows is what a Group Policy execution
policy overriding
-ExecutionPolicy Bypassproduces.Opening the log is deliberately non-fatal on both. The likeliest reason it fails
is a read-only checkout, and the existing writable-checkout probe has a far
better message a few steps later. An uninstall is excluded on both: it prints a
short plan that never scrolls, and an open log would be one more file it then had
to remove.
Two Windows runtime faults
winget's non-zero success codes.
Install-WingetPackagetreated any non-zeroexit as fatal, but winget answers "already installed" and "no applicable update"
that way. A machine that already had ffmpeg or Ollama aborted the whole install
over a dependency that was present. The code is now recorded and reported, and
the executable probe that already followed every call is what decides.
Native stderr under
$ErrorActionPreference = "Stop".ollama showwrites tostderr for a model that is merely absent -- the expected answer during a first
install. The call redirected its streams with
*> $null, and Windows PowerShellturns redirected native stderr into an error record that
Stopmakesterminating. That sat immediately before the step which downloads the model.
Those calls now go through
Invoke-Native, which reads the exit code instead.Why it shipped
windows-smoke.ymlonly parsedsetup.ps1, and parsed it underpwshwhileusers run Windows PowerShell 5.1, where StrictMode and native-command error
handling both differ. Not one line of the installer had ever been executed
anywhere. It now runs the paths that change nothing under 5.1 and reports each:
That last line is the fix proving itself on a real Windows host.
What this does not do
It does not identify the specific failure on the reporting machine. Without the
error text that is not knowable, so the two faults above are credible causes, not
confirmed ones. The logging is what makes the next attempt diagnosable.
Ruled out while auditing: all 154 files
setup.ps1requires are present and shipin the bundle;
dictate.py.lockcarries correctsys_platform == 'win32'markers and
uv sync --lockedpasses on a Windows runner; everydictate.pyflag the installer invokes exists; the script parses cleanly.
Grant of Copyright and Patent Rights
Read the Whisper Face Contributor License Agreement
before submitting outside contributions.
any third-party material and restrictions.
Installer parity
docs/installer-release-process.md.default, test, and instruction-or explained why no installer edit is
required.
Installer parity: updated both installers and both shims --
setup.ps1,Install.cmd,setup.sh,Install.command-- because the missing install logwas identical on each. The winget exit-code handling and the native-stderr
hazard are Windows-only; neither has a macOS analogue. Also updated
scripts/windows_bundle.py(forbidden-entry list),.gitignore,.github/workflows/windows-smoke.yml, andtests/test_installers.py, whosePERSONAL_FILESconstant now coversinstall.logand so enforces on bothplatforms that it cannot be removed without
--remove-personal-data.No runtime source is duplicated; both installers still execute the checkout.
Private state is untouched --
install.logis additive and kept on uninstallunless
--remove-personal-datais passed.Verification
All of the following pass on the committed tree:
uv lock --check --script dictate.pyuv run tests/test_parrot_core.pyuv run tests/test_voice_compiler.pyuv run tests/test_consequence_routing.pyuv run tests/test_cleanup_circuit_breaker.pyuv run tests/test_benchmark_voice_compiler.pyuv run tests/test_benchmark_consequence_routing.pyuv run tests/test_benchmark_cleanup_latency.pyuv run tests/test_cleanup_proof_recovery.pyuv run tests/test_quality_gate.pyuv run quality_gate.pyuv run tests/test_benchmark_cleanup_proof_recovery.pyuv run tests/test_benchmark_asr.pyuv run tests/test_benchmark_macos_asr_warm_path.pyuv run tests/test_performance_lab.pyuv run tests/test_dictate.pyuv run tests/test_gui_settings_runtime.pyuv run tests/test_insertion_integrity.pyuv run tests/test_benchmark_insertion_reliability.pyuv run tests/test_compatibility_fingerprint.pyuv run tests/test_voice_input_protocol.pyuv run tests/test_acoustic_keyword_memory.pyuv run tests/test_acoustic_keyword_bias_evaluation.pyuv run tests/test_acoustic_keyword_activation.pyuv run tests/test_acoustic_calibration.pyuv run tests/test_acoustic_calibration_activation.pyuv run tests/test_measurement_mode.pyuv run tests/test_benchmark_acoustic_calibration.pyuv run tests/test_delayed_cleanup_merge.pyuv run tests/test_macos_delayed_cleanup_destination.pyuv run tests/test_model_wallet.pyuv run tests/test_model_wallet_shadow.pyuv run tests/test_model_readiness_evidence.pyuv run tests/test_point_and_speak_resolver.pyuv run tests/test_drop_to_target.pyuv run tests/test_macos_drop_to_target_snapshot.pyuv run tests/test_voice_objects.pyuv run tests/test_spoken_edit_commands_runtime.pyuv run tests/test_voice_inbox.pyuv run tests/test_demonstration_drafts.pyuv run tests/test_competitor_benchmark.pyuv run tests/test_public_scorecard.pyuv run tests/test_personal_regression.pyuv run tests/test_support_bundle.pyuv run tests/test_whisper_face_gui.pyuv run tests/test_whisper_face_characters.pyuv run --locked --script dictate.py --native-gui-smoke-testuv run tests/test_network_egress.pyuv run tests/test_supply_chain_integrity.pyuv run tests/test_installers.pyuv run tests/test_repository_governance.pyuv run tests/test_macos_distribution.pyuv run tests/test_windows_distribution.pyuv run tests/test_safe_update_advisor.pyuv run tests/test_side_by_side_update.pyuv run tests/test_self_update.pyplatform is explicitly disclosed.
Live verification.
./setup.sh --verifywas run on macOS against thesechanges and exercised the new path end to end: it wrote a
0600 install.logholding the whole run, named that log on failure, and preserved its exit code
through the tee.
./setup.sh --uninstallcorrectly wrote no log and listedinstall.logas an inventory item. The verify itself reports a pre-existingParakeet ASR helper is missingon that machine, unrelated to this change.Disclosed as unavailable: no Windows machine was available, so
.\setup.ps1 --verifywas not run by hand. The new Windows PowerShell 5.1 jobin
windows-smoke.ymlexecutes those paths on a realwindows-latesthostinstead, and passes.
🤖 Generated with Claude Code