10. Introduce a Layered Bundle and Profile Model¶
Date: 2026-05-02
Status¶
Accepted. Amended 2026-08-22: the engine that expands profiles: is the rhiza Claude
Code plugin, not rhiza-cli — see the amendment at the end of this record. The
template-side structure decided here is unchanged.
Context¶
Rhiza's bundle model (ADR-0006) maps named groups of files to a files: list and lets
downstream projects select bundles by name. This works well for feature adoption, but
over time a structural problem emerged:
Feature bundles conflate local tooling with hosted automation.
Several bundles that represent stand-alone local features also directly own
.github/workflows/* files:
testsownsrhiza_ci.yml,rhiza_codeql.yml,rhiza_weekly.ymlbookownsrhiza_book.ymlmarimoownsrhiza_marimo.ymldockerownsrhiza_docker.ymldevcontainerownsrhiza_devcontainer.yml
This makes it impossible to adopt, say, the testing tooling without also syncing GitHub Actions workflows into the project. Projects that are:
- local-only experiments or research codebases
- hosted on GitLab, not GitHub
- private repositories that do not use GitHub Actions
- early-stage projects not yet ready for hosted automation
...have no clean way to express "give me the tooling but not the workflows". They must
either accept unwanted files or manually add exclude: patterns in their
.rhiza/template.yml.
A second problem is discoverability at init time. There is no concept of a recommended
starting set. Users running rhiza init see a flat list of bundle names with no
guidance about which bundles are typically used together, what a sensible first setup
looks like for their hosting context, or which combinations are known to work well.
Decision¶
We will introduce a two-layer model separating file ownership from user-facing intent.
Layer 1: Bundles (unchanged role, refined boundaries)¶
Bundles remain the atomic sync unit. Every synced file belongs to a bundle. Bundles
own files via files:, declare hard dependencies via requires:, and declare soft
suggestions via recommends:.
Key rule change: feature bundles must be local-first. A feature bundle should
include only the files needed to use that feature locally. Platform-specific automation
files (.github/workflows/*, .gitlab/) belong in dedicated platform overlay bundles,
not in the feature bundle itself.
Concretely, GitHub workflow files are moved out of feature bundles and into new GitHub-specific overlay bundles:
github-tests: CI/CodeQL/weekly workflows for thetestsfeaturegithub-book: book publishing workflow for thebookfeaturegithub-marimo: Marimo notebook workflow for themarimofeaturegithub-docker: Docker build and publish workflow for thedockerfeaturegithub-devcontainer: DevContainer publish workflow for thedevcontainerfeature
These overlay bundles have standalone: false and requires: the corresponding feature
bundle and the github bundle.
The existing github bundle keeps base GitHub repository automation: the sync and
release workflows, dependabot.yml, issue and discussion templates, and secret scanning.
Layer 2: Profiles (new)¶
A new top-level profiles: section in template-bundles.yml defines named presets that
expand to a set of bundles.
Rules:
- profiles do not own files directly
- profiles reference bundles only, not raw file paths
- profiles represent stable, tested user intents rather than every possible combination
- selecting a profile is equivalent to selecting all its constituent bundles
- users may combine profile selection with additional manual bundle selection
Initial profiles:
local: local-first development with no hosted workflow filesgithub-project: GitHub-hosted project with CI/CD and release automationgitlab-project: GitLab-hosted project with GitLab CI pipelines
Config¶
Downstream projects will be able to express profile selection in .rhiza/template.yml:
repository: Jebel-Quant/rhiza
ref: v0.9.0
profiles:
- local
templates:
- presentation # any extra bundles beyond the profile
rhiza-cli is responsible for expanding profiles to bundles, merging manual bundle
selections, deduplicating, and validating the result. The CLI implementation is a
separate concern; this ADR covers the template repository structure and metadata.
Consequences¶
Positive¶
- Clean local mode: Selecting the
localprofile installs no.github/workflows/*files. Projects that do not use GitHub Actions get exactly what they need. - Explicit platform separation: It is now clear which files belong to the feature
and which belong to its GitHub automation. Bundle names reflect this —
testsis the feature;github-testsis the GitHub automation layer for that feature. - Better onboarding: Profiles give
rhiza inita recommended starting path without forcing users to understand every bundle upfront. - Composable: Power users retain full control by selecting individual bundles. Profiles are an addition, not a replacement.
- GitLab parity: The split makes it straightforward to produce matching
gitlab-tests,gitlab-book, and similar overlay bundles for GitLab CI in a follow-on step, without restructuring feature bundles again.
Neutral¶
- More bundles: The total bundle count increases as each feature bundle gets one or
more platform overlay siblings. The
template-bundles.ymlfile grows. The existing automated structure tests mitigate risk. - Migration for existing consumers: Downstream projects using bundles such as
teststoday will need to explicitly addgithub-testsif they want the CI workflows. Thegithub-projectprofile bundles both, so profile users are unaffected.
Negative¶
- CLI changes required: Profiles are defined in the template repository, but
consumed by
rhiza-cli. Until the CLI supports theprofiles:key in.rhiza/template.yml, users can approximate local mode by selecting only local bundles manually, or the CLI can expand profiles atinittime and write the resulting bundle list. - Profile maintenance overhead: Profiles must be kept current as bundles are added or renamed. Automated tests enforce that every bundle referenced in a profile exists.
Amendment: the engine that expands profiles (2026-08-22)¶
Two places above name rhiza-cli as the consumer of this metadata: the Decision's note
that the CLI expands profiles to bundles, and the CLI changes required negative. The
engine is rhiza-claude now — rhiza-cli is
unpublished and archived, see the amendment to
ADR-0005 — and that negative is discharged
rather than inherited: /rhiza:init writes a profiles: key chosen from the repo's host
and language, and /rhiza:update expands it, so nobody has to approximate local mode by
hand-picking bundles.
The rhiza init mention in the Better onboarding consequence reads /rhiza:init today.
Nothing else about the layered model changed.