fix: what an audit of the documented behaviour found

An independent check of every behaviour AGENTS.md was to describe:
- `--units ,` and `--only ,` skipped their empty items and so had no
  filter left: a typo started a run of every unit. They are usage errors
  now (exit 3).
- Two fixes the plan lists were missing: a dry run now clears the prompts
  a dry run before it left in the dir they share (the report counted
  them), and `config` reports an absolute functional.mcp.command that is
  no program here - /usr/bin/npx inside the Flatpak, whose /usr is the
  Sdk's.
- A patch that is not UTF-8 stops `run` at preflight (exit 3), as the
  plan has it; it exited 1.
- An exception no command caught aborted the process (134), e.g. a home
  that holds no lenses: it ends with its message and exit 1. An error
  escaping `run` or `freeze` now also clears the signal handler's run and
  sweeps what is left below the subreaper.
- `config -h` printed "unrecognized arguments" (exit 3): it prints the
  usage, exit 0.
- An API seat's .claude.json is linked into place, so a second writer
  never replaces the first's file and what claude wrote to it since.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jorijn van der Graaf 2026-10-31 16:45:53 +01:00
commit 32a0de1a07
Signed by: jorijnvdgraaf
GPG key ID: 2937E59CDCC1BCFB
7 changed files with 114 additions and 11 deletions

View file

@ -441,8 +441,10 @@ namespace Crafter::Review::Seats {
const std::size_t length = Dyn::Length(key);
const std::string tail = Dyn::Slice(key, static_cast<std::int64_t>(length) - std::min<std::int64_t>(20, static_cast<std::int64_t>(length)));
const Json approval = {{"hasCompletedOnboarding", true}, {"customApiKeyResponses", {{"approved", {tail}}, {"rejected", Json::array()}}}};
// O_EXCL: two passes of a fresh seat write it in the same instant; the
// contents are the same, so the one that loses leaves the other's.
// Two passes of a fresh seat write it in the same instant. Written
// whole under another name, then linked into place, which fails when
// the file is there: the one that loses leaves the other's, and with
// it whatever claude has written to it since.
const fs::path temp = std::format("{}.{}.{}.tmp", path.string(), Platform::PublishedPid(), TokenHex(4));
{
std::ofstream out(temp, std::ios::binary);
@ -450,7 +452,7 @@ namespace Crafter::Review::Seats {
}
fs::permissions(temp, fs::perms::owner_read | fs::perms::owner_write, fs::perm_options::replace);
std::error_code error;
if (!fs::exists(path)) fs::rename(temp, path, error);
link(temp.c_str(), path.c_str());
fs::remove(temp, error);
return path;
}