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:
parent
53b7d6de31
commit
32a0de1a07
7 changed files with 114 additions and 11 deletions
|
|
@ -264,6 +264,16 @@ echo "{\"type\":\"result\",\"subtype\":\"success\",\"is_error\":false,\"result\"
|
|||
dry.returnCode == 2 && all > gate1 && gate1 > 1 && !securityUnit.empty() && !proverUnit.empty() && calls() == 0 && Count(planned, "lens", "repo-functional") == 0,
|
||||
dry.out.substr(dry.out.size() > 2000 ? dry.out.size() - 2000 : 0) + dry.err);
|
||||
|
||||
// Dry runs with no full run between them share one dir: the next
|
||||
// drops the last one's prompts.
|
||||
const fs::path dryDir = scratchRoot / std::format("{}-run-1-dry", h8);
|
||||
Write(dryDir / "prompts" / "stale.txt", "a prompt of another range");
|
||||
const Process::Captured again = run({"--dry-run"});
|
||||
std::int64_t prompts = 0;
|
||||
for (const fs::directory_entry& entry : fs::directory_iterator(dryDir / "prompts")) prompts += entry.path().extension() == ".txt" ? 1 : 0;
|
||||
Check("dry run: a second dry run on a hash counts its own prompts, the one before's gone", again.returnCode == 2 && !fs::exists(dryDir / "prompts" / "stale.txt") && prompts == all,
|
||||
std::to_string(prompts));
|
||||
|
||||
// Killed in its last gate: one security pass never answers, and the
|
||||
// driver is SIGKILLed once every other pass has.
|
||||
const fs::path scratchA = scratchRoot / std::format("{}-run-1", h8);
|
||||
|
|
@ -364,6 +374,33 @@ echo "{\"type\":\"result\",\"subtype\":\"success\",\"is_error\":false,\"result\"
|
|||
Check("preflight: an env file that is not UTF-8 stops the run, exit 3, with the decoder's own words",
|
||||
badEnv.returnCode == 3 && badEnv.err.contains("preflight: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte"), badEnv.err);
|
||||
|
||||
// A filter that names nothing is refused: skipping its empty items
|
||||
// must not leave no filter, which is a full run.
|
||||
const Process::Captured noUnits = run({"--units", ","});
|
||||
const Process::Captured noLenses = run({"--only", " , "});
|
||||
Check("--units and --only that name nothing are usage errors (exit 3) - never a full run",
|
||||
noUnits.returnCode == 3 && noUnits.err.contains("argument --units: names no unit id (',')") && noLenses.returnCode == 3
|
||||
&& noLenses.err.contains("argument --only: names no lens file (' , ')"),
|
||||
noUnits.err + noLenses.err);
|
||||
|
||||
// A patch that is not UTF-8 is no artifact: a preflight stop.
|
||||
const fs::path badArtifact = repo / ".review-gates" / "push" / "bad";
|
||||
fs::copy(artifact, badArtifact, fs::copy_options::recursive);
|
||||
for (const fs::directory_entry& entry : fs::directory_iterator(badArtifact)) {
|
||||
if (entry.path().extension() == ".patch") std::ofstream(entry.path(), std::ios::binary | std::ios::app) << std::string("\xff\n", 2);
|
||||
}
|
||||
const Process::Captured badPatch = Process::Run({Platform::SelfExe().string(), "run", "--artifact", ".review-gates/push/bad", "--base", baseSha, "--trees", ".", "--dispositions",
|
||||
".review-gates/push/bad/gate-state/dispositions.json", "--claude-bin", fake.string(), "--scratch", scratchRoot.string(), "--dry-run"},
|
||||
repo, std::chrono::seconds(60), {}, Env);
|
||||
Check("preflight: a patch that is not UTF-8 stops the run, exit 3, saying so",
|
||||
badPatch.returnCode == 3 && badPatch.err.contains("the artifact's patches are not UTF-8 ('utf-8' codec can't decode byte 0xff"), badPatch.err);
|
||||
|
||||
// An error that escapes a run ends it with its message, exit 1,
|
||||
// through the run's cleanup.
|
||||
const Process::Captured badReserve = run({"--no-reuse", "--no-salvage"}, {{"REVIEW_GATES_MEM_RESERVE_MB", "x"}});
|
||||
Check("an error escaping a full run (a reserve that is no number) ends it with its ValueError, exit 1",
|
||||
badReserve.returnCode == 1 && badReserve.err.contains("crafter-review: ValueError: could not convert string to float: 'x'"), badReserve.err);
|
||||
|
||||
// A stop between two gates: the next gate's pool starts aborted and
|
||||
// runs nothing of it.
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue