Skip to content

Make the environment module interface load-bearing #67

Description

@daniel-kindl

Problem

components/<id>/component.json declares an eight-field environment block, and
./install.sh --environments renders all eight as TSV. Four of the fields are decorative.
Every consumer reads them and then derives the value from the identifier instead.

bootstrap/lib/environments.sh:81 reads the whole record:

while IFS=$'\t' read -r id status container ini packages toolchain \
    bootstrap router inference home; do
    [ -n "$id" ] && [ "$container" != - ] || continue
    create_development_environment "$repo_root" "$container"

create_development_environment then rebuilds the path the module already declared:

# bootstrap/lib/environments.sh:64
run distrobox assemble create --file "$repo_root/distrobox/$name.ini" &&

$ini holds distrobox/web-dev.ini. It is read into a variable and never referenced.
bootstrap/lib/devbox.sh:66-69 has the same shape: it reads ten fields to use id and
router.

components/python-dev/install.sh:26 hard-codes box_bootstrap=(./bootstrap/python-dev.sh)
rather than reading the bootstrap field its own manifest declares.

The toolchain field is also a half-truth. bootstrap/python-dev.sh:29-33 sources two
manifests, and the module declares one:

. "$REPO_ROOT/manifests/python-dev.env"
# Agent installer URLs are currently shared with web-dev in this manifest.
. "$REPO_ROOT/manifests/toolchain.env"

Check E3 in verify/17-environments.sh:31-37 can only prove that a declared path exists. It
cannot see the undeclared one.

A distribution-specific command in a shared library

manifests/capabilities.json declares container-runtime as an any probe over podman
and docker:

{
  "id": "container-runtime",
  "summary": "an OCI container runtime",
  "probe": { "kind": "any", "value": [
    { "kind": "command", "value": "podman" },
    { "kind": "command", "value": "docker" } ] }
}

bootstrap/lib/environments.sh:60 asks one runtime:

if podman container exists "$name" 2>/dev/null; then
    ok "container $name already exists (left untouched)"

On a machine that has Docker and not Podman, the capability check passes, the existence
probe fails on every run, and distrobox assemble create is attempted again. AGENTS.md
rule 3 requires an installer to check the current state first and change only what does not
match, so the rule does not hold on that machine.

AGENTS.md rule 11 states that manifests/platforms.json is the only place that names a
distribution, a package manager or a distribution-specific command. Check P5 in
verify/16-platform.sh:16 searches for bazzite|ujust|rpm-ostree|apt-get|pacman|dnf. It
does not catch podman.

bootstrap/lib/host-packages.sh:18-19 has the same shape in the other direction:

for candidate in "$(command -v brew 2>/dev/null || true)" \
                 /home/linuxbrew/.linuxbrew/bin/brew; do

manifests/capabilities.json declares the same two locations, and the macOS adapter in
manifests/platforms.json declares /opt/homebrew and /usr/local. The library already
sources platform.sh and calls platform_hint for the failure message at :26. It asks
the adapter how to obtain Homebrew, and not where to find it.

A manual list stated twice

bootstrap/host.sh:141-151 hard-codes reminders that the manifests already declare:

manual 'Restore the SSH key (see docs/secrets.md), then: ssh-add ~/.ssh/id_ed25519'
manual 'Authenticate GitHub on the host: gh auth login --git-protocol ssh'

The same sentences appear in the manual arrays of components/daniel/component.json and
components/agentbox/component.json. Plan.manual in bin/toolkit-install:153-162 already
computes the de-duplicated set for a profile, and it has a unit test. The copies have
drifted: bootstrap/host.sh never states the Claude and Codex sign-in that
components/web-dev/component.json declares.

verify/17-environments.sh:48 hard-codes for expected in android-dev dotnet-dev python-dev rust-dev web-dev, nine lines after check E6 fails any file in bootstrap/ that names an
environment.

Goal

Make the environment block an interface that its consumers read, and keep a runtime-specific
command behind the capability layer.

Proposed direction

  • Read every declared field by name. environments.sh reads $ini. install.sh reads the
    bootstrap field. A module can then name a file that does not follow the convention, and
    adding an environment is adding one directory.
  • Declare every manifest an environment reads, including the shared manifests/toolchain.env.
  • Add a lookup beside platform_hint that answers where a capability lives, and let
    environments.sh and host-packages.sh use it. Move podman and
    /home/linuxbrew/.linuxbrew/bin/brew behind it.
  • Extend check P5 beyond package-manager names, so a hard-coded runtime command or install
    location fails.
  • Let bootstrap/host.sh read its manual list from the daniel closure.
  • Replace the hard-coded environment names in verify/17-environments.sh:48 with the module
    list that --environments already emits.

The alternative is to delete ini, packages, toolchain and bootstrap and state the
convention once. Either is better than a declared interface that its own implementation
ignores. Reading the fields gives more leverage, because --environments then becomes the
one interface between the Python resolver and the shell half.

Acceptance criteria

  • create_development_environment uses the declared ini path.
  • components/*/install.sh uses the declared bootstrap path.
  • Every manifest an environment bootstrap reads is declared by that module.
  • A probe builds a synthetic module whose ini path does not follow the convention, and proves the declared path is used. It needs no container.
  • The container existence check asks the capability layer, and names no runtime.
  • The Homebrew search asks the capability layer, and names no install location.
  • A converged machine that has Docker and not Podman reports the container as already present.
  • Check P5 fails on a hard-coded runtime command or install location in a shared library.
  • bootstrap/host.sh states no manual step that a component manifest already declares.
  • verify/17-environments.sh names no environment identifier.
  • ./verify.sh remains green.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is not workingrefactorStructural change without intended behavior change

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions