Config upgrade
View as markdownYou may be sitting on a Shelbi config authored by any past release, and no Shelbi config file carries a schema version. So instead of migrating by version number, Shelbi runs a content-based validate-and-upgrade pass that inspects each live config file for deprecated or legacy forms and either fixes them or flags them for a human decision.
When it runs
The pass runs automatically:
- on hub start (across every registered project), and
- on
shelbi reload(for the reloaded project plus the shared global surfaces).
It is read-only about your intent and best-effort about failures: a detection or write error on one project is logged and swallowed so it can never stop the hub from serving or a reload from completing.
Detect, classify, apply
Each run is detect, classify, apply, re-detect, emit:
- Detect. Sniffers run over every Shelbi-owned config surface (project
registration YAML,
statuses.yaml, workflows, the globalconfig.yaml, per-agentsettings.json, and the Zen and instructions Markdown), looking for known legacy forms. Detection never uses a version number, only the file's content, so it works for a config written by any past release. - Classify. Every finding is sorted into one of two buckets:
auto_heal: a deterministic, non-lossy, unambiguous rewrite exists. Examples: a renamed key (workers:becomesworkspaces:), a tag shorthand (tag: xbecomestags: [x]), a moved keybinding, a legacy status-id spelling, or a lifecycle-owned default a project is missing because it predates the file (today the per-projectpr-template.md).needs_judgment: ambiguous, potentially lossy, or the correct target is not mechanically determinable. Examples: an unknown top-level key that could be a typo or real data, adisplay_name:that disagrees withname:, azen.danger_pathsthat sets bothextend:andoverride:, or a prose refresh in instructions a user may have customized. When a sniffer is unsure, a finding lands here rather than risk a lossy auto-heal.
- Apply. The pass applies every
auto_healwrite-back itself, then re-detects the residual so the emitted report reflects the post-heal state. A form healed this run is not re-surfaced.needs_judgmentfindings are never applied automatically. - Emit. The residual is disclosed (see below).
What you see
On events.log. Each affected project gets one line:
project=<id> config-upgrade auto_heal=<M> needs_judgment=<N>auto_heal=<M> is informational: those M fixes are already applied. Only
needs_judgment=<N> with N > 0 needs attention.
The findings file. The residual is written for the orchestrator to ingest at boot, as a single hub-global JSON file:
~/.shelbi/config-upgrade-findings.jsonIt sits next to events.log. The pass removes the file when a config set is
fully clean, so a stale file never lingers after the last deprecation is
resolved. Each finding in it carries a stable id, its code (the deprecation
kind, e.g. ZEN_DANGER_PATHS_CONFLICT), the scope, file, and location, a
one-line message describing the legacy form, a proposed_fix, and for
needs_judgment findings a rationale explaining what cannot be inferred
mechanically.
Needs-judgment findings and the orchestrator
The needs_judgment findings are the channel the
orchestrator surfaces to you. When it sees a
needs_judgment > 0 line, it reads the live channel (shelbi config upgrade --needs-judgment) and folds the findings into its next reply: the surface, the
legacy form, the proposed fix, and the rationale, then asks how you want to
proceed. It never applies a needs-judgment finding you
have not approved. Once you approve one, it applies exactly that finding by its
id, reusing the same atomic write-back the auto-heal pass uses.
Inspecting it yourself
The same detector is exposed through
shelbi config upgrade: run it read-only to preview
what the boot pass would act on, --needs-judgment to read just the human-
decision channel, --apply to perform the auto-heals on demand, or
--apply-finding <id> to resolve a single approved finding.
Both the bare report and --needs-judgment detect live against the files on
disk, so they always agree for the same on-disk state. Neither replays the
config-upgrade-findings.json snapshot the boot pass wrote: a fix you apply by
hand (or with --apply-finding) drops off the --needs-judgment list on the
next run with no hub restart, and reintroducing a legacy form brings its finding
back.
See also
- shelbi config: the
upgradesubcommand and its flags. - The events log: where the
config-upgradedisclosure line lands. - Project config: the surfaces the pass sniffs,
including the
pr-template.mdit materializes for older projects.