On this page
Atomic single-owner accessors for options.json and state.json.
#claudewheel.appdata
#claudewheel.appdata
Atomic single-owner accessors for options.json and state.json.
#OptionsFile
Single-owner accessor for options.json (read-modify-write, atomic).
#load
def load(self, default: dict[str, Any]) -> dict[str, Any]Read options.json fresh from disk; return a copy of default if missing/corrupt.
The fallback is a DEEP COPY: callers pass module-level default dicts and the read-modify-write methods below mutate what they get back, so returning the default by identity would edit the caller's constant and leak it into every later read.
#add_pinned
def add_pinned(self, segment_key: str, value: str, default: dict[str, Any]) -> dict[str, Any]Append value to a segment's pinned list, write, and return the fresh dict.
Fresh read (falling back to default), ensures the segment dict and its pinned list exist, and appends only when value is not already pinned. The file is written only when a new value is actually appended.
#set_metadata
def set_metadata(self, segment_key: str, value: str, meta: dict[str, Any], default: dict[str, Any]) -> dict[str, Any]Set metadata for a segment value, write, and return the fresh dict.
#record_discovered
def record_discovered(self, segment_key: str, values: list[str], metadata: dict[str, dict[str, Any]], default: dict[str, Any]) -> dict[str, Any]Append discovered values and merge their metadata, atomically.
Append-only: a value already in the segment's list keeps its position and nothing is ever removed, so an option survives the day the source that discovered it stops listing it. Metadata is merged key by key, so an entry's other fields (a model_id, say) survive an update that only carries a release date. Fresh read, and the file is written only when something actually changed.
#rename_value
def rename_value(self, segment_key: str, old: str, new: str, default: dict[str, Any]) -> dict[str, Any]Swap old -> new in a segment's values, pinned, and metadata key.
Swaps old -> new in the values list and pinned list (in-place index swap, order preserved). The metadata key is moved verbatim (old -> new) with NO config_dir rewrite -- config_dir is never persisted going forward, but the legacy metadata dict may still exist on disk, so re-keying it avoids orphaning the entry. Fresh read, atomic write, returns the dict.
#remove_value
def remove_value(self, segment_key: str, name: str, default: dict[str, Any]) -> dict[str, Any]Remove name from a segment's values, pinned, and metadata.
Drops name from the values list, the pinned list, and the metadata dict. Fresh read, atomic write only when something was removed, returns the dict.
#write
def write(self, data: dict[str, Any]) -> NoneBare atomic write of the full options dict (used by config migrations).
#StateFile
Single-owner accessor for state.json (read-modify-write, atomic).
#load
def load(self, default: dict[str, Any]) -> dict[str, Any]Read state.json fresh from disk; return a copy of default if missing/corrupt.
The fallback is a DEEP COPY for the same reason as :meth:OptionsFile.load: the returned dict is mutated by callers, and a module-level default handed back by identity would be mutated with it.
#save
def save(self, state: dict[str, Any], out_of_band_keys: tuple[str, ...]=OUT_OF_BAND_STATE_KEYS) -> NoneWrite state to disk, letting fresh on-disk out-of-band keys win.
Re-reads the disk copy and, for each name in out_of_band_keys, copies a non-None disk value into state before writing. This prevents a wholesale save from clobbering values written straight to disk by out-of-band writers (e.g. the auth wizard's auth_browser) with stale in-memory state.
#get_value
def get_value(self, key: str, default: Any=None) -> AnyRead a single key fresh from disk; return default if unavailable.
#set_value
def set_value(self, key: str, value: Any) -> NoneRead-modify-write a single key, preserving all other keys on disk.