feat: setup unification — one packages.json + root setup.sh/verify.sh, shims, dotfiles (#36, Phases 1-7)#37
Merged
Merged
Conversation
) Merge the three per-folder package JSONs (macOS 59, linux-desktop 61, linux-server 31 entries) into one root packages.json (96 entries) per the UNIFICATION.md migration rules: - package_manager is an object keyed by platform {macos,ubuntu,arch,server} - macOS work:true -> environment:["work"] - legacy curl manager (and macOS mongodb brew+install_command) folded into custom; install_command accepts a bare string or a per-platform object - <platform>_name overrides preserved and extended (macos_name/server_name) - packages whose priority/environment/optional genuinely differ across platforms keep one entry per platform group (git, jq, discord, hf, git-xet, notion, llama.cpp, octave, zsh-autosuggestions, zsh-syntax-highlighting, docker vs docker-ce) instead of new override machinery scripts/parity-check.sh replicates each legacy script's exact jq selection on its old JSON and diffs against the planned unified-core semantics on the new JSON, across platform x manager x priority x work/personal combos: 231 checks, 0 failures. Legacy JSONs and scripts untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t -e pnpm_install returned 1 when a tier had no pnpm packages ([[ -n ]] && run), which errexit turns into a mid-run crash: linux-desktop --optional died before the Docker step and macOS --optional before the work/App Store sections. Guard with an explicit early return instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…root setup.sh (#36) Extract the shared install engine from linux-desktop/setup.sh (the superset) into lib/core.sh: arg parsing, platform detection (uname + /etc/os-release, --platform/--distro override, --profile server), run/run_eval dry-run wrappers, jq package selection over root packages.json (envok/icfor/pname — lifted from the Phase 1 parity harness), npm/pnpm supply-chain cooldown, config deploys, and the shared linux desktop flow. Platform quirks live in platforms/<platform>.sh: - macos: Homebrew bootstrap, brew/cask/custom tiers, .zshrc line management, expat-pinned pyenv build, XDG Ghostty config, App Store reminders - ubuntu: apt+snap tiers, external apt repos, batcat/fdfind symlinks, curl-based Tailscale/Docker - arch: yay bootstrap via pacman, hardened batch installs with per-package retry, pacman -T pyenv deps, systemd enables - server: apt-only profile, Tailscale/Docker/Cockpit, homepage stack, .env fills, claude-code (stays hardcoded until Phase 5 folds it into data) Root setup.sh detects the platform and dispatches. Legacy per-folder scripts are untouched (shims come in Phase 4). Gate: scripts/dryrun-parity.sh runs old vs new --dry-run for all 22 platform × flag combos and diffs normalized install/eval lines (batches split per package, eval: prefix stripped, backup timestamps canonicalized, order-insensitive) — 22/22 pass. Phase 1 parity-check.sh still passes 231/231. shellcheck --severity=warning clean. Intentional changes vs legacy, absorbed by normalization or out of gate scope: macOS work packages install inside their priority tier instead of a separate section; server tailscale/docker/claude installs go through run_eval (legacy piped run's dry-run echo into 'sudo sh'); server default shell check reads the real login shell via getent instead of $SHELL. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tmux.conf is duplicated in linux-desktop/ and linux-server/ and missing from macOS/ entirely; ghostty.config is duplicated in macOS/ and linux-desktop/. Plan a dotfiles/ directory as a follow-up PR once #37 merges and the root engine owns config deploys. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extract the read-only verification logic from macOS/verify.sh and
linux-desktop/verify.sh into lib/verify.sh, driven by the root
packages.json via lib/core.sh's jq defs (envok/pname) and platform
detection. Root verify.sh dispatches; flags mirror setup.sh plus --all
(adds work+personal+optional and the priority-"none" tier).
Check semantics are platform-faithful ports, including the asymmetries:
legacy macOS has no command -v fallback for casks/pipx/app-store (only
brew formulas, labeled "via system/other"); legacy linux falls back to
command -v everywhere and probes pacman for custom packages. Labels use
the linux "name (mgr:rname)" format. Server has no legacy verify.sh and
is rejected with an error (out of Phase 3 scope).
Gate: scripts/verify-parity.sh runs legacy vs root verify for all 21
platform × flag combos on the same host and diffs normalized check
lines (sorted; legacy curl→custom and macOS mongodb brew→custom manager
renames; macOS "name (mgr:rname)" collapsed to the legacy install-token
form). 21/21 pass; mutation-tested (breaking the apt probe trips 9
failures, dropping the medium section trips all 21). Phase 1/2 gates
still pass (231 + 22 checks); shellcheck --severity=warning clean.
Intentional change vs legacy, excluded from the status diff but count-
asserted: pnpm-managed packages (codeburn) are now actually verified —
legacy scripts always reported them missing ("unknown manager"). Legacy
macOS verify checks work-tagged packages unconditionally (it predates
env filtering), so the gate passes --work --personal on macOS rows.
Live on this Ubuntu host: root verify.sh --work runs the same 57 checks
as legacy, 57 ok vs legacy's 56 ok + 1 missing (the pnpm fix above).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…oot entrypoint docs (#36) Convert macOS/setup.sh, linux-desktop/setup.sh, linux-server/setup.sh, macOS/verify.sh, and linux-desktop/verify.sh into thin shims that exec the root scripts with the right --platform, so documented per-folder usage keeps working. Shim output diffed byte-identical against direct root invocation for every platform (macOS via stubbed brew, and via zsh invocation). Behavior note: macOS verify now gates work-tagged packages on --work/--all (matching setup.sh) instead of checking them unconditionally — the legacy script predated env filtering. Delete scripts/dryrun-parity.sh and scripts/verify-parity.sh: once the legacy scripts are shims, both gates compare the root scripts against themselves and pass trivially. They did their job — last meaningful green run (22/22 and 21/21) at a8d4149. scripts/parity-check.sh stays: it compares JSONs, not scripts, and remains valid until Phase 5 deletes the legacy JSONs. Docs: root README gains a Setup section for setup.sh/verify.sh; per-folder READMEs point at the root entrypoints; new repo CLAUDE.md documents the architecture and conventions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… dry-run smoke gate (#36) Review findings on the Phase 4 commit, fixed: - Shims pin --platform AFTER "$@" so the folder's identity wins over stray --distro/--platform flags (core_parse_args is last-wins; previously `bash linux-server/setup.sh --distro ubuntu` would have installed the desktop set on the server — legacy scripts ignored stray flags). - linux-desktop shims hard-error on non-Linux hosts, restoring the legacy unsupported-distro guard (previously they'd auto-detect macos on a Mac and run the Homebrew installer). - Shims resolve the repo root from BASH_SOURCE (fallback $0 for zsh), so bash-sourcing one no longer execs a setup.sh relative to the cwd. - verify_parse_args accepts --profile, so `verify.sh --profile server` hits the intended server rejection instead of warning and running the auto-detected platform's checks. - New scripts/dryrun-smoke.sh + CI job: root setup.sh --dry-run must exit clean with a full install plan on all four platforms — without it, nothing executed the root scripts after the parity gates were deleted. - macOS/ scripts are bash now: lifted the shellcheck exclusions in pre-commit and CI, dropped them from the zsh -n globs. - Doc fixes: CLAUDE.md documents verify.sh's real flag set; README no longer implies linux-server has a verify shim; TODO marks the deleted gates in past tense; brew_packages.md expat note points at platforms/macos.sh. Verified: shim output byte-identical to direct root invocation (incl. via zsh and with pin-override attempts); smoke gate green on all platforms; shellcheck clean including macOS/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9 tasks
… claude-code into data (#36) Delete macOS/macOS_packages.json, linux-desktop/linux_desktop_packages.json, and linux-server/linux_server_packages.json — root packages.json has been the single source of truth since Phase 2, and nothing read them anymore. scripts/parity-check.sh goes with them (its inputs are gone); final run immediately before deletion: 231/231 green. Fold the server claude-code install into packages.json: claude-code gains package_manager.server = custom, and platforms/server.sh reads the command via custom_cmd instead of hardcoding it (dry-run output unchanged). Close the legacy custom-install gap: customs that the engine runs by name (pyenv, nvm, claude-code, tailscale, docker) are now marked handled_by_setup: true; print_custom_reminders moves from platforms/server.sh into lib/core.sh and the desktop flow calls it, so ubuntu prints manual install commands for git-xet and claude-desktop instead of silently skipping them (legacy scripts never executed those entries' install_commands; they're reminders rather than auto-runs because both need sudo/repo setup). Verified: full-flag dry-run diffed against a pre-change baseline on all four platforms — byte-identical except the new ubuntu reminders section (server diff is only the backup timestamp); dryrun-smoke 4/4; live verify.sh --work green on the ubuntu host; shellcheck + jq clean. Resolves the UNIFICATION.md open questions: server stays a platform key (A), install_command keeps both string and object forms, priority "none" tier kept. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ified stack (#36) Merge origin/main (PRs #28 powerlevel10k/LACT, #35 railguard fixes, #38 claude-hud) into the unification branch. The two PRs that touched the legacy setup stack landed while this branch converted it to shims, so their features are ported into the unified system as part of the conflict resolution: - packages.json gains zsh-theme-powerlevel10k (arch/yay, high) and lact (arch/yay, medium) — the entries main added to the now-deleted linux-desktop/linux_desktop_packages.json. - The shared desktop flow (lib/core.sh) deploys ~/.p10k.zsh from linux-desktop/p10k.zsh.example via deploy_config — the step main added to the legacy linux-desktop/setup.sh, which stays a shim. - Everything else from main merges clean: zshrc.examples, ghostty font, p10k.zsh.example, LACT configs/docs, railguard and claude-hud config. Gate: per-platform full-flag dry-run diffed against the pre-merge snapshot — macOS and server unchanged (timestamp only); ubuntu/arch show exactly the intended additions (p10k deploy, the two arch packages, and the zshrc update caused by main's new zshrc.example content). dryrun-smoke 4/4; shellcheck + jq clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s tmux (#36) Create dotfiles/ for configs shared verbatim across platforms and move the two byte-identical duplicates there: tmux.conf (was linux-desktop/ + linux-server/) and ghostty.config (was macOS/ + linux-desktop/). All engine deploy sites repoint to $SETUP_ROOT/dotfiles/: the shared desktop flow and ghostty_deploy_linux in lib/core.sh, the server zsh/tmux block, and the macOS ghostty step. macOS additionally gains a tmux.conf deploy (via the shared deploy_config) — the legacy macOS setup never deployed one, leaving Macs on the default green status bar while Linux boxes got the configured blue top bar. The zshrc variants intentionally differ per platform and stay per-folder. Ghostty keeps a single universal config — no overlay mechanism until an OS-specific setting actually exists. Gate: per-platform full-flag dry-run diffed against a pre-change baseline — identical on ubuntu/arch/server (timestamp noise only); macOS shows exactly the new tmux deploy step. dryrun-smoke 4/4; live verify.sh --work green; shellcheck clean. Docs updated (CLAUDE.md, linux-server README files list, ghostty_config.md — also synced its stale config snippet with the real file). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…h server override (#36) Move linux-desktop/zshrc.example to dotfiles/zshrc.example as the shared zshrc base, deployed by the new deploy_zshrc helper in lib/core.sh. The helper implements the override system: a platform folder shipping its own zshrc.example wins over the dotfiles base. Only linux-server/ does — it is headless, so its zshrc drops the Ghostty/fastfetch/notification hooks. macOS is unaffected: its flow appends lines to the user's existing ~/.zshrc (add_to_zshrc) rather than deploying a file, and macOS/zshrc.example stays as reference documentation (zshrc-upgrade.md parity pointers now reference dotfiles/zshrc.example). Gate: per-platform full-flag dry-run diffed against a pre-change baseline — macOS identical; ubuntu/arch differ only in the cp source path (now dotfiles/); server differs only in backup timestamps, proving the override still selects linux-server/zshrc.example. dryrun-smoke 4/4; shellcheck and zsh -n clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…h stack (#36) dotfiles/zshrc.example is now the one zshrc for macOS, Ubuntu, and Arch. macOS-specific bits are guarded so they no-op on Linux: Homebrew PATH + completions FPATH (before compinit), brew source paths for antidote / powerlevel10k / fzf alongside the /usr/share ones, BSD `ls -G`, brewup, PIPX_DEFAULT_PYTHON via pyenv shims (Tahoe bottle fix), and nvm `--delete-prefix default` (brew-node shadowing). GPG_TTY moves into the shared base. zsh_plugins.txt moves to dotfiles/ (list was already what the macOS migration plan called for, including zsh-notify). platforms/macos.sh switches from appending lines to the user's ~/.zshrc (add_to_zshrc — deleted) to deploying the shared zshrc + plugin list via the engine; the previous ~/.zshrc is backed up on first deploy. In-process exports during setup are unchanged. packages.json: zsh-antidote/zoxide/fzf/bat/fd gain macos:brew (antidote via macos_name); new terminal-notifier entry (zsh-notify's macOS backend); the brew zsh-autosuggestions/zsh-syntax-highlighting entries are gone — antidote manages those plugins now (brew uninstall the old formulae after migrating; the server's apt entries are untouched). verify.sh gains macOS checks for ~/.zshrc, ~/.zsh_plugins.txt, ~/.tmux.conf, and the brew antidote path, mirroring the linux extras. macOS/zshrc.example and macOS/zshrc-upgrade.md deleted — the upgrade plan is fully absorbed (every item on its list ships here). Docs/TODO updated. Known caveat (pre-existing, now also on macOS): the deployed p10k config sets POWERLEVEL9K_INSTANT_PROMPT=verbose while fastfetch prints during init — p10k may warn on new tabs; set instant prompt to quiet in ~/.p10k.zsh if it nags. Gate: per-platform full-flag dry-run vs pre-change baseline — ubuntu/arch/ server timestamp-only; macOS shows exactly the intended changes (antidote+ zoxide in high tier, fzf/bat/fd/terminal-notifier in medium, the two plugin formulae gone, 10 add-to-zshrc lines replaced by zshrc+plugins deploys). zsh -n, shellcheck, jq, dryrun-smoke 4/4, live verify.sh --work all green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
High-resolution mouse wheels emit ~7-10 events per notch and Ghostty reports each as a full click, flooding mouse-mode TUIs — micro inserts the mangled sequence fragments as text. mouse-scroll-multiplier = 0.25 cuts the burst below the breaking point and fixes the too-fast scroll feel in TUIs (ghostty-org/ghostty#4259). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
date +%s%3N and bare timeout are GNU-isms — BSD date emits a literal N (breaking the elapsed-ms arithmetic) and macOS only ships gtimeout via coreutils. now_ms() falls back to python3; the runner is resolved via command -v and skipped if absent. pytest is now invoked as python -m pytest (uv run --no-sync python -m pytest when uv.lock is present): console-script bins are absent in uv --no-sync venvs, and --no-sync keeps the hook from re-resolving envs with pinned torch builds. A probe import skips the suite cleanly when pytest isn't in the env. Fixes #33 Fixes #30 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…l on macOS - uv joins the package list (brew / pipx / yay — pipx rides the existing ubuntu tier, no engine changes). Fixes #24 - brew cask installs use --adopt so an app already in /Applications (manual install) is taken over instead of hard-failing the whole bulk-install tier. Fixes #31 - claude-code on macOS switches from brew cask to the official curl installer via the shared claude_code_step (self-updates in place, already guarded + dry-run aware; verify.sh already probes it with command -v claude). Part of #39 Gated: per-platform full-flag dry-run diff shows only the intended lines; scripts/dryrun-smoke.sh passes on all four platforms. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… alias - apt_bootstrap gates the GitHub CLI apt repo on the sources file instead of command -v gh, so machines with a stale distro gh (Ubuntu universe 2.45, Projects-classic GraphQL breakage) get migrated to the official repo on their next run. Fixes #25 - Arch now installs rustup (arch_name) instead of the raw rust package, matching Ubuntu's existing ubuntu_name override; a new rust_toolchain_step runs 'rustup default stable' on fresh boxes where rustup ships without a toolchain. cargo's ~/.cargo/bin joins PATH in the shared zshrc. Part of #39 - The shared zshrc aliases the App Store Tailscale's bundled CLI on macOS (kept App Store per network-extension entitlements). Fixes #22 Gated: per-platform dry-run diff shows only intended lines; dryrun-smoke, shellcheck, zsh -n, jq all pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ence batch) Web-verified against upstream/AUR/brew as of 2026-06: - git-xet (arch): AUR git-xet-bin packages the defunct pre-acquisition XetHub client (flagged out-of-date, 0 votes), not HF's git-xet — switch to the official install.sh reminder already used on ubuntu - hf (arch): extra/python-huggingface-hub is stale (flagged 2026-05) — pipx like ubuntu, restoring the [cli] extras - micro (ubuntu): noble apt frozen at 2.0.13 — official classic snap tracks releases (server stays apt; Debian carries newer) - nvtop (ubuntu): noble 3.0.2 vs 3.3.x — snap published by the upstream author - obs-studio (ubuntu): codify the official obsproject PPA in apt_bootstrap (this desktop already runs PPA 32.1.2 manually) - docker (macos): cask renamed docker-desktop; bare token now collides with the CLI-only formula Part of #39 Gated: per-platform dry-run diff shows only intended lines; dryrun-smoke, shellcheck, jq pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…not Debian
The server platform targets the Ubuntu Server 24.04 box, which has the
same frozen noble universe as the desktop AND ships snapd preinstalled —
so the apt/snap split between the two noble platforms was unjustified.
snap_install_tier moves from ubuntu.sh to core.sh and the server flow
gains a snap section.
Docs carried the same wrong premise ("Raspberry Pi / Debian"): the Pi is
a separate, not-yet-onboarded target (TODO.md); server.sh header and
CLAUDE.md now say so.
Part of #39
Gated: server dry-run gains only the snap section lines; other
platforms unchanged; dryrun-smoke, shellcheck, jq pass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
From a brew-leaves audit of the Mac mini M4 and the MBP (all Macs are M-series): - New entries: forgejo-cli, go, codespell, mlx-lm, swiftlint, swiftformat, opencode (anomalyco tap / AUR opencode-bin / curl reminder on Ubuntu), obsidian (classic snap on Ubuntu), anki, zen-browser (flatpak reminder on Ubuntu — engine has no flatpak manager) - rust/shellcheck/ripgrep/nmap gain macos: brew on their existing entries - Server migrates off apt zsh-autosuggestions/zsh-syntax-highlighting to antidote: noble has no zsh-antidote package, so setup clones it to ~/.antidote and deploys linux-server/zsh_plugins.txt (headless subset — no zsh-notify, no fpath completions since compinit runs before antidote load in the server zshrc) - TODO.md records the per-Mac live-run cleanup lists (testing leftovers, pinned pythons, brew zsh plugins, claude-code cask) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…view of 2fb881b Code-review findings on the brew-leaves audit commit: - lib/verify.sh: probe forgejo-cli via its actual binary (fj) and zen-browser via its flatpak app id — the generic custom-manager fallback could never see either, so verify reported them missing forever after a correct install - platforms/server.sh: gate the antidote install on antidote.zsh (not the bare directory), and pre-clone the plugins with `antidote bundle` right after deploying zsh_plugins.txt, while network is provably up — first login no longer depends on GitHub - linux-server/zshrc.example: also require ~/.zsh_plugins.txt before running antidote load, restoring the deleted apt sourcing's graceful degradation when files are missing - linux-server/apt_packages.md: replace the stale apt plugin entries with antidote, and correct micro/nvtop to snap (stale since fe5999f); renumbered Gates: shellcheck + zsh -n clean, dryrun-smoke passes all 4 platforms, server dry-run diff shows only the new pre-clone line. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The git-clone path was built on a wrong premise: noble/universe carries zsh-antidote 1.9.4-1 (verified via apt-cache; the earlier "no noble package" claim came from a misread packages.ubuntu.com lookup). The server now installs it like every other platform: - packages.json: server: apt; drop the custom install_command + handled_by_setup; restore the platform-neutral description - platforms/server.sh: remove the clone block; the plugin pre-clone step now sources /usr/share/zsh-antidote/antidote.zsh - linux-server/zshrc.example: source the apt path (same as the desktop zshrc), keeping the zsh_plugins.txt guard - linux-server/apt_packages.md: antidote entry is an apt package again Gates: jq/shellcheck/zsh -n clean, dryrun-smoke passes all 4 platforms, server dry-run diff shows only zsh-antidote joining the apt high tier, the clone lines gone, and the pre-clone path change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
verify green except the by-design manual installs; one leftover (apt's stale micro shadows the snap) plus a general caveat for the remaining live runs: setup migrates install methods but never uninstalls the old copy, so check for shadowed binaries after each run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The only installer was the arch-only zsh-theme-powerlevel10k yay entry, so Ubuntu and macOS silently fell back to the vcs_info prompt. Load romkatv/powerlevel10k through antidote everywhere instead: plugins.txt gains the theme, the zshrc drops the system-path sourcing and guards the fallback on $+functions[p10k], and the packages.json entry goes away. Instant prompt verbose→quiet — fastfetch runs during ghostty init and would warn on every tab now that p10k actually loads. Verified live on the Ubuntu desktop (p10k loads, dryrun-smoke green x4). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The statusLine command hardcoded /usr/bin/node, which doesn't exist on nvm-managed machines (issue #34 task 3, deferred by PR #38 and then lost when the PR auto-closed the issue). Resolve via command -v node with a newest-nvm-version glob fallback for contexts where nvm isn't on PATH. TODO gains a tracker for the other dropped #34 task (claude-hud display config not in the repo). Verified live: the HUD renders through ~/.nvm/.../bin/node. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The symlink made the repo file Claude Code's writable user-settings store, so every model switch, plugin install, and /config change landed in the repo as permanent dirt. install.sh now copies the template (migrating the old symlink in place; re-runs back up and reset the live copy), and railguard install's machine-specific rewrite is undone by redeploying the template over the live file instead of snapshotting the repo file. settings-drift.sh diffs template vs live, normalizing key order and ignoring the per-machine model key — which is also why the template no longer pins advisorModel. Migrated and verified live on the Ubuntu desktop: live file is a real copy carrying the machine's model pin, drift check reports in sync, dryrun-smoke green x4. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
desktop_main and macos.sh carried near-identical copies of the related-scripts menu, diverging only in which verify shim they pointed at — and both shims exec the root verify.sh, which auto-detects the platform anyway. Hoist one list next to print_related_scripts and point it at root verify.sh; the per-folder shims remain valid entrypoints, the menu just stops preferring them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…config The deploy only renamed config.ghostty at the Application Support path, but Ghostty also reads pre-1.2.3 plain `config` there — and that path loads AFTER XDG with later values overriding, so a legacy Mac config would silently defeat the deployed one. Rename both filenames at both locations, and write the XDG target as plain `config` to match the Linux deploy (both names are valid; uniform is better). Found while confirming Ghostty's XDG support on macOS (it works; the comment claiming so was already right). The Ubuntu desktop even had a stray empty config.ghostty shadowing scenario waiting — cleaned up locally. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…se negatives Tested end-to-end on a fresh macOS install (clean OS, no prior setup). - platforms/macos.sh: install brew/cask packages one-by-one instead of all-at-once so a single failure doesn't abort the tier; collect per-package errors into a BREW_FAILURES array and print a summary at the end - platforms/macos.sh: wire up p10k deploy (was Linux-desktop only) - dotfiles/p10k.zsh.example: move p10k config here as shared cross-platform source (replaces linux-desktop/p10k.zsh.example) - lib/core.sh: update Linux desktop_main p10k path to dotfiles/ - lib/verify.sh: fix ghostty check — config deploys as `config`, not `config.ghostty` - packages.json: add macos_name=Spark Desktop for Spark Mail (App Store bundle name differs from display name) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace Forgejo-specific option 5 with a generic "Self-Hosted Git Server" to accommodate additional providers (Codeberg, etc.) as new menu entries later. Rename IS_FORGEJO→IS_SELF_HOSTED and FORGEJO_HOSTNAME→GIT_HOSTNAME; prompt for SSH alias instead of hardcoding "forgejo". Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Xcode project generator from a YAML/JSON spec; macOS-only brew formula, placed alongside swiftlint/swiftformat. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ulises-c
marked this pull request as ready for review
June 15, 2026 05:17
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
All phases of the setup-script unification (#36, spec in UNIFICATION.md): one root
packages.json+ one rootsetup.sh+ one rootverify.shbacked by shared engines, with platform quirks isolated in small modules. The per-folder scripts are thin shims onto the root entrypoints and the legacy per-folder JSONs are gone.Phase 1 — unified data + parity harness (
b9ebac3)packages.json(96 entries) merged from the three per-folder JSONs per the migration rules:package_managerkeyed by{macos,ubuntu,arch,server},work:true→environment:["work"], legacycurlmanager folded intocustom,<platform>_nameoverrides preserved.scripts/parity-check.shreplicates each legacy script's exact jq selection and diffs it against the planned unified-core semantics: 231 checks, 0 failures.Phase 2 — engine extraction + root dispatcher (
a40fa60)lib/core.sh: arg parsing, platform detection (--platform/--distrooverride,--profile server), dry-run wrappers, jq selection engine, npm/pnpm supply-chain cooldown, config deploys, shared linux desktop flow.platforms/{macos,ubuntu,arch,server}.sh: bootstrap + manager quirks only (Homebrew, apt repos + snap, yay hardening, server services).setup.sh: detect platform → source core + module → run.scripts/dryrun-parity.shdiffs normalized old-vs-new--dry-runoutput for all 22 platform × flag combos — 0 failures (mutation-tested: demoting one package's priority trips 9 failures).Phase 3 — shared verify engine + root verify.sh (
a8d4149)lib/verify.sh: read-only check engine over rootpackages.json, reusinglib/core.sh's jq defs and platform detection. Per-manager probe semantics are platform-faithful ports ofmacOS/verify.shandlinux-desktop/verify.sh, including their asymmetries (legacy macOS has nocommand -vfallback for casks/pipx/app-store; legacy linux falls back everywhere and probes pacman for custom packages).verify.sh: flags mirrorsetup.shplus--all; server is rejected (no legacy verify to unify — Phase 3 scope is macOS + linux-desktop).scripts/verify-parity.shruns legacy vs root verify for all 21 platform × flag combos — 0 failures on normalized check lines (mutation-tested: breaking the apt probe trips 9 failures, dropping a section trips all 21).codeburn) are now actually verified — legacy scripts always reported them missing ("unknown manager").Phase 4 — thin shims + docs (
95e7425, hardened in8286bb9)macOS/setup.sh,macOS/verify.sh,linux-desktop/setup.sh,linux-desktop/verify.sh,linux-server/setup.share now one-line shims thatexecthe root scripts — documented per-folder usage keeps working (verified byte-identical output, includingzsh macOS/...invocation). The macOS/server shims pin--platformafter"$@"so the folder identity wins over stray flags; the linux-desktop shims hard-error on non-Linux hosts, preserving the legacy unsupported-distro guard.dryrun-parity.sh/verify-parity.shdeleted: with the legacy scripts gone they self-compare trivially. Last meaningful green run (22/22 and 21/21) ata8d4149. In their place,scripts/dryrun-smoke.sh(rootsetup.sh --dry-runmust exit clean with a full plan on all four platforms) runs locally and as a new CI job.parity-check.sh(JSON-level) stays valid until Phase 5.verify_parse_argsgains--profile, soverify.sh --profile serverhits the intended server rejection instead of warning and checking the wrong platform.CLAUDE.md.macOS/verify.shnow gates work-tagged packages on--work/--all(matching setup.sh) instead of checking them unconditionally.Phase 5 — cleanup (
0ba9c22)scripts/parity-check.shwith them (its inputs are gone; final run immediately before deletion: 231/231).packages.json(package_manager.server: "custom");platforms/server.shreads the command viacustom_cmdinstead of hardcoding it.handled_by_setup: true;print_custom_remindersmoved intolib/core.shand the desktop flow now calls it, so ubuntu prints manual install commands forgit-xet/claude-desktopinstead of silently skipping them (reminders, not auto-runs — both need sudo/repo setup).install_commandkeeps both string and object forms;priority: "none"tier kept.Phase 6 — merge main + port in-flight features (
54e1b1e)origin/main(PRs feat: powerlevel10k support and LACT R9700 tuning guide #28 powerlevel10k/LACT, fix(railguard): skip test hook when runner missing; add path-fence guidance rules #35 railguard fixes, feat(claude): track claude-hud plugin config and harden install #38 claude-hud). The two PRs that touched the legacy setup stack landed while this branch converted it to shims, so their features were ported as part of the conflict resolution:packages.jsongainszsh-theme-powerlevel10k(arch/yay, high) andlact(arch/yay, medium) — the entries main added to the now-deleted linux-desktop JSON.~/.p10k.zshfromlinux-desktop/p10k.zsh.exampleviadeploy_config— the step main added to the legacylinux-desktop/setup.sh, which stays a shim.Phase 7 — dotfiles consolidation (
56bdd45)dotfiles/for configs shared verbatim across platforms:tmux.conf(was duplicated in linux-desktop/ + linux-server/) andghostty.config(was duplicated in macOS/ + linux-desktop/). All engine deploy sites repointed; per-folder copies deleted.~/.tmux.conf— the legacy macOS setup never did, leaving Macs on the default green status bar.de7e43a):dotfiles/zshrc.exampleis the shared base, deployed by the newdeploy_zshrchelper; a platform folder shipping its ownzshrc.exampleoverrides it. Onlylinux-server/does — headless, so its zshrc drops the Ghostty/fastfetch/notification hooks. macOS is not part of zshrc deploys (its flow appends to the user's existing~/.zshrc;macOS/zshrc.examplestays reference-only).dotfiles/; the server diff proves the override still selectslinux-server/zshrc.example.a95fbc2): the dotfiles zshrc is fully cross-platform — macOS bits guarded on/opt/homebrew/$OSTYPE(Homebrew PATH+FPATH, brew antidote/p10k/fzf paths,ls -G,brewup,PIPX_DEFAULT_PYTHON, nvm--delete-prefix);zsh_plugins.txtmoves todotfiles/; macOS deploys both via the engine instead of appending lines to~/.zshrc(add_to_zshrcdeleted; old~/.zshrcbacked up on first deploy). antidote/zoxide/fzf/bat/fd/terminal-notifier gainmacos: brew; the brew zsh-autosuggestions/zsh-syntax-highlighting entries are dropped (antidote manages the plugins —brew uninstallthe old formulae after migrating). verify.sh gains the matching macOS config checks.macOS/zshrc.example+zshrc-upgrade.mddeleted — that upgrade plan ships in full here.Bug fix surfaced by the gate (
21061bf)Legacy
--optionalruns crashed mid-script underset -ewhen a tier had no pnpm packages — linux-desktop died before the Docker step, macOS before the work/App Store sections. Fixed in the legacy scripts so the gate compares intended behavior.Intentional behavior differences (documented in the gate headers)
run_eval— legacy pipedrun's dry-run echo intosudo sh.getentinstead of$SHELL.--work --personalon macOS rows.Test plan
bash scripts/parity-check.sh— 231/231bash scripts/dryrun-parity.sh— 22/22 (run on Ubuntu host; covers all four platforms via--dry-run)bash scripts/verify-parity.sh— 21/21 (same host; both sides resolve host state identically) — both gates re-run green ata8d4149immediately before the shim conversion, then deletedbash scripts/dryrun-smoke.sh— 4/4 platforms (also a CI job now)zshinvocation, and pin-override attempts likelinux-server/setup.sh --distro ubuntu)8286bb9bash scripts/parity-check.sh— final 231/231 run immediately before its deletion in Phase 5--dry-runon all four platforms vs pre-change output: identical except the intended ubuntu reminders section; liveverify.sh --workstill green on the Ubuntu hostverify.sh --workgreenzsh -non the unified zshrc; reviewed (4 findings fixed: stale TODO section, missing macOS verify checks, stale plugin docs; p10k instant-prompt caveat documented)verify.sh --workon Ubuntu host — same 57 checks as legacy (57 ok vs legacy 56 ok + 1 missing: the pnpm fix)shellcheck --severity=warningclean on all new scriptsbash setup.sh --dry-run, then real run;bash verify.sh --work) before merge — per UNIFICATION.md only the Arch path can be exercised live🤖 Generated with Claude Code