Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions .ai-context/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,14 @@ boundary. Do not split `setup_common.sh` into sourced fragments by topic.
its own path, sources `base_init.sh`, and dispatches to either a Base command,
an explicit Bash script, or an interactive Base runtime shell.

`bin/base-wrapper` is the Python execution wrapper. It runs Base Python packages
with the selected project virtual environment and a `PYTHONPATH` that includes
Base's `lib/python` and `cli/python`; the `base_cli` provider is selected by
`lib/base/base_cli_runtime.sh` and is not copied into this repository.
`bin/base-wrapper` selects the interpreter through `--project`. With
`--project base` and a `base_*` package it uses the Base interpreter and the
isolated `python -I` module entrypoint, limiting Base imports to `cli/python`
and the selected `base_cli` provider. Other selections use the selected project
environment and module execution. The provider is resolved by
`lib/base/base_cli_runtime.sh`, not copied into this repository. See
`docs/manifest-command-trust.md` for the isolation boundary and
`docs/python-package-ownership.md` for package responsibilities.

Run-bundle propagation is an internal capability, not user configuration.
`basectl` reuses inherited bundle state only when the physical path is a real
Expand All @@ -68,7 +72,8 @@ replaced with a fresh bundle, and finalization repeats the validation before
writing metadata or removing temporary data.

Project Python ownership is explicit. A top-level `python:` mapping (including
`python: {}`) or a `python-package` artifact requires a project runtime.
`python: {}`), a `python-package` artifact, or `test.requirements` requires a
project runtime.
Shell-only manifests use Base's own venv for setup/check/doctor control-plane
work, do not inherit Base bootstrap packages into a project venv, and report
workspace venv state as `not_applicable`. `project.languages` remains taxonomy.
Expand All @@ -77,8 +82,11 @@ The broader runtime separation remains tracked in #1611.
Manifest-declared test requirements are an explicit project-runtime contract.
`test.requirements` is repository-relative and currently supports direct
package names with optional exact `==` versions. Setup installs the file into
the routed project environment; check, doctor, and test preflight fail closed
for missing files, invalid declarations, or missing/mismatched packages.
the routed project environment. Static check/doctor inspection validates the
declaration but reports package readiness as unverified without executing the
project runtime. `--verify-project-runtime` explicitly enables that runtime
verification; missing/mismatched installed requirements then produce failures.
Test preflight verifies requirements before running the declared test command.
uv-managed projects keep dependency ownership in `pyproject.toml` and their
lockfile instead of using this requirements-file path.

Expand Down
7 changes: 5 additions & 2 deletions .ai-context/COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,11 @@ the v1.x compatibility window; new automation should use `--debug-wrapper`.
fixes.
- `basectl test [project]` - run a project's declared test command. A
manifest may declare `test.requirements` as a repository-relative direct
requirements file (package extras are unsupported); setup installs it, check/doctor report missing or
mismatched packages, and test preflights it before execution.
requirements file (package extras are unsupported); setup installs it.
Static check/doctor inspection reports package readiness as unverified;
`--verify-project-runtime` explicitly enables installed-package probes.
Test preflight checks requirements after manifest-command approval and before
executing the declared test command.
- `basectl build [project] [target...]` - run declared build targets for the positional, explicit, or nearest project.
- `basectl demo [project]` - run a declared interactive demo script.
- `basectl run [project] <command>` - run a declared command for the positional, explicit, or nearest project.
Expand Down
4 changes: 2 additions & 2 deletions .ai-context/STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ The current command surface covers:
`project.languages` metadata and Python uv opt-in
- project Python runtime requirements through `python.requires_python`
- shell-only setup/check/doctor routing through the Base-owned runtime without
a control-plane-only project venv; explicit `python:` and `python-package`
manifests keep project Python ownership
a control-plane-only project venv; `python:`, `python-package` artifacts,
and `test.requirements` each request project Python ownership
- cleanup, logs, local command history, and privacy-conscious history reports
- local config inspection
- guided project onboarding and read-only workspace onboarding summaries
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ numeric next development line is tracked in `DEVELOPMENT_VERSION`.

### Fixed

- Run activation-source discovery with Base's interpreter while preserving the
selected project's manifest and activation context.
- Use the same readable blocked-trust explanations in workspace test preflight,
individual project command diagnostics, and `basectl trust status`.
- Make `basectl workspace test` preflight manifest-command trust across the
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ Follow [STANDARDS.md](STANDARDS.md). In particular:

- Keep Bash control flow explicit. Do not rely on `set -e`.
- Keep command implementations under `cli/bash/commands/<command>/`.
- Keep Base-owned Python package code under `cli/python/` or `lib/python/` as
appropriate. The reusable `base_cli` framework is maintained in the
- Keep Base-owned Python packages and integration helpers under `cli/python/`,
following [Python Package Ownership](docs/python-package-ownership.md). The reusable `base_cli` framework is maintained in the
standalone `base-cli` repository.
- Put tests next to the command, library, or package they validate.
- Keep public command launchers in `bin/` thin.
Expand Down
16 changes: 8 additions & 8 deletions STANDARDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,11 +324,10 @@ Python packages for Base commands live under:
cli/python/<package>/
```

Shared Python libraries live under:

```text
lib/python/<package>/
```
Base-owned shared helpers also live under `cli/python/` in the package that
owns their responsibility, including `base_cli_adapters`. The reusable
`base_cli` framework lives in the standalone `base-cli` repository. See
[Python Package Ownership](docs/python-package-ownership.md).

Bash should invoke these packages with:

Expand Down Expand Up @@ -511,8 +510,9 @@ and project activation scripts.

### 6.3 Python Packages

Base Python command packages should live under `cli/python`. Shared Python
libraries should live under `lib/python`.
Base Python command packages and shared Base integration helpers live under
`cli/python`. Keep helpers in the owning package; reusable CLI framework
behavior belongs in the standalone `base-cli` repository.

Keep package tests next to the package:

Expand Down Expand Up @@ -767,7 +767,7 @@ Before adding code, ask where it belongs:
| Is this artifact reconciliation? | Python layer, invoked by Bash through `base-wrapper` |
| Is this a public executable? | Small real launcher in `bin/` |
| Is this a Bash helper used by multiple commands? | `lib/bash/<module>/` |
| Is this a Python helper used by multiple CLIs? | `lib/python/<package>/` |
| Is this a Python helper used by multiple CLIs? | Owning package under `cli/python/`; reusable CLI framework changes belong in `base-cli` |
| Is this a project-owned Go CLI? | Project Go module under `cmd/<tool>/` and `internal/`, declared in `base_manifest.yaml` |
| Is this repeated Go CLI boilerplate? | Document the convention first; consider a shared Go helper only after real repetition |
| Is this command-specific behavior? | The command's module and tests |
Expand Down
3 changes: 3 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ For the public fork workflow and the three contributor paths, see
For GitHub labels, milestones, Projects, issue assignment, branch names, and
worktree-based PR trains, see [GitHub Workflow](github-workflow.md).

For removal scope, retained history, and verification, see
[Uninstall And Retention](uninstall.md).

For release preparation, tagging, GitHub Releases, and the Homebrew tap update,
see [Release Process](release-process.md).

Expand Down
4 changes: 4 additions & 0 deletions docs/adopter-golden-path.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,10 @@ For a clean exit after a rehearsal, use the verified removal path:

Removal previews by default. Use `--yes` only after reviewing the plan; an
applied `--all` removal runs its verification check automatically.
Project run bundles are removed with project caches, while Base-level history
and run bundles remain. Checkouts, manifests, and project-local environments
are retained. See [Uninstall](uninstall.md) for the complete scope and recovery
procedure.

## Validation Record

Expand Down
19 changes: 13 additions & 6 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -482,15 +482,17 @@ orchestration actions. The design rule is delegation-first:
aliases, or functions. Source paths must be relative to the project root and
must resolve inside that root.
- Let Base own Base-aware package reconciliation and any project virtual
environment explicitly requested by `python:` or `python-package` manifest
contracts. Shell-only manifests use the Base runtime for control-plane work.
environment explicitly requested by `python:`, `python-package` artifacts,
or `test.requirements`. Shell-only manifests use the Base runtime for
control-plane work.
- Do not run arbitrary project setup hooks until Base has a clear safety
contract for dry-run behavior, interactivity, setup diagnostics, and broader
side effects. See [setup-hooks.md](setup-hooks.md) for the setup no-hooks
decision and future reconsideration criteria.

Base owns the curated tool artifact registry only for things it must manage
directly. The current registry is `cli/python/base_setup/registry.py`.
directly. Its data lives in `lib/base/artifact-registry.yaml`; the loader and
validation live in `cli/python/base_setup/registry.py`.

The optional top-level `brewfile` field delegates ordinary Homebrew dependencies
to Homebrew's native `brew bundle` flow. The path is relative to the project root
Expand All @@ -513,14 +515,19 @@ requests from supported-but-unavailable interpreters. See
boundary.

A manifest requires project Python when it contains a top-level `python:`
mapping, including an explicit empty `python: {}`, or declares at least one
`python-package` artifact. `project.languages: [python]` remains taxonomy and
does not independently create an environment. When neither project-Python
mapping, including an explicit empty `python: {}`, declares at least one
`python-package` artifact, or declares `test.requirements`.
`project.languages: [python]` remains taxonomy and
does not independently create an environment. When none of these project-Python
contract is present, setup, check, and doctor run Base-owned parsing and
reconciliation from `~/.base.d/base/.venv`, skip Base's default project Python
bootstrap artifacts, and do not require `<project-root>/.venv`. Workspace
status reports that venv as `not_applicable` rather than missing.

The [Python runtime decision table](python-manifest.md#when-a-project-python-runtime-is-required)
is the complete trigger reference. Requirements-file installation and static
versus executing verification are described in [Testing](testing.md).

This is the shell-only vertical slice of the broader runtime-boundary work in
[#1611](https://github.com/basefoundry/base/issues/1611). Activation and
project-owned run, test, build, and demo command environment semantics retain
Expand Down
34 changes: 17 additions & 17 deletions docs/artifact-adapter-registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ 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.
The bundled YAML records the supported definitions, manager, package name,
install target, version policy, and check kind. Python owns schema validation
and typed manager behavior. Adding a new manager or artifact family still
requires implementation and tests; a registry row cannot introduce arbitrary
execution.

## Goals

Expand Down Expand Up @@ -55,8 +56,8 @@ 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;
- Python environments to the Base-managed project venv or the implemented
`python.manager: uv` delegation contract;
- language dependencies to native files such as `go.mod`, `package.json`,
`pom.xml`, or `build.gradle`;
- project tasks to explicit task runners such as mise, just, or repo-local
Expand All @@ -68,15 +69,16 @@ typed adapters. They should not absorb project-specific installers into Base.

## Recommended Shape

Base should introduce the registry in phases.
Phase 1 is implemented. Phases 2 and 3 below are proposals, not accepted input
to the current bundled-registry loader.

### Phase 1: Declarative Built-ins
### Phase 1: Declarative Built-ins (Implemented)

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.
The built-in declarative registry is bundled at
`lib/base/artifact-registry.yaml`. Command behavior remains in Python manager
adapters.

Example:
Complete current schema example:

```yaml
version: 1
Expand All @@ -98,10 +100,10 @@ artifacts:
version_policy: latest-only
check:
kind: homebrew_package
package: kubernetes-cli
```

The Python API can stay close to the current code:
`check` accepts only `kind`; the package comes from the entry's `package` field.
The loader exposes this Python record:

```python
@dataclass(frozen=True)
Expand All @@ -116,9 +118,7 @@ class ArtifactDefinition:
registry_source: str = ""
```

`get_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.
`get_artifact_definition(type, name)` returns one definition or `None`.

### Phase 2: Workspace Overlay Registry

Expand Down
11 changes: 10 additions & 1 deletion docs/cache-ownership-and-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ The Base history index may contain metadata for both owners so that
`basectl history` can provide one cross-project view. That index is metadata;
raw project logs and caches remain in the project-owned tree.

## Proposed directory tree
## Directory tree

The default cache root is `~/Library/Caches/base` on macOS and `~/.cache/base`
on Ubuntu/Debian. `BASE_CACHE_DIR` can override either default. The ownership
Expand Down Expand Up @@ -183,6 +183,15 @@ files are created with mode `0600`; raw log files use the same mode. Directory
permissions remain controlled by the host cache filesystem and do not change
the ownership boundary described above.

## Uninstall Retention

Uninstall is a separate lifecycle boundary from per-run temporary cleanup.
Project uninstall removes the whole selected `projects/<project-id>/` subtree,
including logs and run metadata for its checkout IDs. `--all` removes all
project namespaces and `base/cache/`, while retaining `base/runs/` and
`base/history/`. The history index may consequently refer to removed project
logs. See [Uninstall And Retention](uninstall.md) for verification and recovery.

## Runtime invariants

1. Every accepted public invocation has one owner and one top-level run bundle;
Expand Down
2 changes: 1 addition & 1 deletion docs/ci-supply-chain-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ this policy if the rule changes.

The security job must keep these checks:

- Bandit over `cli/python` and `lib/python`
- Bandit over `cli/python`
- `pip-audit` over `requirements-dev.txt`
- ShellCheck errors over tracked Bash entry points, scripts, and `.bash`
helpers; managed Zsh startup and completion sources are checked with `zsh -n`.
Expand Down
14 changes: 9 additions & 5 deletions docs/command-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ with `BASE_SETUP_PYTHON_FORMULA` when a workspace needs a different formula.
After this Bash bootstrap layer creates Base's own Python environment, setup
installs Base bootstrap Python packages into that environment. Shell-only
project reconciliation runs from that Base runtime and does not copy those
packages into a project venv. Projects that explicitly declare `python:` or a
`python-package` artifact keep the project-runtime path: Base first seeds the
packages into a project venv. Projects that explicitly declare `python:`, a
`python-package` artifact, or `test.requirements` keep the project-runtime path: Base first seeds the
target project venv with `bootstrap: true` default artifacts and then invokes
the Python project setup layer through `base-wrapper --project <project>`.
Prerequisite profiles are opt-in. Use `--profile dev` to install Base
Expand Down Expand Up @@ -195,7 +195,7 @@ manifest trust.
| `basectl logs --open` | Open the newest matching log in `PAGER` or `EDITOR`. | `--command <name[,name...]>` |
| `basectl logs --tail` | Tail and follow the newest matching log. | `--command <name[,name...]>`, `--lines <count>` |
| `basectl clean` | Preview cleanup of completed Base run bundles and component caches; use `--yes` to delete matches. Active runs are retained and reported. | `--older-than <age>`, `--keep-last <count>`, `--dry-run`, `--yes` |
| `basectl uninstall <project>\|--all` | Preview or remove Base-managed trust, check state, external project environments, and persistent runtime caches. Project checkouts and manifests are never deleted; `--all` also removes workspace settings and managed shell startup sections. | `--project <name>`, `--workspace <path>`, `--dry-run`, `--yes`, `--verify` |
| `basectl uninstall <project>\|--all` | Preview or remove Base-managed trust, check state, external project environments, and runtime caches, including project run bundles. Base-level history remains; see [Uninstall And Retention](uninstall.md). Project checkouts and manifests are never deleted; `--all` also removes workspace settings and managed shell startup sections. | `--project <name>`, `--workspace <path>`, `--dry-run`, `--yes`, `--verify` |
| `basectl config path` | Print the local Base config path. | none |
| `basectl config show` | Show local Base config as redacted JSON. | none |
| `basectl config doctor` | Diagnose local Base config. | none |
Expand Down Expand Up @@ -562,8 +562,8 @@ For uv-managed projects, Base delegates setup to `uv sync`, including any
activation and project commands, and skips
Base-managed `python-package` reconciliation. See
[Python Manifest Section](python-manifest.md).
Projects without a top-level `python:` section or any `python-package`
artifacts are treated as shell-only for setup and diagnostics. Base parses and
Projects without a top-level `python:` section, any `python-package`
artifacts, or `test.requirements` are treated as shell-only for setup and diagnostics. Base parses and
reconciles those manifests from its own runtime, does not create a project
`.venv` for its control plane, and reports workspace venv state as
`not_applicable`. An explicit `python: {}` keeps the existing Base-managed
Expand Down Expand Up @@ -726,6 +726,10 @@ root. This is the fastest way to roll out shared repo or Project schema repairs
across a local repository set while keeping each repository's `repo configure`
behavior idempotent.

Workspace setup/test/update have fixed per-operation deadlines. See
[Execution Deadlines And Cancellation](workspace-manifest.md#execution-deadlines-and-cancellation)
for timeout, interruption, process-group, output-capture, and retry boundaries.

Use `basectl workspace setup --dry-run` to preview project setup across the
expected repositories, then run `basectl workspace setup` to execute it. Setup
walks the manifest in order, skips the active `base` control plane and
Expand Down
Loading
Loading