Summary
Re-onboarding a project to fullsend (v0.37) removes existing custom agent configuration. The onboarding scaffold overwrites .fullsend/config.yaml without preserving user customizations.
Evidence
Root Cause
The scaffold install path (BuildScaffoldFiles → NewPerRepoConfig) bakes all defaults into config.yaml. This means:
- User customizations and scaffold-generated defaults are mixed in the same file
- Re-onboarding must choose between overwriting (losing customizations) or preserving (missing new defaults)
- Neither approach handles agents added or removed in a new fullsend version
Proposed Solution: Leverage the Existing Three-Tier Layered Config (ADR 0069)
Fullsend already has a three-tier configuration hierarchy designed for exactly this scenario:
config.yaml (overlay, user-owned) → config.base.yaml (base, scaffold-managed) → code defaults
From the layered-config-reference:
"Upgrading a base layer (e.g., refreshing config.base.yaml) does not require editing config.yaml — the overlay inherits new defaults automatically."
The existing merge semantics already support this:
- Agents use keyed merge by
DerivedName() — overlay entries merge with parent entries
- Marshal only emits locally-set values (no leaking of inherited defaults)
loadExistingPerRepoConfig already reads both config.yaml and config.base.yaml
Required Changes
On initial install (admin install / github setup):
- Generate
config.base.yaml with scaffold defaults (roles, agents, runtime defaults)
- Generate
config.yaml as a minimal overlay — only version: "1" or truly user-specific overrides
- Users customize agents/settings by editing
config.yaml (overlay)
On re-onboarding (re-running install/setup on an existing repo):
- Regenerate
config.base.yaml with the new version's defaults
- Never touch
config.yaml — it is user-owned
- The layered resolution handles the rest automatically
How This Solves Each Scenario
| Scenario |
Behavior |
| New agent added in fullsend upgrade |
Appears in regenerated config.base.yaml, automatically visible via fallthrough |
| Agent removed in fullsend upgrade |
Removed from config.base.yaml, no longer in effective config (unless user explicitly added it in overlay) |
| User customizes an agent (model, effort, runtime) |
Override lives in config.yaml overlay, untouched during upgrade |
| User adds a custom agent |
Defined in config.yaml overlay, persists across upgrades |
| User removes a default agent |
Can override with enabled: false in overlay (if supported) or just not reference it in roles |
Migration Path
For repos already onboarded with defaults baked into config.yaml:
- On next re-onboarding, generate
config.base.yaml from the new version's defaults
- Diff the existing
config.yaml against config.base.yaml to produce a minimal overlay containing only user customizations
- Replace
config.yaml with the minimal overlay
- Present this as a clear PR diff showing the split
Environment
Summary
Re-onboarding a project to fullsend (v0.37) removes existing custom agent configuration. The onboarding scaffold overwrites
.fullsend/config.yamlwithout preserving user customizations.Evidence
roming22-org/fullsend-pythonusing fullsend v0.37config.yaml, but this approach is too simplistic — it doesn't handle agent additions/removals between fullsend versions.Root Cause
The scaffold install path (
BuildScaffoldFiles→NewPerRepoConfig) bakes all defaults intoconfig.yaml. This means:Proposed Solution: Leverage the Existing Three-Tier Layered Config (ADR 0069)
Fullsend already has a three-tier configuration hierarchy designed for exactly this scenario:
From the layered-config-reference:
The existing merge semantics already support this:
DerivedName()— overlay entries merge with parent entriesloadExistingPerRepoConfigalready reads bothconfig.yamlandconfig.base.yamlRequired Changes
On initial install (
admin install/github setup):config.base.yamlwith scaffold defaults (roles, agents, runtime defaults)config.yamlas a minimal overlay — onlyversion: "1"or truly user-specific overridesconfig.yaml(overlay)On re-onboarding (re-running install/setup on an existing repo):
config.base.yamlwith the new version's defaultsconfig.yaml— it is user-ownedHow This Solves Each Scenario
config.base.yaml, automatically visible via fallthroughconfig.base.yaml, no longer in effective config (unless user explicitly added it in overlay)config.yamloverlay, untouched during upgradeconfig.yamloverlay, persists across upgradesenabled: falsein overlay (if supported) or just not reference it in rolesMigration Path
For repos already onboarded with defaults baked into
config.yaml:config.base.yamlfrom the new version's defaultsconfig.yamlagainstconfig.base.yamlto produce a minimal overlay containing only user customizationsconfig.yamlwith the minimal overlayEnvironment
roming22-org/fullsend-python