How Pinocchio builds hidden base settings and merges engine profiles without contaminating the underlying baseline.
This page explains the lifecycle that sits between Geppetto's profile model and Pinocchio's runtime behavior.
The core idea is simple, but the implementation has two different "base settings" paths that are easy to confuse:
profilesYou need this page when you are trying to understand:
BaseInferenceSettings really meansFinalInferenceSettings is separateai-client.* belong in the baseline rather than in profiles.pinocchio.yml participate in bootstrapPinocchio treats profile resolution as:
baseline + profile overlay = active runtime settings
The baseline is app-owned.
The profile overlay is Geppetto-owned.
The active runtime settings are what the current engine is actually built from.
Pinocchio keeps baseline reconstruction separate from profile overlay merging so selected profiles do not become the new baseline for future command resolution.
This is the internal baseline reconstructed through profilebootstrap.ResolveBaseInferenceSettings(...).
It comes from:
Pinocchio now supports a layered config plan instead of only a single implicitly discovered config file.
The standard low-to-high precedence order is:
system -> user -> repo -> cwd -> explicit
In concrete terms, Pinocchio can load from:
/etc/pinocchio/config.yaml$HOME/.pinocchio/config.yaml${XDG_CONFIG_HOME}/pinocchio/config.yaml.pinocchio.yml at the git repository root.pinocchio.override.yml at the git repository root.pinocchio.yml in the current working directory.pinocchio.override.yml in the current working directory--config-file <path>Later layers win.
That means:
.pinocchio.override.yml can override committed repo .pinocchio.yml.pinocchio.override.yml can override committed cwd .pinocchio.yml--config-file can override everything elseThis layered path is implemented through Glazed config-plan primitives and consumed by Geppetto bootstrap.
See:
pinocchio/pkg/cmds/profilebootstrap/engine_settings.gogeppetto/pkg/cli/bootstrap/engine_settings.goThis path is especially important for commands such as web-chat, which intentionally expose a narrower visible CLI but still need a full AI baseline.
This is the baseline Pinocchio can recover from already parsed command values by removing parse steps whose source is profiles.
It comes from:
See:
pinocchio/pkg/cmds/profile_base_settings.goThis path is especially important for commands that already parsed their real flag surface and still need a clean profile-free baseline.
This is the result of merging the selected engine-profile overlay onto one of those bases:
final = merge(base, resolved_profile.inference_settings)
This is the object used to build engines.
See:
geppetto/pkg/engineprofiles/inference_settings_merge.gopinocchio/pkg/cmds/cmd.goGeppetto-owned profile overlay
- resolved engine-profile inference settings
- model/provider defaults
Pinocchio-owned baseline
- config, env, defaults
- command-level non-profile flags
- shared transport settings such as ai-client.*
Runtime-owned active state
- merged final InferenceSettings
- selected profile metadata
- live session builder / engine
The ownership rule behind this diagram is the main architectural takeaway:
The standard command path in pinocchio/pkg/cmds/cmd.go uses both a directly decoded settings object and a profile-safe baseline.
Sequence sketch:
Glazed middleware parses values
-> stepSettings from parsed values
-> baseSettingsFromParsedValuesWithBase(...)
-> profilebootstrap.ResolveCLIEngineSettingsFromBase(...)
-> BaseInferenceSettings
-> FinalInferenceSettings
-> engine factory
The important subtlety is that stepSettings can already reflect profile effects. That makes it useful for ordinary command execution but unsafe as a clean baseline unless profile-derived values are removed first.
profiles Parse Steps MattersPinocchio stores parse provenance in values.Values. Each field keeps a log of where its value came from.
baseSettingsFromParsedValuesWithBase(...) walks those logs and keeps the last non-profile parse step for each field. That means:
Conceptually:
parsed values
- remove source == "profiles"
= profile-free parsed baseline
That is the key trick that lets Pinocchio recover the original launch-time settings instead of treating the selected profile as part of the baseline.
The parsed field history is now also the main debugging surface for layered config resolution.
Config-derived parse steps carry metadata such as:
config_fileconfig_indexconfig_layerconfig_source_nameconfig_source_kindThat means you can inspect parsed fields or inference debug output and answer questions like:
--config-file win last?A simplified example looks like this:
profile.active:
value: explicit-profile
log:
- source: config
value: repo-profile
metadata:
config_layer: repo
config_source_name: git-root-local-profile
- source: config
value: cwd-profile
metadata:
config_layer: cwd
config_source_name: cwd-local-profile
- source: config
value: explicit-profile
metadata:
config_layer: explicit
config_source_name: explicit-config-file
This provenance is especially useful when reviewing bug reports or unexpected profile selection in nested repositories.
ai-clientai-client settings are cross-profile transport settings.
That means they are baseline settings, not profile settings.
Examples:
If you put these settings in engine profiles, you are mixing operator/app infrastructure into profile overlays. If you keep them in the baseline, they naturally survive profile changes and stay consistent across runtime switches.
web-chat Specific Caveatweb-chat intentionally does not mount the full Geppetto sections on its public CLI. Its command surface currently exposes profile-selection controls plus redis, rather than the entire shared runtime flag surface.
See:
pinocchio/cmd/web-chat/main.goIt still builds a hidden base through ResolveBaseInferenceSettings(...), so shared baseline fields can still reach it through config and environment.
But there is an important consequence:
ai-client section to the web-chat command description would not be enough by itself to make new CLI flags effective if the runtime continues to rely only on the hidden-base path that rebuilds from env/config/defaults.If web-chat ever wants explicit cross-profile ai-client CLI flags, it will need both:
ai-client section on the commandOne subtlety that often confuses contributors is that Pinocchio's command repositories are not part of the shared Geppetto bootstrap sections.
The top-level config key:
repositoriesis intentionally excluded by pinocchio/pkg/cmds/profilebootstrap/configFileMapper(...) before the shared bootstrap middleware sees the config file.
That means there are two startup flows running side by side:
shared bootstrap path
config/env/defaults -> section values -> profile selection -> registry chain -> inference settings
root CLI repository path
resolved config files -> top-level repositories[] -> prompt directories -> command discovery
The repository path currently lives in:
pinocchio/cmd/pinocchio/main.goMore specifically, loadRepositoriesFromConfig() does the following:
profilebootstrap.ResolveCLIConfigFiles(nil) so repository discovery uses the same config-file plan as the shared bootstrap pathrepositories list from each file$HOME/.pinocchio/promptsThis is important for architecture discussions because it explains why repositories should not be treated like a normal shared section:
So the clean split is:
Use pinocchio profiles list to inspect the profile registry chain with explicit table headers:
pinocchio profiles list \
--profile-registries ./profiles.yaml
The default table includes columns for selection/default status, registry slug, profile slug, effective engine/API values, reasoning effort, and description. The registry column is important: if it says default, that means the profile lives in the default registry; it is not itself the default-profile marker.
Use --verbosity detailed to see raw profile overrides and effective inherited values:
pinocchio profiles list \
--profile-registries ./profiles.yaml \
--profile child \
--verbosity detailed
Detailed output includes fields such as:
override_pathsoverride_chat_engineoverride_inference_reasoning_efforteffective_chat_engineeffective_chat_api_typeeffective_reasoning_effortUse pinocchio profiles show when you want one profile's raw overrides and resolved effective settings without repeating full details for every profile row:
pinocchio profiles show child \
--profile-registries ./profiles.yaml \
--verbosity full \
--output json
The profile argument can be profile or registry/profile. If omitted, profiles show uses the selected/default profile.
Use normal Glazed output flags for machine-readable output:
pinocchio profiles list \
--profile-registries ./profiles.yaml \
--verbosity full \
--output json
Pinocchio routes these commands through its application-specific profile bootstrap path, so local .pinocchio.yml inline profiles are included alongside imported Geppetto registries.
| Problem | Cause | Solution |
|---|---|---|
| Switching profiles leaks values from the previous profile | The runtime reused active settings instead of a preserved baseline | Re-merge from the preserved base every time |
| A shared setting disappears after profile changes | The setting was treated like profile data instead of baseline data | Move it into the shared baseline section and preserve it in base reconstruction |
web-chat sees config/env settings but not equivalent CLI settings | Hidden base reconstruction currently rebuilds from env/config/defaults, not full parsed CLI values | Add a parsed-values-aware base path if widening web-chat CLI surface |
| A contributor puts transport config into engine profiles | Ownership boundary between baseline and overlay is unclear | Treat ai-client.* and similar operator settings as baseline-only |
| Repository changes in one config file do not behave like profile overrides | repositories is loaded as Pinocchio-local top-level app metadata across all resolved config files, not as a shared section merge | Inspect cmd/pinocchio/main.go and the resolved config-file stack, not just profile bootstrap |
geppetto/pkg/doc/topics/01-profiles.mdgeppetto/pkg/doc/tutorials/09-migrating-cli-commands-to-glazed-bootstrap-profile-resolution.md