Skip to content

Package optimization: per-platform fields, tags, validator + interactive picker#52

Merged
ulises-c merged 7 commits into
mainfrom
feat/packages-per-platform-tags
Jun 24, 2026
Merged

Package optimization: per-platform fields, tags, validator + interactive picker#52
ulises-c merged 7 commits into
mainfrom
feat/packages-per-platform-tags

Conversation

@ulises-c

@ulises-c ulises-c commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Package optimization

Cleans up packages.json, hardens its schema, and adds an interactive category picker on top — plus fresh-install and review-driven fixes. 12 files, ~+510/−130, packages.json down to 96 entries (8 duplicate pairs merged).

1. Per-platform field forms + merged duplicates

priority, optional, and environment now accept a scalar or a per-platform object (like install_command already did), resolved via new prfor/optfor/envfor jq defs. This lets one entry serve platforms that differ in tier/optionality/gating, so the duplicate entries that only existed for that reason are merged: git, git-xet, hf, llama.cpp, discord, notion, octave, and jq.

  • Proven backward-compatible: old vs new engine produce byte-identical dry-run output on all four platforms.

2. Category tags + schema validator

  • Every entry gains a required tags array (18-tag controlled vocabulary, metadata only).
  • New scripts/validate-packages.sh (jq) enforces the platform vocabulary, the tag set, environment shape, and the no-silent-drop rule — every platform a package targets must resolve a valid priority tier and a boolean optional. Also catches duplicate names, non-object/empty/missing package_manager, missing name, and non-array documents without crashing jq. Wired into pre-commit and CI.

3. Interactive category picker

Running setup.sh bare on a TTY (no selection flag) now prompts the user to choose what to install; base essentials always install, picked categories and enabled work/personal apps layer on top.

  • New flags: --base (base only) and --tags <csv> (base + named categories), with warnings on unknown/empty tags.
  • Implemented via a tagok() filter that reads TAG_FILTER_ACTIVE/SELECTED_TAGS from the env — inactive by default, so all flag-driven and CI runs are unchanged. Prompt is skipped on the server profile and in non-interactive/CI runs.
  • The dedicated custom-install steps (tailscale, claude-code, docker) gate on a pkg_selected predicate so they honor the selection too; the server profile keeps the filter inactive, so they install as before there.

4. Fresh-install fix

On a fresh Ubuntu desktop, the nvm installer (piped curl) ran before curl was installed and aborted the whole run under set -euo pipefail — which also prevented the later Rust-default and zsh-shell-switch steps from executing. apt_bootstrap now installs ca-certificates + any missing curl/wget/jq right after apt update, before any install script or apt-repo key fetch.

5. docker as source of truth

platform_docker_optional hardcoded the same command docker's install_command already held (dead data). Added the missing server entry and routed install through custom_cmd docker, matching tailscale/pyenv/nvm/claude-code; post-install group step stays in code.

Also

  • Migrated poetryuv (uv bumped to high priority; poetry removed) across packages.json, README.md, and the macOS docs.

Review & verification

  • Two review passes (adversarial multi-dimension + a max-effort local /code-review): zero confirmed correctness defects; all findings either fixed or verified as intended behavior. The duplicate-name validator check caught the un-merged jq entry during review.
  • shellcheck clean, validator passes (96 entries) and rejects every malformed-fixture class, dry-run smoke green on all four platforms.

Worth a live check before relying on it

Dry-run on this host can't fully exercise the fresh-Ubuntu path (host already has curl/docker). Best validated on a VM/container: the curl bootstrap and the now-reachable Rust-default + zsh-shell-switch, and the picker + docker install on a machine without docker preinstalled.

🤖 Generated with Claude Code

ulises-c and others added 6 commits June 22, 2026 17:52
Let priority/optional/environment accept a per-platform object (like
install_command already did), resolved via new prfor/optfor/envfor jq defs in
lib/core.sh; envok gains a $plat arg. Proven backward-compatible: old vs new
engine produce byte-identical dry-run output on all four platforms.

Merge the 7 duplicate entries that only existed to vary these fields per
platform (git, git-xet, hf, llama.cpp, discord, notion, octave) into single
entries. Add a required 'tags' category array (18-tag controlled vocabulary,
metadata only) to all 97 entries.

Add scripts/validate-packages.sh (jq) enforcing the platform vocabulary, the
tag set, environment shape, and the no-silent-drop rule (every targeted
platform must resolve a valid priority tier + boolean optional). Wired into
pre-commit and CI (lint.yml).

Also migrate poetry -> uv (uv bumped to high priority; poetry removed) across
packages.json, README.md, and the macOS docs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bare 'setup.sh' on a TTY (no selection flag) now prompts the user to choose
which categories to install; base essentials are always included. Adds a
tagok() filter to the engine selection (CORE_JQ_DEFS) that reads
TAG_FILTER_ACTIVE/SELECTED_TAGS from the environment — base (high-priority
packages) and enabled work/personal apps bypass the filter, everything else
must match a selected tag.

New flags: --base (base only) and --tags <csv> (base + named categories) for
non-interactive use. The prompt is skipped on the server profile and in
non-interactive/CI runs; the filter is inactive by default so all flag-driven
and CI runs are byte-identical (smoke gate unchanged: 79/31/19/39).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On a fresh Ubuntu desktop (no curl preinstalled) the nvm step — which pipes the
curl installer into a shell via eval at the top of the Linux flow — failed under
'set -euo pipefail' and aborted the whole run before rust_toolchain_step and
set_default_shell could execute. That is why the Rust default toolchain and the
zsh login-shell switch never happened either; both steps run later in the flow.

apt_bootstrap now installs ca-certificates plus any missing curl/wget/jq right
after 'apt update', before any install script or apt-repo key fetch runs. wget
is bootstrapped too since the eza/obs repo setup needs it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tion

Adversarial review of the branch surfaced these (all medium/low):

validator (scripts/validate-packages.sh):
- detect duplicate package names (group_by .name) — this caught a real one:
  jq was still two entries, so merge it like the other per-platform duplicates
  (macOS brew low/optional, apt/yay medium/required). 97 -> 96 entries.
- guard keys-walking on object type so a non-object/missing package_manager
  yields the clean diagnostic instead of crashing jq and masking other errors
- flag empty {} package_manager (targets zero platforms)
- flag missing/empty name
- assert the document top-level is a JSON array

selection (lib/core.sh, setup.sh):
- --tags warns on tokens matching no category and on an empty result instead
  of silently degrading to base-only
- apt_bootstrap prints its intent under --dry-run (command -v probes the host,
  so the bootstrap was invisible in dry-run/smoke)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ction

These handled_by_setup custom packages ran as unconditional steps, so --base
and --tags installed them regardless of the picker (e.g. --tags development
still pulled in tailscale + claude-code). Add a pkg_selected predicate that
mirrors the tier/envok/tagok selection and guard each step with it:

  --base            -> none of the three
  --tags networking -> tailscale; --tags ai-coding -> claude-code
  --tags containers --optional -> docker

tailscale/claude-code gate inside their step functions; the desktop docker call
gates at its (already --optional) call site. The server profile keeps the filter
inactive, so all three still install there (server docker stays unconditional,
since docker has no server entry in packages.json and that path is essential).
Addresses the low-severity tag-bypass findings from the branch review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
platform_docker_optional hardcoded the same get.docker.com command that
docker's packages.json install_command already held, leaving the field dead and
misleading. Add the missing "server":"custom" key (the server installs docker
but had no entry for it) and read the command via custom_cmd docker, mirroring
tailscale/pyenv/nvm/claude-code. The post-install group step stays in code since
no install_command can express it. Resolves the docker dead-data follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ulises-c
ulises-c marked this pull request as ready for review June 24, 2026 09:48
The tag-vocabulary jq query was duplicated in core_prompt_selection and
core_validate_tag_selection; extract a single core_tag_vocabulary helper both
call. Cosmetic DRY cleanup from the max-effort review — no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ulises-c
ulises-c merged commit ecf08c2 into main Jun 24, 2026
5 checks passed
@ulises-c
ulises-c deleted the feat/packages-per-platform-tags branch June 24, 2026 10:22
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