Replies: 4 comments
|
I'm open for alternative implementations and suggestions as you obviously have better context how to solve such things. |
|
Thanks for the detailed write-up. I traced this through the current config and bootstrap implementations, and I agree with the separation between selection and resource composition. I opened #12100 with the smaller provenance step proposed here. It retains the declaring config, A couple of implementation findings affect the collection follow-up:
My preferred next step after #12100 is a bootstrap-specific selected-root loader, reusing the monorepo discovery internals, followed by conflict-aware composition for AI-assisted — Tool: Codex; model: openai/GPT-5; version: unavailable. |
|
First — thanks @jdx. Composition shipped within days of this discussion, the Follow-up with verified results on v2026.8.9, which shipped the selected-root composition from this discussion (#12105, #12132), checked against a fixture mirroring this repo's shape ( Confirmed working:
One divergence from the conflict semantics discussed here. The comment above states:
The shipped composer errors before that leaf expansion: any two declarations of the same target are a conflict, even when the source trees hold disjoint leaves ( Sharing one directory across roots is One more behavior worth flagging for adoption planning: a parent's Minimal repro with materialized fixture trees and a fix-detector script: https://github.com/Guria/mise-config-roots-repro Is target-keyed conflict the intended MVP boundary, with the leaf-identity validation quoted above as a follow-up? Leaf-keyed validation — union the source trees, conflict only when two roots claim the same leaf — would unblock the original use case; the custom loop in this repo exists mostly to perform exactly that union by hand. |
|
Confirmed — this was an implementation gap in the config-root composer. It keyed sibling whole-file declarations by the directory target before I opened #12190 with the fix. It:
The focused unit tests and The inherited parent Thanks for the careful verification and the fix-detector repro. AI-assisted — Tool: Codex; model: openai/GPT-5; version: unavailable. |
Uh oh!
There was an error while loading. Please reload this page.
Summary
I maintain a dotfiles repository that composes a workstation configuration from multiple selected configuration roots.
The repository uses mise configuration environments for selection. This works, but the selection layer currently requires a manually maintained template:
The problem is not simply that mise lacks environment selection. The problem is that a configuration composed from several selected roots has no first-class bootstrap resource graph. The repository has to discover selected roots, materialize their contributions, and reconcile their interactions through a custom orchestration loop.
This discussion proposes a low-level API for composing selected bootstrap resource declarations. It does not assume that the solution should be called components, packages, or bundles, and it does not require a new selector DSL. Whether the existing environment model should be extended or reused is one of the design questions.
The repository model
The repository contains capability-specific configuration roots. A root may provide mise tools and global task fragments, application configuration, shell fragments, templates, managed blocks, agent instructions and skills, repository seeds and overlays, and host-specific services.
The roots are not independent applications. They contribute to shared configuration surfaces. For example,
~/.config/mise/, the shared agent instruction tree, and~/.config/pitchfork/config.tomlreceive contributions from multiple roots. Some contributions are active only for a selected environment.The current implementation applies these roots in a custom sequence, then runs a reconciliation phase after all selected global configuration has materialized.
Selection and environment dimensions
The repository currently uses mise environments for selection:
headed;~/.config/dotty/envs;not-exe.xyz.This is an adopted approach, not an assumption that the current API is sufficient.
The practical problem is that the repository must maintain a template describing the dimensions it knows about. That template also manually emits negated labels. For example, a contribution that should apply outside
exe.xyzdepends onnot-exe.xyz.toml, while the bootstrap template must know thatexe.xyzis a dimension that can be negated.This creates two maintenance obligations:
The selection rules are distributed between mise's environment discovery, the repository's
.miserc.tomltemplate, environment-specific entrypoints, and manual environment labels.A future API does not necessarily need a new selector language. It could reuse the existing environment resolution model. But the current manually maintained dimension/negation template should not be treated as the final selection design.
Cross-root example:
fffcontributes to the shared harness configurationThe
fffconfiguration is not part of the Claude Code configuration itself.fffis a capability that provides:fff-mcpexecutable;Claude Code is only one consumer of this capability. Other agents or harness clients may consume the same shared configuration in the future. Putting the entire integration in the
clauderoot would make the ownership direction wrong: the consumer would own a capability-specific contribution.The desired composition is:
In the current repository, the
fffroot contributes to the shared~/.local/share/dotty/agents/AGENTS.mdtree and declares a reconciliation task. That task reaches Claude Code through an optional task dependency because Claude Code may not be selected.The important relationship is therefore not:
It is:
The same pattern applies to
herdr,mise-introspection,catchup, and other capabilities that add permissions, tools, instructions, or integrations to the shared harness configuration.Other concrete examples
The names and domains below are specific to this setup. They are examples of resource relationships, not proposed mise concepts:
cpacontributes client configuration, whilecpa-hostcontributes gateway/backend service configuration oncpamc.exe.xyz.cpacontributes a tunnel only outsideexe.xyzthroughmise.not-exe.xyz.toml.apphane-host,axisflow-host, andeasysellpublish environment-specific repository seeds and overlays consumed later by thereposcapability.t3codepatches its provider settings from available CPA sources during reconciliation.Current orchestration
The custom bootstrap task currently has to determine which environment-specific roots are active, apply each active root separately, merge shared
symlink-eachtrees, compose managed blocks, render templates with the correct source root, install tools contributed by selected roots, run integrations only when their consumers are selected, re-render shared configuration after new fragments appear, run a final reconciliation phase, and resolve selected resources such as encrypted SSH keys.The result is a second orchestration layer around mise. It repeats decisions that mise already makes for configuration selection, but it has no common resource graph, conflict model, or provenance output.
Existing mise behavior that seems reusable
The existing API already has useful semantics:
sourcepaths resolve relative to the config that declares them;config_root;[dotfiles]entries merge by target;(path, id);symlink-eachrecords managed links;[bootstrap.files]and[bootstrap.directories]already participate in plan/status;mise config lscan show loaded configuration files.The gap is that
monorepo.config_rootsis primarily used for task/tool discovery and workspace operations. It does not currently mean “collect bootstrap declarations from all these roots and apply them as one graph.”Proposed API direction
The first question is whether the existing environment selection model can be used directly, or whether mise needs a more general way to describe selectable configuration dimensions.
The bootstrap resource problem is separate from that question.
A possible low-level API would let mise collect declarations from selected configuration roots:
The exact key and relationship to
[monorepo].config_rootsare open questions. The intended behavior would be:config_root;This proposal does not require introducing a new
bundle,component, orpackageselector. It also does not claim that the current environment dimension template is ideal. Selection and resource composition should be designed as related but separable concerns.Possible conflict semantics
A composed resource graph would need deterministic conflict handling.
A possible MVP policy:
symlink-eachleaf are an error;--forcebehavior;Errors should identify both declarations, for example:
Provenance and status
For composed resources, status and plan output would be more useful if they included the contribution origin.
For example:
{ "target": "~/.local/share/dotty/agents/AGENTS.md/fff", "source": "bundles/fff/agents/instructions/fff.agents.md", "config": "bundles/fff/mise.toml", "config_root": "bundles/fff", "environment": [], "state": "applied" }This could also explain why a resource is active, why it is inactive, which config root owns a managed link, which declarations conflict, which source disappeared, and why a shared target is still considered managed.
The current
dotfiles statusoutput already reports target, source, mode, and state. The proposal would extend that with provenance rather than introducing an unrelated ownership system.Source and template semantics
I would expect the following behavior:
The source should resolve relative to
bundles/fff/mise.toml, and the template should see:This preserves the behavior of independently applied config roots and avoids making templates depend on the aggregate root.
What this would cover
This would cover composed
symlink-eachconfiguration trees, shared managed files, contributions from multiple selected roots, environment-specific contributions, source-relative templates, one plan/status/apply operation, deterministic conflict diagnostics, and provenance for each resource.It would also make it possible for repositories like this one to remove much of their custom bootstrap looping without requiring mise to understand a repository-specific “bundle” concept.
What it would not cover initially
I would not expect the MVP to aggregate arbitrary imperative tasks or hooks.
Tasks may install or call tools, access the network, mutate repositories, provision credentials, start services, or assume another task exists. Their lifecycle and dependency semantics deserve a separate discussion.
A first implementation could therefore support only declarative resources and leave custom reconciliation tasks explicit.
Alternative smaller step
A smaller first step could add provenance to existing bootstrap resources without adding multi-root collection:
That would improve conflict diagnostics and status output and could serve as a foundation for later resource collection. It would not solve the need for one aggregate plan/apply/status operation.
Open questions
[bootstrap.config_roots]setting, or should this extend the existing[monorepo].config_rootsmodel?symlink-eachstate be represented when multiple roots contribute to one target tree?Suggested MVP
The narrowest useful MVP appears to be a selected-root collection for declarative resources:
[dotfiles],[bootstrap.files], and[bootstrap.directories].It should provide:
config_root;The MVP should not aggregate arbitrary hooks or tasks. Those have side effects and need a separate lifecycle contract.
The selection problem should remain an explicit open question. The current environment setup is useful, but maintaining the dimension and negation template is a real part of the cost of this approach.
All reactions