feat(run): the repository under review is the freeze record's, not the working directory's

A directory can review repositories other than itself: a workbench whose
config, env file and records serve several, freezing each with --repo and
--out. The run took the working directory for the repository under review:
the prompt named it, the calibration was kept under it, and --trees fell
back to it. The run now reads the repository from the artifact's
freeze.json - the prompt names it and the calibration is kept under it -
and --trees defaults to what the record says: the worktree, else the
repository when it is not the working directory, else `.`, the same rule
that builds the printed run line. The working directory keeps its roles:
the config, the env file, the slots, the lens processes' cwd and every
logged path. A damaged record stops the run at preflight.
This commit is contained in:
Jorijn van der Graaf 2026-10-31 21:31:48 +01:00
commit 4fc15fe1c2
Signed by: jorijnvdgraaf
GPG key ID: 2937E59CDCC1BCFB
9 changed files with 133 additions and 14 deletions

View file

@ -423,6 +423,30 @@ namespace Crafter::Review::Freeze {
Dyn::Str(Dyn::At(report, "base")), trees, Dyn::JoinPath(Dyn::JoinPath(outRel, "gate-state"), "dispositions.json"), Dyn::Str(Dyn::At(report, "date")));
}
std::string TreeOf(const Json& record, const fs::path& cwd) {
const Json& worktree = Dyn::Get(record, "worktree");
if (Dyn::Truthy(worktree)) return Dyn::Str(worktree);
const Json& repo = Dyn::Get(record, "repo");
if (!Dyn::Truthy(repo)) return ".";
std::error_code error;
return fs::weakly_canonical(fs::path(Dyn::Str(repo)), error) == fs::weakly_canonical(cwd, error) ? std::string(".") : Dyn::Str(repo);
}
Reviewed ReviewedBy(const fs::path& artifact, const fs::path& cwd) {
const fs::path file = artifact / "freeze.json";
std::error_code error;
if (!fs::exists(file, error)) return {cwd, "."};
Json record;
try {
record = Dyn::LoadJsonFile(file);
} catch (const std::exception& e) {
throw std::runtime_error(std::format("{}: not a freeze record ({})", file.string(), e.what()));
}
if (!record.is_object()) throw std::runtime_error(std::format("{}: not a freeze record", file.string()));
const Json& repo = Dyn::Get(record, "repo");
return {Dyn::Truthy(repo) ? fs::path(Dyn::AbsPath(Dyn::Str(repo))) : cwd, TreeOf(record, cwd)};
}
std::int32_t Main(const Arguments& arguments, const Json& config) {
const std::string cwd = fs::current_path().string();
const std::string repo = Dyn::AbsPath(arguments.repo && !arguments.repo->empty() ? *arguments.repo : cwd);
@ -467,14 +491,7 @@ namespace Crafter::Review::Freeze {
std::println("frozen: {} commit(s) {}..{} (base from {}) -> {}/ ({})", files.size(), Head12(report.at("base").get<std::string>()), Head12(report.at("head").get<std::string>()),
report.at("baseFrom").get<std::string>(), rel, Dyn::Join(files, ", "));
std::println("hash: {}", report.at("hash").get<std::string>());
std::string tree;
if (Dyn::Truthy(Dyn::Get(report, "worktree"))) {
tree = report.at("worktree").get<std::string>();
} else {
std::error_code error;
tree = fs::weakly_canonical(repo, error) == fs::weakly_canonical(cwd, error) ? std::string(".") : report.at("repo").get<std::string>();
}
const std::string line = RunLine(report, rel, tree);
const std::string line = RunLine(report, rel, TreeOf(report, cwd));
std::ofstream(fs::path(report.at("out").get<std::string>()) / "run-line.txt", std::ios::binary) << line << "\n";
std::println("run (from the repository root, in a detached background shell; also in {}):\n {}", Dyn::JoinPath(rel, "run-line.txt"), line);
return 0;