Skip to content

feat: load configurations written for a newer StableBar - #337

Merged
M4n0x merged 1 commit into
mainfrom
feat/newer-configurations
Oct 26, 2026
Merged

M4n0x merged 1 commit into
mainfrom
feat/newer-configurations

Conversation

@M4n0x

@M4n0x M4n0x commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator

Problem

Unknown properties are errors, so a file saved by a newer build fails to load in an older one, and the error says only Unknown property.: beta users who go back to Stable, dotfiles shared across Macs, shared includes, and rollbacks. The real case: bar.motion and bar.popupTransition broke v0.5.0.

Part of #223. This covers the issue's first part, the version marker. Running the migrations on every file the loader reads is the second part and gets its own pull request, so #223 stays open.

Changes

  • The marker is $schema. When Settings, setup or a command-line edit changes a configuration file, it pins $schema to the schema at the tag of the version that saved it, such as …/StableBar/v0.10.0/stablebar.schema.json, and adds $schema to a file without one, as its first key. The pin only moves forward: an older version leaves a newer pin alone, and a $schema pointing elsewhere (a local copy, a fork) stays as written. Every release since v0.2.0 accepts any $schema value (v0.1.0 accepted any key), so pinning never stops an older version from reading the file. A separate key such as savedBy would have broken every file saved by a fixed build in all older releases. Editors also validate against the saving version's schema. Development builds and tests never pin.
  • Older versions skip what they don't know. When a file the configuration reads (main, include or screen-setup bar) is pinned to a newer version than the one running, or an installed package's minimumVersion is newer, unknown properties are skipped in the whole configuration instead of rejected. rejectUnknownKeys consults a task-local collector, so nested decodes (widgets, templates, overrides, workspace defaults, profiles) are covered, and each skipped property keeps its full path. Skipped properties stay in the composed document and in the file, so edits made in the older version keep them.
  • A profile condition the version can't check never matches instead of being dropped, which would make the profile match everywhere. A pinned profile still applies.
  • Other failures say why. If a configuration written for a newer version still fails to load (a new widget type, a new enum value), the error ends with "This configuration was written for StableBar 0.10.0, and this is 0.9.0. Update StableBar to load it."
  • Typos stay errors. Without a newer pin, unknown properties are errors as before. Package manifests stay strict even in a newer configuration: they hold what installing reviewed, such as permissions, and skipping a key could drop a restriction.
  • Where the warning shows: the menu-bar icon's menu and the bar's context menu (after any error), an orange banner in Settings on the sections that show configuration errors, Status, --validate (on standard error), --query (warning), and the facts StableBar triage gathers. No warning triangle on the bar: the bar works, and someone who chose an older build on purpose would see it permanently.
  • Versions: compareReleases orders versions as SemVer does: a beta comes before its release. Stable always ships the newest beta'd commit, so a file saved by 0.9.0-beta.… reads strictly in 0.9.0.
  • Docs: a new Files from a newer StableBar section; --validate, the minimumVersion row in packages.md and the $schema description in the schema point to it.

Surfaces from AGENTS.md:

  • Decoding: no new property; $schema was already allowed and listed, so schemaListsTheSamePropertiesAsTheDecoders is unchanged.
  • Layers: includes, screen-setup bars and package files carry the version; templates, widget overrides and profiles are skipped with full paths.
  • Editing: ConfigurationEdits.swift's configurationData(_:editing:) pins the schema, which covers Settings (Appearance, Your bar, Profiles), command-line edits, setup, other screen setups' bars and the launch migrations.
  • Docs: configuration.md, command-line.md, packages.md and the schema. No example needed a change.

Not covered:

  • Releases up to 0.8.0, and betas built before this lands, still stop at the first unknown property. Only releases from this one on read newer files leniently.
  • Profile editing in Settings decodes profiles strictly in three places, for the × buttons and the theme-override hints. With a newer profile key, those hints are missing; the profile still loads and edits.
  • A package selection or theme selection is decoded before the package files are read. When only a package (not a configuration file) is newer, an unknown key in its selection is still an error.

Testing

  • swift test: 471 Swift Testing tests and 294 XCTest tests pass.
  • New NewerConfigurationTests (9 tests): version ordering; skipping in the main file, templates, widget styles and profile conditions, with the warning text; strict loading for files pinned to the running version, a beta of it, main, a local schema, and in development builds; an include and a package that are newer; the error note; pinning on save (added first, raised, kept when newer or custom, untouched when nothing changed); BarStore loading, warning, --query and keeping skipped properties and the pin on save; Settings and command-line saves pinning to the bar's version.
  • Mutation check: with the skip removed from rejectUnknownKeys, the two skipping tests fail; with the profile-condition guard removed, the profile expectations fail.
  • --validate against real app bundles and scratch files, with no bar started: one release-stamped build (0.8.0, from the tracked plist) and one development build.
    • Pinned to v0.10.0, with bar.edgeGlow and widgets.clock.style.glow: the release build prints Warning: This configuration was written for StableBar 0.10.0, so 0.8.0 skips bar.edgeGlow, widgets.clock.style.glow. Update StableBar to use them. and Valid configuration: 1 widgets in 3 files., and exits 0. The development build reads strictly: bar.edgeGlow: Unknown property., exit 1.
    • main schema with the typo bar.heigth: both builds print bar.heigth: Unknown property. and exit 1.
    • Pinned to v0.10.0, with widget type hologram: the release build prints widgets.w: type: Unknown widget type. This configuration was written for StableBar 0.10.0, and this is 0.8.0. Update StableBar to load it. The development build prints the error without the note.
  • Not checked: the app on screen, as I didn't launch a second bar. The status menu and the bar's context menu show the same string as the Settings banner. A real save from a release build is covered only by tests that set the bar's version.

Screenshots: drawn offscreen by a temporary test (not committed) from the same file: $schema pinned to v0.10.0, with made-up properties bar.edgeGlow and widgets.clock.style.glow. Before is main; after runs as 0.9.0.

Before (main) After (running as 0.9.0)
General, dark
Status, light
General, light
Status, dark

Merging

Squash or rebase: one commit.

Claude Opus 5.5 via Claude Code

Saving from Settings, setup or the command line now pins $schema to the
tag of the version that saved the file. Every release accepts any
$schema, so the pin never stops an older one from reading the file.

When a file the configuration reads is pinned to a newer version, or an
installed package's minimumVersion is newer, StableBar skips the
properties it doesn't know and lists them in a warning: in the menus,
Settings, --validate and --query. A profile condition it can't check
never matches. Other load errors name the version the file was written
for. Unknown properties stay errors otherwise, and package manifests
stay strict.

Part of #223.
@M4n0x
M4n0x requested a review from Seb-GRAF as a code owner October 26, 2026 17:20
@M4n0x
M4n0x merged commit 068b6fc into main Oct 26, 2026
7 checks passed
@M4n0x
M4n0x deleted the feat/newer-configurations branch October 26, 2026 17:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant