Skip to content

v0.26.0

Choose a tag to compare

@smm-h smm-h released this 14 Aug 04:25
· 818 commits to main since this release

Profile deletion is recoverable: profile delete hands the profile directory to saferm, which archives it before removing it, and prints the handle that restores it. claudewheel negotiates on saferm's declared features before deleting anything and offers to install saferm when there is none, so deletion now requires it. permission list answers a machine through the framework's --json envelope instead of its own --format json arm.

Context

Deleting a profile used to be irreversible: the directory went, and the stored
OAuth token inside it went with it. It now goes to saferm's archive first, and
the deletion prints the saferm undelete <uuid> that puts all of it back. That
makes saferm a precondition rather than an optimisation -- claudewheel asks
saferm capabilities what the installed binary ships and refuses the deletion
when a feature the delegation uses is missing, because there is deliberately no
flag that deletes without the archive. Deletion therefore requires saferm
0.9.0 or newer.

This release also closes claudewheel's window onto an unreleased framework: the
strictcli floor moves to the released 0.40.0, whose value-aware mutex election
refuses --no-all-profiles at parse time, and whose machine-mode envelope is
where permission list now puts its machine output.

Breaking

  • profile delete now requires saferm. claudewheel asks saferm capabilities what the installed binary ships and refuses the deletion when saferm is absent, too old to answer, or missing a feature the delegation uses -- deleting without it would be irreversible, and there is deliberately no flag that does so. At a terminal claudewheel offers to install it, verifying the download against the release's published SHA-256 manifest; without a terminal the refusal is a hard error naming the install.
  • permission list machine output moved to the framework's --json. --format now chooses between the two human renderings and its json value is refused. claudewheel permission list --json answers instead with the strictcli envelope, whose single payload carries every listed profile: the old arm printed one json.dumps per profile, so --all-profiles emitted concatenated objects no JSON reader accepts.

Features

  • Deleting a profile is now recoverable. profile delete hands the profile directory to saferm, which archives it before removing it, and prints the saferm undelete <uuid> that puts all of it back -- settings, credentials and the stored OAuth token included, with the token file restored at mode 0600. The shared session store behind a profile's symlinks is never copied out and never touched.

Fixes

  • A restored profile no longer stages itself in a git index. The saferm undelete command profile delete prints now carries --no-update-git-index, matching the archival. Following it for a profile inside a git worktree used to git add the restored directory -- its .credentials.json and stored OAuth token among it.
  • A deletion that archived the profile always reports its restore handle. When the archival succeeded but writing options.json or state.json failed, the profile was gone and the uuid that restores it was never printed. That failure now names the handle, the restore command, the write that failed, and the deletion to re-run to finish the cleanup.
  • A malformed saferm answer is no longer reported as a refusal. A success envelope claudewheel could not read -- an empty uuid, a non-numeric size, a record of the wrong shape -- used to abort with "Nothing was deleted" after the profile directory had been archived and removed. It is now a hard error that says the archival happened, carries whatever handle information survived, and points at saferm list.
  • --no-all-profiles is refused where it is written, not after the fact. claudewheel now requires strictcli 0.40.0, where a present-but-false negatable boolean no longer elects a mutex member. permission add, permission remove, permission list and purge-plugins refuse --no-all-profiles at parse time, naming what it did (--no-all-profiles declines an option; it does not choose one), instead of accepting the command and refusing it inside the handler.