Distribution, not a language feature — this does not touch the freeze. The one judgement call is noted under the lux update item below.
Decision (2026-08-09): lux is permanently an Anderix project. That settles the identity question left open on 2026-07-31, when apt, brew, and WinGet were treated as one all-or-nothing package because no standalone anderix distribution identity existed yet. It does now, so the three legs can be judged on cost instead of branding. Homebrew and WinGet are in. apt stays deferred — not on identity grounds any more, but because it is the only one of the three that needs infrastructure you have to stand up and host, and the 2–3 personal CLI threshold for that still hasn't been met. Nothing here goes under Excelano; a personal teaching project carrying business branding is the regret path and the arrow only points anderix → excelano.
The motivation is trust at the front door, not reach. irm https://anderix.com/lux/install.ps1 | iex is the exact shape of the instruction a careful parent should refuse, and the Track B cold test puts lux in front of a stranger's parent who has no reason to extend the benefit of the doubt. winget install lux and brew install lux don't ask anyone to override a good habit. That framing also fixes the timing: these want to be live comfortably before the cold test runs, not after it, and certainly not held back for 1.0 — Track B is the gate on 1.0, so waiting for 1.0 gets the order exactly backwards.
1. lux update has to learn where lux came from — and this lands first
This is the blocking prerequisite, not a follow-on. update_cmd in src/main.rs:473 has no idea how lux was installed. On Unix it unconditionally runs the cargo-dist installer through the vanity URL, which writes into ~/.cargo/bin. A learner who installed with brew ends up with two binaries — the Homebrew one and the cargo-dist one — and PATH order decides which answers lux. The failure mode is silent: the command prints "Done. Run lux --version to see what you're on," and lux --version reports the old version. A later brew upgrade then overwrites only the copy brew knows about.
Windows is worse, and worse in a way that attacks the whole reason for doing this. The Windows branch prints irm … | iex, so the parent who deliberately chose WinGet to avoid piping a URL into a shell is told by lux itself to pipe a URL into a shell. If they follow it, they get a second copy outside WinGet's bookkeeping and a later winget upgrade quietly reverts them.
The fix costs no dependency: std::env::current_exe() is std. If the running binary sits under a Homebrew prefix (/opt/homebrew, /usr/local/Cellar, /home/linuxbrew/.linuxbrew) print brew upgrade lux and stop; if it sits under WinGet's packages directory print winget upgrade Anderix.lux and stop; otherwise behave exactly as it does today. Keep the command rather than removing it on those channels — the learner shouldn't have to remember how lux arrived on their machine, and handing back the right next step is the same trail-not-pointer stance the diagnostics take. Watch the symlink detail: Homebrew links into bin/ while the real file lives in the Cellar, and current_exe() canonicalizes on both Linux and macOS, so match on the resolved path and test it rather than assuming. Verify WinGet's actual install path when the manifest is built instead of hard-coding a guess.
Ordering matters more than the code here. The README, lux --help, and the post-update nudge all point people at lux update, so the moment a tap or a manifest exists, every user who follows lux's own advice lands in the shadowed state. This must be released before the first tap or manifest, not alongside them.
The freeze call: I read this as outside the freeze — it's install plumbing, it changes nothing a learner writes, and it isn't an enhancement so much as a precondition, since the defect only comes into existence once brew and WinGet do. It is the closest call so far, though. If the record should be unarguable, the clean alternative is to write it after 2026-09-05 and ship both channels behind it, which costs a few weeks and no ambiguity. David's call.
2. Before either manifest: check the name
Pushing into public indexes is a distribution act under a name, and lux is a common word with commercial users in several categories. Do a trademark search before submitting anything, not after. Separately, check what winget install lux actually resolves to: the package identifier is namespaced and safe as Anderix.lux, but the moniker is not namespaced, and the moniker is the short command that goes in the README and carries the whole trust argument. crates.io is the precedent that the plain name isn't always free — the crate had to become luxc because lux was a real, unclaimable 2016 package.
3. Homebrew — cheap and automated, do it first
cargo-dist already ends every release with publish-homebrew-formula. This needs an anderix/homebrew-tap repo, the tap declared in dist-workspace.toml, and a token, and then formulas update themselves on every tag. It must be a new anderix tap, not excelano/homebrew-tap — both for branding and because lux being alone in its own tap sidesteps the multi-repo tag race documented in ~/notes/build_release_gotchas.md, where four Rust repos tagged within a minute of each other collide committing to the shared tap.
Do this leg first. It is roughly an afternoon, it is fully automated afterwards, and it proves the Anderix publisher identity end to end on the channel where nothing can go slowly wrong.
4. WinGet — start early, because the latency isn't yours
This is the opposite of the brew leg on every axis. Nothing in the fleet automates it: neither xray nor xshape has a komac workflow, so every release is a hands-on submission. On top of that sits the Defender queue, where an unsigned Rust binary can stall for days and there is no code signing cert (declined 2026-07-30, and not to be re-pitched). The recipes and the hard-won gotchas are all in ~/notes/build_release_gotchas.md — read the whole WinGet section before starting, particularly the workflow token scope trap, the CRLF-in-manifests trap, and the rule that @wingetbot run is moderator-only. Expect a first submission under a brand-new Anderix publisher namespace to draw more scrutiny than a routine update to an established one, and start it early for that reason alone.
One decision to make deliberately rather than by default: a WinGet manifest that rots behind the curl installer is worse for trust than no manifest at all, because the parent who took the legible path ends up on an older lux than the one who didn't. Either wire an auto-update workflow — which the whole fleet would benefit from, since none of the eight packages has one — or decide that WinGet tracks meaningful versions rather than every patch, and say so plainly in the README.
5. Docs
The README's Installing section gains brew and WinGet alongside the existing one-liners, and the site install page follows (that belongs to the anderix.com session, not this one). Keep the vanity URLs as they are — they stay the canonical path for people who want them, and lux update keeps using them for cargo-dist installs.
Note also that issue #84 describes lux as already distributed "through crates.io, apt, Homebrew, and the vanity install URLs," which isn't true today and won't be true afterwards either, since apt is staying deferred. Worth correcting there when that issue is picked up.
Suggested order
lux update provenance detection, released.
- Name and moniker check.
anderix/homebrew-tap and the cargo-dist wiring.
- WinGet submission, plus the decision on manifest cadence.
- README.
Raised from the lux vision session; the reasoning behind the identity decision is in ~/notes/lux_vision.md. The all-or-nothing paragraph in ~/notes/lux.md is now superseded and should be updated by whoever owns that dossier.
Distribution, not a language feature — this does not touch the freeze. The one judgement call is noted under the
lux updateitem below.Decision (2026-08-09): lux is permanently an Anderix project. That settles the identity question left open on 2026-07-31, when apt, brew, and WinGet were treated as one all-or-nothing package because no standalone anderix distribution identity existed yet. It does now, so the three legs can be judged on cost instead of branding. Homebrew and WinGet are in. apt stays deferred — not on identity grounds any more, but because it is the only one of the three that needs infrastructure you have to stand up and host, and the 2–3 personal CLI threshold for that still hasn't been met. Nothing here goes under Excelano; a personal teaching project carrying business branding is the regret path and the arrow only points anderix → excelano.
The motivation is trust at the front door, not reach.
irm https://anderix.com/lux/install.ps1 | iexis the exact shape of the instruction a careful parent should refuse, and the Track B cold test puts lux in front of a stranger's parent who has no reason to extend the benefit of the doubt.winget install luxandbrew install luxdon't ask anyone to override a good habit. That framing also fixes the timing: these want to be live comfortably before the cold test runs, not after it, and certainly not held back for 1.0 — Track B is the gate on 1.0, so waiting for 1.0 gets the order exactly backwards.1.
lux updatehas to learn where lux came from — and this lands firstThis is the blocking prerequisite, not a follow-on.
update_cmdinsrc/main.rs:473has no idea how lux was installed. On Unix it unconditionally runs the cargo-dist installer through the vanity URL, which writes into~/.cargo/bin. A learner who installed with brew ends up with two binaries — the Homebrew one and the cargo-dist one — and PATH order decides which answerslux. The failure mode is silent: the command prints "Done. Runlux --versionto see what you're on," andlux --versionreports the old version. A laterbrew upgradethen overwrites only the copy brew knows about.Windows is worse, and worse in a way that attacks the whole reason for doing this. The Windows branch prints
irm … | iex, so the parent who deliberately chose WinGet to avoid piping a URL into a shell is told by lux itself to pipe a URL into a shell. If they follow it, they get a second copy outside WinGet's bookkeeping and a laterwinget upgradequietly reverts them.The fix costs no dependency:
std::env::current_exe()is std. If the running binary sits under a Homebrew prefix (/opt/homebrew,/usr/local/Cellar,/home/linuxbrew/.linuxbrew) printbrew upgrade luxand stop; if it sits under WinGet's packages directory printwinget upgrade Anderix.luxand stop; otherwise behave exactly as it does today. Keep the command rather than removing it on those channels — the learner shouldn't have to remember how lux arrived on their machine, and handing back the right next step is the same trail-not-pointer stance the diagnostics take. Watch the symlink detail: Homebrew links intobin/while the real file lives in the Cellar, andcurrent_exe()canonicalizes on both Linux and macOS, so match on the resolved path and test it rather than assuming. Verify WinGet's actual install path when the manifest is built instead of hard-coding a guess.Ordering matters more than the code here. The README,
lux --help, and the post-update nudge all point people atlux update, so the moment a tap or a manifest exists, every user who follows lux's own advice lands in the shadowed state. This must be released before the first tap or manifest, not alongside them.The freeze call: I read this as outside the freeze — it's install plumbing, it changes nothing a learner writes, and it isn't an enhancement so much as a precondition, since the defect only comes into existence once brew and WinGet do. It is the closest call so far, though. If the record should be unarguable, the clean alternative is to write it after 2026-09-05 and ship both channels behind it, which costs a few weeks and no ambiguity. David's call.
2. Before either manifest: check the name
Pushing into public indexes is a distribution act under a name, and
luxis a common word with commercial users in several categories. Do a trademark search before submitting anything, not after. Separately, check whatwinget install luxactually resolves to: the package identifier is namespaced and safe asAnderix.lux, but the moniker is not namespaced, and the moniker is the short command that goes in the README and carries the whole trust argument. crates.io is the precedent that the plain name isn't always free — the crate had to becomeluxcbecauseluxwas a real, unclaimable 2016 package.3. Homebrew — cheap and automated, do it first
cargo-dist already ends every release with
publish-homebrew-formula. This needs ananderix/homebrew-taprepo, the tap declared indist-workspace.toml, and a token, and then formulas update themselves on every tag. It must be a new anderix tap, notexcelano/homebrew-tap— both for branding and because lux being alone in its own tap sidesteps the multi-repo tag race documented in~/notes/build_release_gotchas.md, where four Rust repos tagged within a minute of each other collide committing to the shared tap.Do this leg first. It is roughly an afternoon, it is fully automated afterwards, and it proves the Anderix publisher identity end to end on the channel where nothing can go slowly wrong.
4. WinGet — start early, because the latency isn't yours
This is the opposite of the brew leg on every axis. Nothing in the fleet automates it: neither xray nor xshape has a komac workflow, so every release is a hands-on submission. On top of that sits the Defender queue, where an unsigned Rust binary can stall for days and there is no code signing cert (declined 2026-07-30, and not to be re-pitched). The recipes and the hard-won gotchas are all in
~/notes/build_release_gotchas.md— read the whole WinGet section before starting, particularly theworkflowtoken scope trap, the CRLF-in-manifests trap, and the rule that@wingetbot runis moderator-only. Expect a first submission under a brand-newAnderixpublisher namespace to draw more scrutiny than a routine update to an established one, and start it early for that reason alone.One decision to make deliberately rather than by default: a WinGet manifest that rots behind the curl installer is worse for trust than no manifest at all, because the parent who took the legible path ends up on an older lux than the one who didn't. Either wire an auto-update workflow — which the whole fleet would benefit from, since none of the eight packages has one — or decide that WinGet tracks meaningful versions rather than every patch, and say so plainly in the README.
5. Docs
The README's Installing section gains brew and WinGet alongside the existing one-liners, and the site install page follows (that belongs to the anderix.com session, not this one). Keep the vanity URLs as they are — they stay the canonical path for people who want them, and
lux updatekeeps using them for cargo-dist installs.Note also that issue #84 describes lux as already distributed "through crates.io, apt, Homebrew, and the vanity install URLs," which isn't true today and won't be true afterwards either, since apt is staying deferred. Worth correcting there when that issue is picked up.
Suggested order
lux updateprovenance detection, released.anderix/homebrew-tapand the cargo-dist wiring.Raised from the lux vision session; the reasoning behind the identity decision is in
~/notes/lux_vision.md. The all-or-nothing paragraph in~/notes/lux.mdis now superseded and should be updated by whoever owns that dossier.