Repository navigation
fix(cli): reject unknown args in logs tail / backup list|show; flag a corrupt preferences file - #27
Merged
Conversation
… corrupt preferences file Closes two "Known limits" in docs/PROJECT_STATUS.md that broke the CLI contract (JSON in, JSON out, stable error codes): - `logs tail` validates through _check_args: `--n N` (positive integer) and `--level info|warn|error|debug`; anything else is INVALID_USAGE. `backup list` takes nothing and `backup show` one id; every backup subcommand validates before the preferences object is built. - `status` reports `preferences_file` (corrupt, problem, warning) and `diagnose` reports the new error finding PREFS_FILE_CORRUPT when user_preferences.json is not valid JSON or not a JSON object. Both stay read-only: the file is detected, never repaired or backed up. - A JSON array/string preferences file no longer crashes status, diagnose and get with INTERNAL_ERROR; they report the defaults, as for an unparseable file. The hook runner still fails on such a file; recorded as a new known limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018HwdjLSbSkWMCv6DTyoecy
6 of 8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Closes two items under "Known limits that are accepted for now" in
docs/PROJECT_STATUS.md. Both broke the CLI contract (JSON in, JSON out, stable error codes).1.
logs tailandbackup list|showignored arguments they don't definelogs tailnow validates with_check_args(valued=("--n", "--level"), max_positionals=0)before it reads any state.--nmust be a positive integer.--levelmust be one ofinfo|warn|error|debug. Anything else returnsINVALID_USAGE … Nothing was changed.These all used to run silently: a bare--n,--n five,--level loud,--follow, or any positional.--n 0and negative values are now rejected too. They used to return the whole over-read window, becauseevents[-0:]returns every event.backup listtakes no arguments andbackup showtakes exactly one id. All fourbackupsubcommands now validate beforeprefs_factory()runs.restoreandprunekeep their old grammar; only the order changed.2. A corrupt
user_preferences.jsonlooked like the defaults_check_prefs_file()inbin/audio-hooks.py. It reads the file and flags it as corrupt when it is not valid JSON (this includes an empty file and invalid UTF-8) or when the JSON is not an object.statusgainspreferences_file: {path, exists, corrupt}. When the file is corrupt it also carriesproblemand awarningwith codePREFS_FILE_CORRUPT. Top-levelokstaystrue, the same wayinstall.warningworks.diagnosereportsPREFS_FILE_CORRUPTas an error (ok:false, exit 1), because every setting the user has is being ignored. It also addsprefs_fileto its output. The suggested command isaudio-hooks backup list.CLI_ERROR_CODESwithappears_in: "diagnose", likeDUAL_INSTALL_DETECTED, which also shows up in status. The test only allowserror/diagnose/error, diagnose.status,diagnoseandgetfail withINTERNAL_ERROR.load()returns such a value as-is and only falls back to{}when the file fails to parse._load_config_raw()now uses the defaults in that case, as it already did for an unparseable file.hooks/user_preferences.pyis unchanged.Docs
docs/PROJECT_STATUS.md: removed both items and added one new known limit (see below).docs/INVARIANTS.md: replaced the "Known limit" sentence in Commands that only report must stay read-only with the rule "detected, never repaired" plus thelogs tail/backupgrammar.CHANGELOG.md: new## [Unreleased]section.SKILL.mdand README don't describe this behaviour, so they are unchanged.bash scripts/build-plugin.shonly.Type of Change
logs tail/backup list|shownow getINVALID_USAGE, which is the intent.How Has This Been Tested?
Tests added to
tests/test_read_only_commands.py:REJECTED: 14 bad invocations oflogs tail/backup list|show. Each must returnINVALID_USAGEand leave home and data dirs byte-identical. This runs against a fresh home and against an older-version prefs file, so a rejected call doesn't migrate it.READ_ONLYgainslogs tail --level errorandlogs tail --level debug --n 1.TestReadOnlyCommandsCorruptPreferencescovers five corrupt files: truncated JSON, empty, array, string, non-UTF-8. For each:statusanddiagnoseboth flag it.READ_ONLY+REJECTEDinvocation leaves the file and dirs byte-identical (no.bak, no lock).TestReadOnlyCommandsRealProcessCorruptPreferencesruns the same check in real processes.python -m unittest discover testsbash scripts/build-plugin.sh --checkReal CLI, scratch
CLAUDE_PLUGIN_DATA(andHOME), holding{"audio_theme": "custom", "enabled_hooks": {"stop": fal:Afterwards the data dir held only
user_preferences.json: no.bak, lock,logs/orqueue/.<scratch>stands for the session scratchpad path.Sample rejections (real output):
Left undone / uncertain
hook_runner.is_hook_enabled(AttributeError, no sound), and the runner never replaces the file. This was reproduced by hand and predates this PR. Fixing it means changinghook_runner.pyor the runtimeload()path, which is outside this task's scope. It is now listed under Known limits inPROJECT_STATUS.md.diagnoseflags it, andaudio-hooks setrepairs it (verified).setnow repairs it to the defaults (the old bytes are snapshotted to the backups first). Before this PR it failed withINTERNAL_ERROR.PREFS_FILE_CORRUPTis reported as an error (diagnoseexits 1), not a warning. The reasoning is that the user's settings are silently not in effect.statusanddiagnosethemselves still accept extra arguments without complaint (both handlers take_argsand ignore it). That wasn't one of the two listed limits, so it is untouched. It is not recorded inPROJECT_STATUS.mdeither.claude/cli-known-limits-ee12t7rather thanclaude/cli-known-limits, because the session is restricted to that branch.Checklist
Generated by Claude Code