IMPLEMENTATION STATUS — Base ships Phase 1 of this design: built-in artifact definitions live in
lib/base/artifact-registry.yamlwith schema version1, andcli/python/base_setup/registry.pyloads and validates that bundled registry. Workspace overlays and repo-local registries remain future work.
This document records how Base evolves its built-in artifact mapping without turning project setup into arbitrary plugin execution.
Today, Base has two artifact behaviors backed by the bundled registry:
python-packageartifacts resolve to pip packages installed in the Base-managed project virtual environment.toolartifacts resolve to Homebrew packages installed on the host system.
On Ubuntu/Debian, the provider layer may adapt a small supported subset of
portable tool artifacts to system packages. The initial mapping is
tool:bats-core to system package bats; macOS keeps using the Homebrew
bats-core package for the same manifest artifact.
That behavior is useful but too implicit. The supported artifact list, manager selection, package name, install target, and diagnostics are encoded in Python. Adding another artifact family would require changing command code before Base has a stable vocabulary for describing what it can manage.
- Make supported artifacts visible as data before setup, check, doctor, and docs need to explain them.
- Keep artifact execution behind typed manager adapters that Base owns and tests.
- Let future Base versions support more artifact types without broad command rewrites.
- Improve diagnostics by reporting where an artifact definition came from and which manager/check path owns it.
- Preserve the current trust model: manifests request supported artifacts, but do not inject executable setup commands.
- No arbitrary project-provided shell hooks.
- No third-party Python plugin loading during setup.
- No automatic remote registry download.
- No repo mutation as part of artifact resolution.
- No attempt to replace native package managers such as Homebrew, uv, npm, Go modules, Maven, or Gradle.
The registry should make Base's orchestration vocabulary more explicit, not make Base responsible for every installer or dependency solver.
Base should continue to delegate:
- host packages to Homebrew or a project-owned Brewfile;
- Python environments to the current Base project venv path or a future
structured
python:contract such as uv; - language dependencies to native files such as
go.mod,package.json,pom.xml, orbuild.gradle; - project tasks to explicit task runners such as mise, just, or repo-local scripts;
- container and machine images to tools such as devcontainers, Docker, and Nix.
Registry entries should describe artifacts Base can check and reconcile through typed adapters. They should not absorb project-specific installers into Base.
Base should introduce the registry in phases.
Move the current hard-coded _ARTIFACTS table into a built-in declarative
registry bundled with Base, for example lib/base/artifact-registry.yaml.
Command behavior remains in Python manager adapters.
Example:
version: 1
artifacts:
- type: python-package
name: pytest
manager: pip
package: pytest
target: project-venv
version_policy: requested
check:
kind: python_import
module: pytest
- type: tool
name: kubectl
manager: homebrew
package: kubernetes-cli
target: system
version_policy: latest-only
check:
kind: homebrew_package
package: kubernetes-cliThe Python API can stay close to the current code:
@dataclass(frozen=True)
class ArtifactDefinition:
name: str
artifact_type: str
manager: str
package: str
target: str
version_policy: str
registry_source: strget_artifact_definition(type, name) should continue to return one
definition or None, so existing setup/check call sites can migrate without
changing their control flow.
After the built-in registry is data-backed, Base can optionally support a
machine-local workspace overlay configured in ~/.base.d/config.yaml.
Example:
artifact_registry:
overlays:
- ~/work/base-artifacts.yamlOverlay entries should be constrained to manager adapters Base already
implements. For example, an overlay can map tool:internal-cli to Homebrew
package company/internal-cli, but it cannot define a new executable install
script.
Overlay loading should be explicit in diagnostics and JSON output:
{
"artifact_type": "tool",
"artifact": "internal-cli",
"manager": "homebrew",
"package": "company/internal-cli",
"target": "system",
"registry_source": "/Users/example/work/base-artifacts.yaml"
}Repo-local registry files should remain deferred until there is a proven need. They are more convenient, but they change the trust model because cloning a repo would also make new setup definitions available.
If Base adds this later, it should be opt-in from user-local config, not
automatic from base_manifest.yaml.
The first schema should be deliberately small:
| Field | Required | Meaning |
|---|---|---|
type |
yes | Manifest artifact type, such as tool or python-package. |
name |
yes | Manifest-facing artifact name. |
manager |
yes | Base-owned adapter, such as homebrew or pip. |
package |
yes | Manager-facing package identifier. |
target |
yes | Install target, such as system or project-venv. |
version_policy |
yes | Supported version behavior, such as requested or latest-only. |
check.kind |
yes | Base-owned check implementation key. |
metadata |
no | Display-only or future extension data. |
Unknown fields should fail validation unless the schema version explicitly allows them. That keeps typos from silently changing setup behavior.
Registry data should describe artifacts. Manager adapters should own behavior.
Each adapter should provide:
- validation for definitions it can handle;
- check behavior for
basectl checkandbasectl doctor; - reconcile behavior for
basectl setup; - dry-run rendering;
- structured diagnostics fields.
The initial adapter set is the current behavior:
pipinstalls intotarget: project-venv;homebrewinstallstarget: systemartifacts withversion_policy: latest-only, reports installed-but-outdated packages during check/doctor, and upgrades outdated packages during setup.
Adding a new manager should require a Python adapter and tests. Registry data alone should not be enough to execute new behavior.
Registry-backed artifacts should behave the same across setup, check, doctor, dry-run, and JSON output.
basectl setup should resolve each manifest artifact through the registry, then
delegate reconciliation to the manager adapter. The registry selects the
adapter; it does not store shell commands to execute.
basectl setup --dry-run should render the adapter-owned action that would run,
including the manager, package, target, version policy, and registry source. A
malformed registry entry or unsupported manager should fail before rendering a
partial install plan.
basectl check should report whether each requested artifact is satisfied using
the registry-backed check definition and the manager adapter. Unsupported,
malformed, or policy-incompatible definitions should return actionable findings
instead of collapsing into a generic missing-artifact message.
basectl doctor should include the same finding IDs as check, plus enough
structured detail for automation to identify the registry entry, manager, and
package mapping that produced the finding.
JSON output should treat registry-backed fields as first-class data rather than embedding them only in human-readable messages.
Artifact diagnostics should include the definition source and manager path wherever the output is structured. Human messages can stay compact, but JSON should make support triage possible:
artifact_typeartifactmanagerpackagetargetversion_policyregistry_sourcefinding_id
Unsupported artifact errors should distinguish:
- no definition found;
- definition found but manager unsupported by this Base version;
- version policy mismatch;
- malformed registry entry.
- Add the bundled registry file and parser.
- Load the bundled registry in
base_setup.registry. - Keep the current
python-packagefallback for one release if needed, but warn in diagnostics when a package resolves through fallback rather than an explicit definition. - Add registry validation tests for duplicate entries, unknown managers, invalid targets, unknown fields, and malformed files.
- Add JSON diagnostics fields while preserving existing human output.
- Document the registry surface in
README.mdand this docs map. - Only then consider workspace overlay support.
The shipped Phase 1 implementation is:
- built-in YAML registry;
- no overlay support;
- no repo-local registry;
- same manager behavior as today;
- focused parser/validation tests;
- unchanged manifest syntax.
That slice makes Base's supported artifacts inspectable while avoiding new trust or execution risks.