Configuration layering and reloadability practice

Updated Jul 27, 2026

Roadmap item 3.1.2 defines configuration layering rules and hot-reload semantics. Follow these rules when adding or changing configuration settings:

Layering rules

Configuration merges in the following precedence order (highest to lowest):

  1. Runtime overrides (ephemeral, in-memory only, not persisted)
  2. User config (config.json5)
  3. Built-in defaults (bundled with the app)

Workspace-level overrides are deferred to a future milestone.

Merge semantics:

  • Objects: deep merge (nested properties recursively merged).
  • Arrays: replace (user array completely replaces default array).

Use resolveConfigLayers() from app/config/layering.ts to resolve the effective configuration:

import {resolveConfigLayers} from './config/layering';
import type {configOptions} from '@shared/types/config';

const defaults = {} as configOptions;
const userConfig = {} as Partial<configOptions>;
const runtimeOverrides = {} as Partial<configOptions>;

const effectiveConfig = resolveConfigLayers(defaults, userConfig, runtimeOverrides);

Reloadability classification

Every configuration key must have a reloadability classification:

  • live: Changes apply immediately without restart (theme, fonts, keybindings).
  • restart: Changes require application restart (shell settings, update channel).

Classify new settings in shared/src/constants/config-reloadability.ts:

// In profileConfigReloadability (or rootConfigReloadability for root keys)
newSetting: {
  classification: 'live', // or 'restart'
  rationale: 'Brief explanation of why this classification was chosen'
}

Classification guidelines:

Live-reloadable Restart-required
Theme/UI appearance (colours, padding) Backend transport (shell, shellArgs)
Font settings (family, size, weight) Update channel / auto-update settings
Cursor appearance (shape, blink, colour) Environment variables (env)
Keybindings WebGL renderer (deferred to CONFIG-001)
Custom CSS (css, termCSS) Process-level configuration

Detecting and handling config changes

Use createReloadHandler() from app/config/reload-handler.ts to process config reloads with automatic classification:

import {createReloadHandler} from './config/reload-handler';
import type {configOptions} from '@shared/types/config';

const currentConfig = {} as configOptions;
const newConfig = {} as configOptions;

const handler = createReloadHandler({
  getCurrentConfig: () => currentConfig,
  applyLiveConfig: (config) => { /* apply live changes */ },
  emitRestartWarning: (diagnostics) => { /* notify user */ }
});

const result = handler.processReload(newConfig);
// result.appliedLive: keys applied immediately
// result.restartRequired: diagnostics for keys requiring restart

Settings UI integration

When building settings UI components:

  1. Use useConfigReloadability({configKey}) to obtain requiresRestart and classification for a setting.
  2. Display restart-required indicators using RestartRequiredIndicator component, passing requiresRestart from the hook.
  3. Show inline warnings when users modify non-reloadable settings using InlineRestartWarning component, passing classification from the hook.
  4. Use keyRequiresRestart(key) for imperative checks outside React render.

Example:

import {RestartRequiredIndicator, InlineRestartWarning} from
  '../components/restart-required-indicator';
import {useConfigReloadability} from '../hooks/use-config-reloadability';

function ShellSetting() {
  const hasChanged = true; // example: derived from form state
  const {requiresRestart, classification} = useConfigReloadability({configKey: 'shell'});

  return (
    <div>
      <label>
        Shell Path
        <RestartRequiredIndicator
          requiresRestart={requiresRestart}
          tooltip="Changing the shell requires a restart to take effect."
          ariaLabel="Requires restart"
        />
      </label>
      <input type="text" />
      <InlineRestartWarning
        classification={classification}
        show={hasChanged}
        message="This change will take effect after restarting the application."
      />
    </div>
  );
}

Testing requirements

Add unit tests when adding new configuration settings:

  • Verify reloadability classification via test/unit/config-reloadability.test.ts.
  • Validate merge semantics in test/unit/config-layering.test.ts.
  • Test hot-reload detection in test/unit/config-hot-reload.test.ts.

Deferred features

The following features are explicitly deferred:

  • WebGL renderer hot-reload: Tracked under CONFIG-001 in docs/tracking-issues.md.
  • Workspace-level overrides: Will be implemented in a future milestone.