Config upgrade

View as markdown

You 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:

  1. Detect. Sniffers run over every Shelbi-owned config surface (project registration YAML, statuses.yaml, workflows, the global config.yaml, per-agent settings.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.
  2. Classify. Every finding is sorted into one of two buckets:
    • auto_heal: a deterministic, non-lossy, unambiguous rewrite exists. Examples: a renamed key (workers: becomes workspaces:), a tag shorthand (tag: x becomes tags: [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-project pr-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, a display_name: that disagrees with name:, a zen.danger_paths that sets both extend: and override:, 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.
  3. Apply. The pass applies every auto_heal write-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_judgment findings are never applied automatically.
  4. 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.json

It 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 upgrade subcommand and its flags.
  • The events log: where the config-upgrade disclosure line lands.
  • Project config: the surfaces the pass sniffs, including the pr-template.md it materializes for older projects.