test(pipeline): the checks of the pure pipeline

Stream, Normalize, Merge, Dispositions, Config, CodeKeys and the pure half
of CommitsMode, each a test of its own (131 checks). The fixture rewrites
are checked in (code-keys/<name>/, commits3/), so the suite reads fixtures
and edits nothing. The allowlist, command-line and MCP checks of
CommitsMode come with the pool they test.

Among them: this binary's `<exe> sql` spelling counts as a SQL call, and a
result line with a raw U+2028 in a JSON string is still the result line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jorijn van der Graaf 2026-10-31 06:56:09 +01:00
commit 71dba5690f
Signed by: jorijnvdgraaf
GPG key ID: 2937E59CDCC1BCFB
12 changed files with 3778 additions and 1 deletions

View file

@ -64,12 +64,19 @@ namespace {
// One per directory under tests/, each an ordinary main() that links the // One per directory under tests/, each an ordinary main() that links the
// library: exit 0 passes, nonzero fails, 77 skips. // library: exit 0 passes, nonzero fails, 77 skips.
constexpr std::array<std::string_view, 5> Tests = { constexpr std::array<std::string_view, 12> Tests = {
"Crypto", "Crypto",
"Text", "Text",
"Platform", "Platform",
"Process", "Process",
"Goldens", "Goldens",
"Stream",
"Normalize",
"Merge",
"Dispositions",
"Config",
"CommitsMode",
"CodeKeys",
}; };
// The version the binary reports: CI sets it from the tag, a checkout // The version the binary reports: CI sets it from the tag, a checkout

180
tests/CodeKeys/main.cpp Normal file
View file

@ -0,0 +1,180 @@
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
// Code keys: a static code pass is keyed on its code, a functional pass on
// its code AND the range. Every rewrite of the commits fixture is checked in
// under tests/Goldens/fixture/code-keys/<rewrite>/.
import std;
import Crafter.Review;
namespace fs = std::filesystem;
using namespace Crafter::Review;
namespace {
std::int32_t Failures = 0;
const std::string Fn(Plan::FunctionalLens);
void Check(std::string_view name, bool cond, std::string_view detail = {}) {
std::println("{}{}{}", cond ? " ok " : " FAIL ", name, !cond && !detail.empty() ? std::format(" ({})", detail) : std::string());
if (!cond) ++Failures;
}
using Keys = std::map<std::string, std::string>;
Keys CodeKeys(const fs::path& d, const std::string& lens = "repo-bug-prover.md", std::string_view env = {}) {
const Inventory::Built built = Inventory::Build("commits", d.string());
const Plan::CommitCode cc = Plan::CommitCodeOf(Artifact::PatchTexts(d));
Keys out;
for (const Json& u : built.out.at("units")) {
const std::string id = u.at("id").get<std::string>();
if (Plan::KindFor(lens, u) != "code") continue;
if (lens == Fn && (id.ends_with(":outside") || id.ends_with(":absent") || id.ends_with("#hdr"))) continue;
const Json job = {{"kind", "block"}, {"unit", id}, {"unitKind", "code"}};
out[id] = Plan::BlockReuseKey(job, lens, Plan::UnitText(built.lines, u), cc, {}, "c", 1, env).value_or("");
}
return out;
}
std::optional<std::string> WholeKey(const fs::path& d, const std::string& lens = "repo-better-solution.md", std::string_view env = {}) {
return Plan::WholeCodeKey({{"kind", "whole"}, {"unit", nullptr}}, lens, Plan::CommitCodeOf(Artifact::PatchTexts(d)), {}, "c", 1, env);
}
std::set<std::string> Changed(const Keys& now, const Keys& before) {
std::set<std::string> out;
for (const auto& [id, key] : before) {
if (now.at(id) != key) out.insert(id);
}
return out;
}
bool AllChanged(const Keys& now, const Keys& before) {
return Changed(now, before).size() == before.size();
}
std::string Show(const std::set<std::string>& ids) {
Json out = Json::array();
for (const std::string& id : ids) out.push_back(id);
return Dyn::ReprValue(out);
}
}
int main() {
std::println("reuse keys: a static code pass is keyed on its code; a functional pass on its code AND the range (fixtures/commits and rewrites of it)");
const fs::path fixture = fs::current_path() / "tests" / "Goldens" / "fixture";
const fs::path src = fixture / "commits";
auto rewrite = [&](std::string_view name) { return fixture / "code-keys" / name; };
const Keys base = CodeKeys(src);
const Keys fbase = CodeKeys(src, Fn);
const Keys sbase = CodeKeys(src, "repo-security.md");
std::set<std::string> baseValues;
std::set<std::string> sbaseValues;
for (const auto& [id, key] : base) baseValues.insert(key);
for (const auto& [id, key] : sbase) sbaseValues.insert(key);
const bool keysDiffer = std::ranges::all_of(base, [&](const auto& entry) { return sbase.contains(entry.first) && sbase.at(entry.first) != entry.second; });
Check("the security lens keys the same code units as the bug-prover, every key its own (the lens file is in the key): a verdict of one is never evidence for the other",
std::ranges::equal(std::views::keys(sbase), std::views::keys(base)) && keysDiffer && sbaseValues.size() == baseValues.size());
std::set<std::string> slots;
for (const auto& [id, key] : base) {
if (id.ends_with(":outside") || id.ends_with(":absent")) slots.insert(id);
}
std::set<std::string> fbaseValues;
for (const auto& [id, key] : fbase) fbaseValues.insert(key);
const std::set<std::string> functionalUnits = {"p1:hello.py#1", "p2:hello.py#1", "p2:hello.py#2"};
std::set<std::string> shared;
std::ranges::set_intersection(baseValues, fbaseValues, std::inserter(shared, shared.begin()));
Check("fixture: every static code unit has a key (4 slots, hunks, headers), the functional units are the hunks alone, no doc unit, no shared key",
std::ranges::all_of(base, [](const auto& entry) { return !entry.second.empty(); }) && slots.size() == 4 && base.contains("p2:hello.py#hdr")
&& std::set<std::string>(std::views::keys(fbase).begin(), std::views::keys(fbase).end()) == functionalUnits
&& std::ranges::none_of(base, [](const auto& entry) { return entry.first.contains(".md#"); }) && shared.empty());
const fs::path rw = rewrite("rewrite");
Check("a rewrite that leaves the code alone (ids, counter, blobs, line numbers, git version, a reworded message) -> every key the same, static and functional",
CodeKeys(rw) == base && CodeKeys(rw, Fn) == fbase && WholeKey(rw) == WholeKey(src) && WholeKey(rw, Fn) == WholeKey(src, Fn)
&& Artifact::ArtifactHash("commits", rw) != Artifact::ArtifactHash("commits", src));
const fs::path code = rewrite("code");
std::set<std::string> diff = Changed(CodeKeys(code), base);
std::set<std::string> expected = slots;
expected.insert("p2:hello.py#1");
Check("one changed line in p2:hello.py#1 -> static: that hunk and the four slots, nothing else", diff == expected, Show(diff));
Check("...functional: EVERY unit is asked again (the range is in its key)", AllChanged(CodeKeys(code, Fn), fbase));
Check("...and both whole passes", WholeKey(code) != WholeKey(src) && WholeKey(code, Fn) != WholeKey(src, Fn));
const Keys ctx = CodeKeys(rewrite("context"));
Check("a changed context line re-keys its hunk", ctx.at("p2:hello.py#1") != base.at("p2:hello.py#1") && ctx.at("p2:hello.py#2") == base.at("p2:hello.py#2"));
const fs::path nobody = rewrite("nobody");
Check("p2 loses its body -> p2:absent alone (static); nothing functional", Changed(CodeKeys(nobody), base) == std::set<std::string>{"p2:absent"} && CodeKeys(nobody, Fn) == fbase);
const fs::path bse = rewrite("base");
Check("another base -> every key changes, static, functional and both wholes", AllChanged(CodeKeys(bse), base) && AllChanged(CodeKeys(bse, Fn), fbase) && WholeKey(bse) != WholeKey(src));
const fs::path mode = rewrite("mode");
expected = slots;
expected.insert("p2:hello.py#hdr");
Check("a mode change re-keys the file header (and the slots), not the hunks; the functional hunks follow the range", Changed(CodeKeys(mode), base) == expected
&& AllChanged(CodeKeys(mode, Fn), fbase));
const fs::path docs = rewrite("docs");
Check("a reworded README sentence -> no key changes anywhere (documentation is nobody's code)", CodeKeys(docs) == base && CodeKeys(docs, Fn) == fbase && WholeKey(docs) == WholeKey(src));
const fs::path nodoc = rewrite("nodoc");
Check("p2 drops its README hunk -> p2's two slots and the whole passes are asked again, no hunk", Changed(CodeKeys(nodoc), base) == std::set<std::string>{"p2:absent", "p2:outside"}
&& WholeKey(nodoc) != WholeKey(src) && CodeKeys(nodoc, Fn) == fbase);
// The environment in the keys (plan.ENVIRONMENTS item 1).
const Keys sb = CodeKeys(src, "repo-bug-prover.md", "supabase");
const Keys fb = CodeKeys(src, Fn, "browser+supabase");
Check("environment: a lens in its default environment keeps the key every stored verdict was built with (none for the static lenses, browser for the functional lens)",
CodeKeys(src, "repo-bug-prover.md", "none") == base && CodeKeys(src, Fn, "browser") == fbase && WholeKey(src, "repo-better-solution.md", "none") == WholeKey(src)
&& WholeKey(src, Fn, "browser") == WholeKey(src, Fn) && Plan::EnvDeps("repo-bug-prover.md").empty() && Plan::EnvDeps(Fn, "browser").empty()
&& Plan::EnvDeps(Fn, "browser+supabase") == std::vector<std::string>{"env=browser+supabase"});
std::set<std::string> everyKey;
for (const Plan::Environment& environment : Plan::Environments) {
for (const auto& [id, key] : CodeKeys(src, "repo-bug-prover.md", environment.name)) everyKey.insert(key);
}
Check("environment: any other environment re-keys every unit, block and whole, static and functional - each environment its own keys, none shared",
AllChanged(sb, base) && AllChanged(fb, fbase) && WholeKey(src, "repo-better-solution.md", "supabase") != WholeKey(src)
&& WholeKey(src, Fn, "browser+supabase") != WholeKey(src, Fn) && everyKey.size() == Plan::Environments.size() * base.size());
expected = slots;
expected.insert("p2:hello.py#1");
Check("environment: the code rules hold inside an environment - a rewrite that leaves the code alone keeps every supabase key, a changed line re-keys that hunk and the slots alone",
CodeKeys(rw, "repo-bug-prover.md", "supabase") == sb && CodeKeys(rw, Fn, "browser+supabase") == fb
&& WholeKey(rw, "repo-better-solution.md", "supabase") == WholeKey(src, "repo-better-solution.md", "supabase")
&& Changed(CodeKeys(code, "repo-bug-prover.md", "supabase"), sb) == expected);
const Plan::CommitCode cc = Plan::CommitCodeOf(Artifact::PatchTexts(src));
Check("whole key: both wholes have keys and differ; a block never; the bug-prover has none", WholeKey(src) && WholeKey(src, Fn) && WholeKey(src) != WholeKey(src, Fn)
&& !WholeKey(src, "repo-bug-prover.md") && !Plan::WholeCodeKey({{"kind", "block"}, {"unit", "x"}}, "repo-better-solution.md", cc, {}, "c", 1));
const Json job = {{"kind", "whole"}, {"unit", nullptr}};
const std::vector<std::string> w1 = {"w1"};
Json withModel = job;
withModel["model"] = "m";
const std::set<std::optional<std::string>> wholes = {
WholeKey(src), Plan::WholeCodeKey(job, "repo-better-solution.md", cc, w1, "c", 1), Plan::WholeCodeKey(job, "repo-better-solution.md", cc, {}, "c2", 1),
Plan::WholeCodeKey(job, "repo-better-solution.md", cc, {}, "c", 2), Plan::WholeCodeKey(withModel, "repo-better-solution.md", cc, {}, "c", 1),
};
Check("whole key: dispositions, the charter, the prompt version and the model are in it", wholes.size() == 5);
Plan::CommitCode two;
two.base = "b";
two.range = "r";
two.code = {{3, Plan::Sha("")}, {4, Plan::Sha("")}};
two.body = {{3, true}, {4, true}};
two.docs = {{3, {"h"}}, {4, {"h"}}};
two.upto = {{3, "u"}, {4, "u"}};
Plan::CommitCode renamed = two;
renamed.docs = {{3, {"h2"}}, {4, {"h"}}};
Check("slot keys: two documentation-only commits do not share a slot key (the position is in it); a renamed doc file re-asks the slots of its commit",
Plan::CodeKeyParts("p3:absent", two) != Plan::CodeKeyParts("p4:absent", two)
&& Plan::CodeKeyParts("p3:outside", two) != Plan::CodeKeyParts("p3:outside", renamed) && Plan::CodeKeyParts("p3:absent", two) != Plan::CodeKeyParts("p3:absent", renamed));
Check("a hunk is keyed on its file and text, not on its number or its commit; the functional ident is its own", Plan::CodeKeyParts("p2:hello.py#2", cc) == Plan::CodeKeyParts("p7:hello.py#5", cc)
&& Plan::CodeKeyParts("p2:hello.py#2", cc).first != Plan::CodeKeyParts("p2:other.py#2", cc).first
&& Plan::CodeKeyParts("p2:hello.py#hdr", cc).first != Plan::CodeKeyParts("p2:hello.py#1", cc).first);
const std::vector<std::string> parts = {"repo-bug-prover.md", "U", Plan::Sha("T"), Plan::Sha("d"), "w1", "cs", "1", "", "max"};
Check("reuse key: the pre-2026-10-02 formula with the effort slot frozen at max", Plan::ReuseKey({{"kind", "block"}, {"unit", "U"}}, "repo-bug-prover.md", "T", {"d"}, {"w1"}, "cs", 1)
== Plan::Sha(Dyn::Join(parts, "|")).substr(0, 16) && Plan::ReuseKeyEffortSlot == "max");
Check("code_norm leaves a hunk body alone", Plan::CodeNorm("@@ -1,2 +1,2 @@ f\n-- \n+index 1..2\n+@@ -1 +1 @@") == "@@ f\n-- \n+index 1..2\n+@@ -1 +1 @@");
const Json e = {{"unit", "p2:hello.py#2"}, {"coverage", {{"item", "p2:hello.py#2"}, {"verdict", "clear"}, {"basis", "b"}}}, {"findings", Json::array()},
{"notes", {{{"note", "n"}, {"items", {"p2:hello.py#2", "p2:hello.py#1"}}}}}};
const Json r = Plan::Rebind(e, "p2:hello.py#3");
Json v = {{"verdict", "SEND-READY"}, {"findings", r.at("findings")}, {"notes", r.at("notes")}, {"coverage", {r.at("coverage")}}};
const std::vector<std::string> ids = {"p2:hello.py#3"};
const Json n = Normalize::NormalizeOutput(v, {}, "repo-bug-prover.md", "H", Json::array({{{"id", "p2:hello.py#3"}}}), ids);
Check("rebind: the stored result under the unit's id of today, and it covers the renumbered unit", r.at("unit") == "p2:hello.py#3"
&& r.at("notes").at(0).at("items") == Json::array({"p2:hello.py#3"}) && e.at("coverage").at("item") == "p2:hello.py#2" && n.at("verdict") == "SEND-READY"
&& n.at("coverageMissing").empty());
return Failures ? 1 : 0;
}

271
tests/CommitsMode/main.cpp Normal file
View file

@ -0,0 +1,271 @@
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
// Commits mode: the inventory, the gates, the plans (the functional lens's
// n/a), the prompts, over the commits fixture. The allowlist, command line
// and MCP checks sit with the pool they test.
import std;
import Crafter.Review;
namespace fs = std::filesystem;
using namespace Crafter::Review;
namespace {
std::int32_t Failures = 0;
const std::string Fn(Plan::FunctionalLens);
void Check(std::string_view name, bool cond, std::string_view detail = {}) {
std::println("{}{}{}", cond ? " ok " : " FAIL ", name, !cond && !detail.empty() ? std::format(" ({})", detail) : std::string());
if (!cond) ++Failures;
}
std::set<std::string> Set(const std::vector<std::string>& values) {
return {values.begin(), values.end()};
}
std::string Show(const std::set<std::string>& values) {
Json out = Json::array();
for (const std::string& value : values) out.push_back(value);
return Dyn::ReprValue(out);
}
const Json& Unit(const Json& units, std::string_view id) {
for (const Json& unit : units) {
if (unit.at("id") == id) return unit;
}
throw std::runtime_error(std::format("no unit {}", id));
}
const Json& Job(const std::vector<Json>& jobs, std::string_view kind, std::string_view unit = {}) {
for (const Json& job : jobs) {
if (job.at("kind") == kind && (unit.empty() || job.at("unit") == unit)) return job;
}
throw std::runtime_error(std::format("no {} job {}", kind, unit));
}
std::vector<std::pair<std::string, std::vector<std::string>>> Gates(const std::vector<Plan::Gate>& gates) {
std::vector<std::pair<std::string, std::vector<std::string>>> out;
for (const Plan::Gate& gate : gates) out.emplace_back(std::string(gate.title), std::vector<std::string>(gate.lenses.begin(), gate.lenses.end()));
return out;
}
std::vector<std::string> Titles(const std::vector<Plan::Gate>& gates) {
std::vector<std::string> out;
for (const Plan::Gate& gate : gates) out.emplace_back(gate.title);
return out;
}
// re.findall(r'(?i)\b(exploit\w*|attack\w*|...)\b', text): the words that
// start with one of the stems.
std::vector<std::string> Offensive(std::string_view text) {
constexpr std::array<std::string_view, 10> Stems = {"exploit", "attack", "payload", "malicious", "weaponi", "hack", "pwn", "backdoor", "intrusion", "penetrat"};
const std::u32string decoded = Dyn::Decode(text);
std::vector<std::string> found;
std::size_t at = 0;
while (at < decoded.size()) {
if (!Dyn::IsWord(decoded[at])) {
++at;
continue;
}
std::size_t end = at;
while (end < decoded.size() && Dyn::IsWord(decoded[end])) ++end;
const std::u32string_view word = std::u32string_view(decoded).substr(at, end - at);
for (const std::string_view stem : Stems) {
if (word.size() < stem.size()) continue;
bool match = true;
for (std::size_t i = 0; i < stem.size(); ++i) match = match && Dyn::IgnoreCaseLetter(word[i]) == stem[i];
if (match) {
found.push_back(Dyn::Encode(word));
break;
}
}
at = end;
}
return found;
}
}
int main() {
std::println("commits mode: inventory, gates, plans (the functional lens's n/a), prompts (fixtures/commits)");
SetHome(fs::current_path());
const fs::path fixture = fs::current_path() / "tests" / "Goldens" / "fixture";
const fs::path d = fixture / "commits";
const Inventory::Built built = Inventory::Build("commits", d.string());
const Json& units = built.out.at("units");
const std::vector<std::string>& lines = built.lines;
std::vector<std::string> ids;
for (const Json& u : units) ids.push_back(u.at("id").get<std::string>());
const std::set<std::string> idSet = Set(ids);
Check("body-less p1: no msg/trailers units, subject/diffstat/hunk present", !idSet.contains("p1:msg") && !idSet.contains("p1:trailers") && idSet.contains("p1:subject")
&& idSet.contains("p1:diffstat") && idSet.contains("p1:hello.py#1"));
Check("p2: msg unit, no trailers, three hunks over two files", idSet.contains("p2:msg") && !idSet.contains("p2:trailers") && idSet.contains("p2:README.md#1")
&& idSet.contains("p2:hello.py#1") && idSet.contains("p2:hello.py#2"));
Check("the three whole slots", idSet.contains("whole:order") && idSet.contains("whole:absent") && idSet.contains("whole:behaviour") && !idSet.contains("whole:recipients"));
Check("body-less commit: diffstat unit starts at the --- line", Plan::UnitText(lines, Unit(units, "p1:diffstat")).starts_with("---"));
std::string all;
for (const std::string& name : Artifact::PatchFiles(d)) all += *Platform::ReadFile(d / name);
Check("artifact_hash = sha256 of the sorted concatenation; patch_head = the From line of the last patch", Artifact::ArtifactHash("commits", d) == Sha256Hex(all)
&& Artifact::PatchHead(d) == std::string(40, '2'));
const auto g = Gates(Plan::GatesFor());
std::vector<std::string> gateNames;
for (const auto& [title, lenses] : g) gateNames.push_back(Dyn::Split(title, " — ")[0]);
Check("gates: 1 approach, 2 functional, 3 proof - the bug-prover and the security lens side by side (2026-10-22), the owner stays Gate 4",
gateNames == std::vector<std::string>{"Gate 1", "Gate 2", "Gate 3"} && g[0].second == std::vector<std::string>{"repo-better-solution.md"} && g[1].second == std::vector<std::string>{Fn}
&& g[2].second == std::vector<std::string>{"repo-bug-prover.md", "repo-security.md"});
const std::vector<std::string> onlyFn = {Fn};
const std::vector<std::string> security = {"repo-security.md"};
std::set<std::string> lensSet;
for (const Plan::Lens& lens : Plan::Lenses) lensSet.emplace(lens.file);
using GateList = std::vector<std::pair<std::string, std::vector<std::string>>>;
Check("mode_lenses = the four; --only keeps, --without drops, an emptied gate disappears; Gate 3 with one of its lenses dropped keeps the other",
Set(Plan::ModeLenses()) == lensSet && Gates(Plan::GatesFor(onlyFn)) == GateList{{"Gate 2 — Functional", {Fn}}}
&& Titles(Plan::GatesFor({}, onlyFn)) == std::vector<std::string>{"Gate 1 — Approach", "Gate 3 — Proof"} && Plan::GatesFor(onlyFn, onlyFn).empty()
&& Gates(Plan::GatesFor({}, security)).back() == std::pair<std::string, std::vector<std::string>>{"Gate 3 — Proof", {"repo-bug-prover.md"}}
&& Gates(Plan::GatesFor(security)) == GateList{{"Gate 3 — Proof", {"repo-security.md"}}});
const std::vector<std::string> threeOff = {"repo-better-solution.md", "repo-bug-prover.md", "repo-security.md"};
const std::vector<std::string> bugOff = {"repo-bug-prover.md"};
Check("lenses off by config: gates_for drops the lens and its gate; --only cannot bring it back", Titles(Plan::GatesFor({}, {}, onlyFn)) == std::vector<std::string>{"Gate 1 — Approach", "Gate 3 — Proof"}
&& Plan::GatesFor(onlyFn, {}, onlyFn).empty() && Gates(Plan::GatesFor({}, {}, threeOff)) == GateList{{"Gate 2 — Functional", {Fn}}}
&& Gates(Plan::GatesFor({}, {}, bugOff)).back() == std::pair<std::string, std::vector<std::string>>{"Gate 3 — Proof", {"repo-security.md"}});
const std::vector<std::string> allLenses(lensSet.begin(), lensSet.end());
const std::set<std::string> nrAll = Set(Plan::NotReviewed(units, lines, Plan::ModeLenses()));
Check("not reviewed (every lens runs): the text units and the documentation units", nrAll == std::set<std::string>{"p1:subject", "p1:diffstat", "p2:subject", "p2:msg", "p2:diffstat", "p2:README.md#hdr", "p2:README.md#1"},
Show(nrAll));
std::vector<std::string> statics;
for (const Plan::Lens& lens : Plan::Lenses) {
if (lens.file != Fn) statics.emplace_back(lens.file);
}
std::set<std::string> plusBehaviour = nrAll;
plusBehaviour.insert("whole:behaviour");
std::set<std::string> plusStatic = nrAll;
plusStatic.insert({"whole:order", "whole:absent", "p1:outside", "p1:absent", "p2:outside", "p2:absent", "p1:hello.py#hdr", "p2:hello.py#hdr"});
Check("not reviewed is derived from the lenses that run: the functional lens off adds whole:behaviour; only the functional lens adds the static slots and the file headers (a mechanical n/a is no reading)",
Set(Plan::NotReviewed(units, lines, statics)) == plusBehaviour && Set(Plan::NotReviewed(units, lines, onlyFn)) == plusStatic, Show(Set(Plan::NotReviewed(units, lines, onlyFn))));
std::set<std::string> codeIds;
for (const std::string& id : ids) {
if (Plan::UnitKindOf(id) == "code" && !id.contains(".md#")) codeIds.insert(id);
}
auto unitsOf = [](const std::vector<Json>& jobs) {
std::set<std::string> out;
for (const Json& job : jobs) out.insert(job.at("unit").get<std::string>());
return out;
};
const Plan::LensPlan approach = Plan::PlanLens("repo-better-solution.md", units, lines);
std::vector<std::string> expected = {approach.whole.at("expectedIds").at(0).get<std::string>(), approach.whole.at("expectedIds").at(1).get<std::string>()};
std::ranges::sort(expected);
Check("repo-better-solution: whole pass over order+absent, every code unit incl. outside/absent, no doc unit", !approach.whole.is_null()
&& expected == std::vector<std::string>{"whole:absent", "whole:order"} && approach.whole.at("expectedIds").size() == 2 && unitsOf(approach.blocks) == codeIds
&& std::ranges::all_of(approach.blocks, [](const Json& j) { return j.at("kind") == "block"; }));
const Plan::LensPlan prover = Plan::PlanLens("repo-bug-prover.md", units, lines);
Check("repo-bug-prover: no whole pass, the same code units", prover.whole.is_null() && unitsOf(prover.blocks) == codeIds && codeIds.contains("p2:hello.py#hdr"));
const Plan::LensPlan sec = Plan::PlanLens("repo-security.md", units, lines);
const Plan::Lens& s = Plan::LensOf("repo-security.md");
const Plan::Lens& b = Plan::LensOf("repo-bug-prover.md");
const bool sameShape = s.gate == b.gate && s.whole == b.whole && std::ranges::equal(s.slots, b.slots) && std::ranges::equal(s.block, b.block) && s.model == b.model && s.reuse == b.reuse
&& s.wholeReuse == b.wholeReuse && s.reads == b.reads && s.key == b.key && s.empirical == b.empirical && s.perCommitSlots == b.perCommitSlots && s.env == b.env;
Check("repo-security: the bug-prover's shape - no whole pass, the same code units, static blocks keyed on the code, three reads, none by default, not empirical",
sec.whole.is_null() && unitsOf(sec.blocks) == codeIds && sameShape && s.gate == 3
&& std::ranges::all_of(sec.blocks, [](const Json& j) { return j.at("maxTurns") == 60 && j.at("timeoutMin") == 40 && j.at("model") == Plan::ModelFor("opus"); }));
const std::vector<std::string> exclude = {"**/*.test.*", "docs/**"};
const Plan::LensPlan functional = Plan::PlanLens(Fn, units, lines, {}, exclude);
std::map<std::string, std::string> kinds;
for (const Json& j : functional.blocks) kinds[j.at("unit").get<std::string>()] = j.at("kind").get<std::string>();
const std::map<std::string, std::string> wantKinds = {{"p1:hello.py#hdr", "auto-na"}, {"p1:hello.py#1", "block"}, {"p2:hello.py#hdr", "auto-na"}, {"p2:hello.py#1", "block"}, {"p2:hello.py#2", "block"}};
Check("repo-functional: whole pass over whole:behaviour only; hunks as blocks; file headers n/a; no per-commit slots; no doc unit", !functional.whole.is_null()
&& functional.whole.at("expectedIds") == Json::array({"whole:behaviour"}) && kinds == wantKinds);
const Json& functionalBlock = Job(functional.blocks, "block");
Check("functional budgets: block 90/60, whole 220/120; static block 60/40, whole 180/105", functionalBlock.at("maxTurns") == 90 && functionalBlock.at("timeoutMin") == 60
&& functional.whole.at("maxTurns") == 220 && functional.whole.at("timeoutMin") == 120 && prover.blocks.at(0).at("maxTurns") == 60 && prover.blocks.at(0).at("timeoutMin") == 40
&& approach.whole.at("maxTurns") == 180 && approach.whole.at("timeoutMin") == 105);
// A third commit touching a test file: excluded for the functional lens,
// a block for the static ones.
const Inventory::Built three = Inventory::Build("commits", (fixture / "commits3").string());
const std::vector<std::string> testsGlob = {"tests/**"};
std::map<std::string, std::pair<std::string, std::string>> f3;
for (const Json& j : Plan::PlanLens(Fn, three.out.at("units"), three.lines, {}, testsGlob).blocks) {
if (j.at("unit").get<std::string>().starts_with("p3:")) f3[j.at("unit").get<std::string>()] = {j.at("kind").get<std::string>(), Dyn::Get(j, "basis").is_string() ? j.at("basis").get<std::string>() : ""};
}
std::map<std::string, std::string> s3;
for (const Json& j : Plan::PlanLens("repo-bug-prover.md", three.out.at("units"), three.lines).blocks) {
if (j.at("unit").get<std::string>().starts_with("p3:")) s3[j.at("unit").get<std::string>()] = j.at("kind").get<std::string>();
}
Check("functional.exclude: the test hunk is a mechanical n/a naming the glob; the static lens still reads it", f3["p3:tests/test_hello.py#1"].first == "auto-na"
&& f3["p3:tests/test_hello.py#1"].second.contains("tests/**") && s3["p3:tests/test_hello.py#1"] == "block" && !f3.contains("p3:outside") && s3.contains("p3:outside"));
const std::vector<std::string> testTs = {"**/*.test.ts"};
const std::vector<std::string> github = {".github/**"};
Check("excluded_by: ** prefixes match at the root too", Plan::ExcludedBy("a.test.ts", testTs) == "**/*.test.ts" && Plan::ExcludedBy("src/a.test.ts", testTs)
&& !Plan::ExcludedBy("src/a.ts", testTs) && Plan::ExcludedBy(".github/x.yml", github));
// Prompts.
const std::string st = Prompt::SystemText();
Check("system text: commit range; the browser tools belong to a lens with an APPLICATION UNDER TEST block, not to the functional lens by name", st.contains("commit range")
&& st.contains("mcp__playwright__") && st.contains("APPLICATION UNDER TEST block") && !st.contains("The functional lens alone") && !st.contains("The functional lens acts"));
const std::string h = Artifact::ArtifactHash("commits", d);
const std::vector<std::string> dot = {"."};
const std::string rl = Prompt::RunLayer(".review-gates/push/x", "abc123", "2026-10-15", dot, h, lines, units, {}, "myrepo");
Check("run layer: INPUTS (tree `.`, base, no review record), the artifact with line numbers, the inventory", rl.contains("myrepo") && rl.contains("`.` is the working directory")
&& !Dyn::Lower(rl).contains("ledger") && rl.contains("\nARTIFACT (frozen, hash ") && rl.contains("\n\nINVENTORY - ") && rl.contains("\n0001| "));
const std::vector<std::string> worktree = {".review-gates/trees/r-1234"};
const std::vector<std::string> captures = {"captures/"};
const std::string rl2 = Prompt::RunLayer("x", "", "2026-10-15", worktree, h, lines, units, captures, "r");
Check("run layer: the context paths and a worktree named", rl2.contains("captures/") && rl2.contains(".review-gates/trees/r-1234") && rl2.contains("base-commit line"));
const std::string totp = "/app/bin/crafter-review totp cypress/fixtures/created-user.json";
const std::string ab = Prompt::AppBlock({{"appUrl", "http://localhost:8080"}, {"apiUrls", {"http://127.0.0.1:54321"}}, {"login", {{"notes", "TOTP enrolled"}}}, {"notes", "leave billing alone"}},
"cypress/fixtures/created-user.json", totp);
Check("app block: this lens drives it (no lens named); the URL, the origins, the fixture path (not its content), the exact TOTP command, the notes, the shared-stack rules",
ab.contains("this lens drives it") && !ab.contains("every other lens") && ab.contains("http://localhost:8080") && ab.contains("127.0.0.1:54321")
&& ab.contains("cypress/fixtures/created-user.json") && ab.contains(std::format("`{}`", totp)) && ab.contains("TOTP enrolled") && ab.contains("leave billing alone")
&& ab.contains("not reset between passes") && ab.contains("browser_take_screenshot") && ab.contains("browser_snapshot before every action"));
const std::string ab2 = Prompt::AppBlock({{"appUrl", "http://localhost:1"}});
Check("app block without a fixture says so", ab2.contains("no fixture was found"));
const std::string tailApproach = Prompt::Tail(approach.whole, units, lines);
const std::string tailFunctional = Prompt::Tail(functional.whole, units, lines);
Check("whole tails: the approach text lists order+absent; the functional text lists behaviour and names the APPLICATION UNDER TEST", tailApproach.contains("for THIS repository")
&& tailApproach.contains("whole:order") && tailFunctional.contains("as a USER") && tailFunctional.contains("whole:behaviour") && !tailFunctional.contains("whole:order"));
Check("block tails: the functional hint on a functional block, none on a static hunk, the repo hints on the slots",
Prompt::Tail(functionalBlock, units, lines).contains("judges ONE hunk in the running application") && !Prompt::Tail(Job(prover.blocks, "block", "p2:hello.py#1"), units, lines).contains("judges ONE hunk")
&& Prompt::Tail(Job(prover.blocks, "block", "p1:absent"), units, lines).contains("body-less commit") && Prompt::Tail(Job(prover.blocks, "block", "p1:outside"), units, lines).contains("users and callers"));
const std::string ll = Prompt::LensLayer(Fn, "CHARTER TEXT", "DISPOSITIONS BLOCK", ab, "SUMMARY");
const std::string sp = Prompt::SystemPrompt(st, rl, ll);
const std::string up = Prompt::UserPrompt(tailFunctional, {{"maxTurns", 220}});
Check("lens layer order: charter, application block, dispositions, the whole summary; system prompt = common + run + lens; user prompt = tail + budget line, no run layer",
ll.find("CHARTER TEXT") < ll.find("APPLICATION UNDER TEST") && ll.find("APPLICATION UNDER TEST") < ll.find("DISPOSITIONS BLOCK") && ll.find("DISPOSITIONS BLOCK") < ll.find("SUMMARY")
&& sp.starts_with(std::format("{}{}{}{}", st, Prompt::SepRun, rl, Prompt::SepLens)) && sp.ends_with(ll) && up.contains("Tool-call budget for this pass: 220")
&& !up.contains(rl.substr(0, 200)));
const std::string sql = "/app/bin/crafter-review sql";
const std::string dbb = Prompt::DbBlock({{"dbUrl", "postgresql://postgres:hunter2@127.0.0.1:54322/postgres"}, {"notes", "the scan tables are seeded"}}, sql, "postgres", false,
"it answers (x); its 3 applied migrations are the range HEAD's", true);
const std::string dba = Prompt::DbBlock({{"notes", ""}}, sql, "postgres", true);
Check("db block: the exact helper command and its heredoc, one transaction rolled back, nothing shared, the refused statements, the API roles, the limits, the evidence, the owner's notes - never the URL or its password",
dbb.starts_with("DATABASE UNDER TEST") && dbb.contains(std::format("`{} <<'SQL'`", sql)) && dbb.contains("rolls back at its end") && dbb.contains("not by the other passes")
&& dbb.contains("COMMIT") && dbb.contains("ROLLBACK TO are fine") && dbb.contains("set local role authenticated") && dbb.contains("request.jwt.claims") && dbb.contains("service_role")
&& dbb.contains("statement_timeout 20 s") && dbb.contains("NO DATABASE") && dbb.contains("the scan tables are seeded") && !dbb.contains("hunter2") && !dbb.contains("54322")
&& !dbb.contains("reaches the application") && dba.contains("Nothing you write here reaches the application")
&& dbb.contains("Checked by the driver before this gate: it answers (x); its 3 applied migrations are the range HEAD's.") && dba.contains("Checked by the driver before this gate: nothing (a dry run).")
&& dbb.contains("the role `postgres`, which bypasses row-level security.") && dba.contains("the role `postgres`.")
&& Prompt::DbBlock(Json::object(), sql, "reviewer", false, std::nullopt, false).contains("the role `reviewer`, which row-level security applies to."));
const std::string lld = Prompt::LensLayer(Fn, "CHARTER TEXT", "DISPOSITIONS BLOCK", ab, "SUMMARY", dbb);
const std::vector<std::string> layered = {std::format("YOUR CHARTER (process/lenses/{}), verbatim:\n\nCHARTER TEXT", Fn), ab, "DISPOSITIONS BLOCK",
"The whole pass of this lens on this hash concluded (the constraints your block pass rests on):\nSUMMARY"};
Check("lens layer with the database: charter, application block, database block, dispositions, summary; without one it is byte for byte what it was",
lld.find("CHARTER TEXT") < lld.find("APPLICATION UNDER TEST") && lld.find("APPLICATION UNDER TEST") < lld.find("DATABASE UNDER TEST")
&& lld.find("DATABASE UNDER TEST") < lld.find("DISPOSITIONS BLOCK") && lld.find("DISPOSITIONS BLOCK") < lld.find("SUMMARY")
&& Prompt::LensLayer(Fn, "CHARTER TEXT", "DISPOSITIONS BLOCK", ab, "SUMMARY", "") == ll && ll == Dyn::Join(layered, "\n\n"));
Check("system text: a lens with a DATABASE UNDER TEST block runs SQL there, rolled back, and has the exact command it prints",
st.contains("DATABASE UNDER TEST block runs SQL in that database, in transactions the pipeline rolls back") && st.contains("exact SQL command that block prints"));
Check("prompt version 1; the charters exist and the common text has the placeholder filled", PromptVersion == 1
&& std::ranges::all_of(Plan::Lenses, [](const Plan::Lens& lens) { return fs::exists(fs::current_path() / "process" / "lenses" / lens.file); }) && !st.contains("{{"));
// The security charter is the owner's defensive review of their own code
// (2026-10-22: a charter that reads as a request to do harm is declined by
// the process, and a declined pass is NOT RETURNED).
const std::string charter = Prompt::Charter("repo-security.md");
const std::vector<std::string> banned = Offensive(charter);
Check("the security charter: no offensive vocabulary; the throwaway local copy, the smallest step, no tooling built, refutations name the control, the per-commit slots, one residual-risk on the null path",
banned.empty() && charter.contains("throwaway local copy") && charter.contains("smallest step") && charter.contains("no tooling built") && charter.contains("Refutations name the control")
&& charter.contains("`pK:absent`") && charter.contains("`pK:outside`") && charter.contains("exactly one\n `residual-risk`") && charter.starts_with("# Lens: repo-security")
&& charter.contains("Calibration (append dated entries at the bottom)"), Dyn::Join(banned, ", "));
Check("the security charter names what the other lenses own and tells its blocks to route it as a note, never to suppress it", charter.contains("`repo-better-solution`")
&& charter.contains("`repo-bug-prover`") && charter.contains("`repo-functional`") && charter.contains("instead of suppressing it"));
return Failures ? 1 : 0;
}

147
tests/Config/main.cpp Normal file
View file

@ -0,0 +1,147 @@
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
// The consumer config: the defaults, the merge, unknown keys, validation,
// the lenses (run, environment), authors, the login fixture, the example.
#include <stdlib.h>
import std;
import Crafter.Review;
namespace fs = std::filesystem;
using namespace Crafter::Review;
namespace {
std::int32_t Failures = 0;
void Check(std::string_view name, bool cond, std::string_view detail = {}) {
std::println("{}{}{}", cond ? " ok " : " FAIL ", name, !cond && !detail.empty() ? std::format(" ({})", detail) : std::string());
if (!cond) ++Failures;
}
bool Any(const std::vector<std::string>& problems, std::string_view part) {
return std::ranges::any_of(problems, [&](const std::string& p) { return p.contains(part); });
}
std::string Show(const std::vector<std::string>& problems) {
Json out = Json::array();
for (const std::string& p : problems) out.push_back(p);
return Dyn::ReprValue(out);
}
}
int main() {
std::println("config: defaults, merge, unknown keys, validation, lenses (run, environment), authors, login fixture, the example");
// A git call of a test reads no global config: this machine's signs
// every commit with the owner's key.
setenv("GIT_CONFIG_GLOBAL", "/dev/null", 1);
setenv("GIT_CONFIG_SYSTEM", "/dev/null", 1);
SetHome(fs::current_path());
const std::string fn(Plan::FunctionalLens);
const fs::path td = fs::temp_directory_path() / std::format("crafter-review-config-{}", Platform::PublishedPid());
fs::remove_all(td);
fs::create_directories(td);
auto load = [&] { return Config::Load(td); };
Config::Loaded loaded = load();
Check("no file: the defaults, no problem, _path None", loaded.problems.empty() && loaded.config.at("aiTrailers") == "forbid"
&& loaded.config.at("functional").at("appUrl") == "http://localhost:8080" && loaded.config.at("_path").is_null());
fs::create_directories(td / ".review-gates");
const fs::path p = td / ".review-gates" / "config.json";
auto put = [&](const Json& value) { std::ofstream(p, std::ios::binary) << DumpSpaced(value); };
auto bad = [&](const Json& value) {
put(value);
return load().problems;
};
put({{"base", "origin/dev"}, {"aiTrailers", "allow"}, {"allowDirty", {"supabase/config.toml"}},
{"functional", {{"exclude", {"**/*.test.ts"}}, {"readyPath", "/health"}, {"extraTools", {"Bash(redis-cli:*)", "mcp__x__y"}}}}});
loaded = load();
Check("the file merges over the defaults, nested too", loaded.problems.empty() && loaded.config.at("base") == "origin/dev" && loaded.config.at("aiTrailers") == "allow"
&& loaded.config.at("functional").at("readyPath") == "/health" && loaded.config.at("functional").at("appUrl") == "http://localhost:8080" && loaded.config.at("_path") == p.string(),
Show(loaded.problems));
Check("rangeFrom 'base' without a base branch is a problem; 'sideways' is one too", Any(bad({{"rangeFrom", "base"}}), "needs a base")
&& Any(bad({{"rangeFrom", "sideways"}, {"base", "origin/dev"}}), "rangeFrom must be"));
std::vector<std::string> problems = bad({{"bogus", 1}, {"functional", {{"nope", 2}, {"appUrl", "ftp://x"}}}, {"aiTrailers", "maybe"}, {"authors", {"nomail"}}});
Check("unknown keys and bad values are each a problem", problems.size() == 5 && Any(problems, "bogus") && Any(problems, "functional.nope") && Any(problems, "appUrl")
&& Any(problems, "aiTrailers") && Any(problems, "authors"), Show(problems));
problems = bad({{"functional", {{"extraTools", {"rm -rf /"}}, {"mcp", {{"args", "x"}}}}}});
Check("extraTools must be Bash(<prefix>:*) or mcp__...; mcp.args a list", problems.size() == 2, Show(problems));
std::ofstream(p, std::ios::binary) << "{not json";
Check("not JSON -> one problem", load().problems.size() == 1);
Check("--config to a missing file is a problem", Any(Config::Load(td, td / "none.json").problems, "no such file"));
put(Json::object());
loaded = load();
bool allRun = true;
for (const Json& entry : loaded.config.at("lenses")) allRun = allRun && entry.at("run") == true;
bool defaultsMatch = true;
std::set<std::string> names;
for (const Plan::Lens& lens : Plan::Lenses) {
names.insert(Plan::LensName(lens.file));
defaultsMatch = defaultsMatch && Config::Defaults().at("lenses").at(Plan::LensName(lens.file)).at("environment") == lens.env;
}
const Json defaults = Config::Defaults();
std::set<std::string> defaultNames;
for (auto it = defaults.at("lenses").begin(); it != defaults.at("lenses").end(); ++it) defaultNames.insert(it.key());
const Json environments = {{"repo-better-solution.md", "none"}, {fn, "browser"}, {"repo-bug-prover.md", "none"}, {"repo-security.md", "none"}};
Check("lenses: the defaults run all four with none/browser/none/none - plan.LENSES' env; the table names every lens of plan.LENSES", loaded.problems.empty() && allRun
&& Config::LensesOff(loaded.config).empty() && Dyn::Equal(Config::Environments(loaded.config), environments) && defaultNames == names && defaultsMatch, Show(loaded.problems));
put({{"lenses", {{"repo-functional", {{"run", false}}}, {"repo-bug-prover", {{"environment", "browser"}}}}}});
loaded = load();
Check("lenses: run=false merges and lists the lens off in gate order; a static lens may take the browser", loaded.problems.empty()
&& Config::LensesOff(loaded.config) == std::vector<std::string>{fn} && Config::Environments(loaded.config).at("repo-bug-prover.md") == "browser"
&& loaded.config.at("lenses").at("repo-functional").at("environment") == "browser", Show(loaded.problems));
problems = bad({{"lenses", {{"nope", {{"run", true}}}, {"repo-better-solution", {{"run", "yes"}, {"environment", "http"}}}}}});
Check("lenses: an unknown lens is an unknown key; run must be a bool; environment must be one of none, browser, supabase, browser+supabase", problems.size() == 3
&& Any(problems, "lenses.nope") && Any(problems, "run must be") && Any(problems, "one of none, browser, supabase, browser+supabase"), Show(problems));
problems = bad({{"lenses", {{"repo-functional", {{"environment", "none"}}}}}});
Check("the functional lens running with no application under test is a problem; off with none is fine; an unknown environment is reported once",
problems.size() == 1 && problems[0].contains("empirical") && bad({{"lenses", {{"repo-functional", {{"run", false}, {"environment", "none"}}}}}}).empty()
&& bad({{"lenses", {{"repo-functional", {{"environment", "http"}}}}}}).size() == 1, Show(problems));
problems = bad({{"lenses", {{"repo-functional", {{"environment", "supabase"}}}}}});
Check("the database alone is no application under test for the functional lens; browser+supabase is; a static lens may take either, or the database alone",
problems.size() == 1 && problems[0].contains("empirical") && problems[0].contains("browser, browser+supabase")
&& bad({{"lenses", {{"repo-functional", {{"environment", "browser+supabase"}}}, {"repo-bug-prover", {{"environment", "supabase"}}},
{"repo-better-solution", {{"environment", "browser+supabase"}}}}}}).empty(), Show(problems));
loaded = load();
Check("supabase: the defaults are the local stack's Postgres (54322), no start/stop, no notes",
Dyn::Equal(loaded.config.at("supabase"), {{"dbUrl", "postgresql://postgres:postgres@127.0.0.1:54322/postgres"}, {"start", nullptr}, {"stop", nullptr}, {"notes", ""}}));
problems = bad({{"supabase", {{"dbUrl", "postgresql://postgres:secret@db.abcdefgh.supabase.co:5432/postgres"}}}});
Check("supabase.dbUrl: a hosted database is refused (this machine only), and the message does not echo the password", problems.size() == 1
&& problems[0].contains("not this machine") && !problems[0].contains("secret"), Show(problems));
Check("supabase.dbUrl: http:// is no database URL; localhost and ::1 are this machine; start/stop must be command lines; an unknown key is reported",
Any(bad({{"supabase", {{"dbUrl", "http://127.0.0.1:54321"}}}}), "postgres://") && bad({{"supabase", {{"dbUrl", "postgres://u:p@localhost:6543/x"}}}}).empty()
&& bad({{"supabase", {{"dbUrl", "postgresql://u@[::1]:5432/x"}}}}).empty() && Any(bad({{"supabase", {{"start", {"bash", "x"}}}}}), "supabase.start")
&& Any(bad({{"supabase", {{"nope", 1}}}}), "supabase.nope"));
Check("functional.appUrl is checked only while a lens that runs drives the application (plan.ENVIRONMENTS item 4)",
Any(bad({{"functional", {{"appUrl", "ftp://x"}}}}), "appUrl")
&& bad({{"functional", {{"appUrl", "ftp://x"}}}, {"lenses", {{"repo-functional", {{"run", false}}}, {"repo-bug-prover", {{"environment", "supabase"}}}}}}).empty()
&& Any(bad({{"functional", {{"appUrl", "ftp://x"}}}, {"lenses", {{"repo-functional", {{"run", false}}}, {"repo-bug-prover", {{"environment", "browser+supabase"}}}}}}), "appUrl"));
problems = bad({{"functional", {{"extraTools", {"Bash(psql:*)", "Bash(psql -h 127.0.0.1:*)", "Bash(psqlx:*)", "Bash(npm run e2e:*)", "mcp__x__y"}}}}});
Check("functional.extraTools: an entry pool.DENY overrides is a problem (psql commits to the shared database; npm is a package manager); a mere prefix twin is not",
problems.size() == 1 && problems[0].contains("Bash(psql:*)") && problems[0].contains("Bash(psql -h 127.0.0.1:*)") && problems[0].contains("Bash(npm run e2e:*)")
&& !problems[0].contains("psqlx") && problems[0].contains("supabase environment"), Show(problems));
Json withPassword = load().config;
withPassword["supabase"] = {{"dbUrl", "postgresql://postgres:hunter2@127.0.0.1:54322/postgres"}, {"start", nullptr}, {"stop", nullptr}, {"notes", ""}};
const Json rc = Config::Redacted(withPassword);
Check("redacted: the dump's copy of the config masks the database password and drops _path", rc.at("supabase").at("dbUrl") == "postgresql://postgres:***@127.0.0.1:54322/postgres"
&& !DumpSpaced(rc).contains("hunter2") && !rc.contains("_path"));
problems = bad({{"lenses", {{"repo-functional", false}}}});
Check("a bare false under lenses is a shape problem, never an exception; lenses_off and environments fall back to the defaults", problems.size() == 1
&& problems[0].contains("must be an object") && Config::LensesOff(load().config).empty() && Config::Environments(load().config).at(fn) == "browser", Show(problems));
Json allOff = Json::object();
for (auto it = defaults.at("lenses").begin(); it != defaults.at("lenses").end(); ++it) allOff[it.key()] = {{"run", false}};
problems = bad({{"lenses", allOff}});
Check("every lens off is a problem", problems.size() == 1 && problems[0].contains("every lens is off"), Show(problems));
const Json ex = Config::Example();
Check("the example validates and is what process/templates/config.json holds", Config::Validate(ex).empty()
&& Dyn::Equal(Dyn::LoadJsonFile(fs::current_path() / "process" / "templates" / "config.json"), ex));
const fs::path repo = td / "r";
fs::create_directories(repo);
Process::Run({"git", "-C", repo.string(), "init", "-q"});
Process::Run({"git", "-C", repo.string(), "config", "user.email", "me@example.com"});
Check("authors: the configured list, else git config user.email", Config::Authors({{"authors", {"a@b.c"}}}, repo) == std::vector<std::string>{"a@b.c"}
&& Config::Authors({{"authors", nullptr}}, repo) == std::vector<std::string>{"me@example.com"});
std::ofstream(td / "u.json", std::ios::binary) << "{}";
Check("login_fixture: the first candidate that exists", Config::LoginFixture({{"functional", {{"login", {{"fixtures", {"nope.json", "u.json"}}}}}}}, td) == "u.json"
&& !Config::LoginFixture({{"functional", {{"login", {{"fixtures", Json::array()}}}}}}, td));
fs::remove_all(td);
return Failures ? 1 : 0;
}

View file

@ -0,0 +1,46 @@
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
// Dispositions: in force, carried, lapsed, and the prompt block.
import std;
import Crafter.Review;
namespace fs = std::filesystem;
using namespace Crafter::Review;
namespace {
std::int32_t Failures = 0;
void Check(std::string_view name, bool cond, std::string_view detail = {}) {
std::println("{}{}{}", cond ? " ok " : " FAIL ", name, !cond && !detail.empty() ? std::format(" ({})", detail) : std::string());
if (!cond) ++Failures;
}
}
int main() {
std::println("dispositions: in-force / carried / lapsed, the prompt block");
const fs::path td = fs::temp_directory_path() / std::format("crafter-review-dispositions-{}", Platform::PublishedPid());
fs::create_directories(td);
std::ofstream(td / "0001-a.patch", std::ios::binary) << "x\nANCHOR TEXT\ny\nANCHOR TEXT\n";
const std::vector<Json> e = {
{{"id", "a"}, {"hash", "H1"}},
{{"id", "b"}, {"hash", "H0"}, {"carry", true}, {"anchors", {{{"file", "0001-a.patch"}, {"text", "ANCHOR TEXT"}, {"count", 2}}}}},
{{"id", "c"}, {"hash", "H0"}, {"carry", true}, {"anchors", {{{"file", "0001-a.patch"}, {"text", "ANCHOR TEXT"}, {"count", 1}}}}},
{{"id", "d"}, {"hash", "H0"}},
};
std::map<std::string, std::string> rows;
for (const Json& row : Dispositions::Classify(e, "H1", td)) rows[row.at("id").get<std::string>()] = row.at("status").get<std::string>();
const std::map<std::string, std::string> expected = {{"a", "in-force"}, {"b", "carried"}, {"c", "lapsed"}, {"d", "lapsed"}};
Check("hash match in-force, count match carried, count mismatch lapsed, no carry lapsed", rows == expected);
const std::vector<Json> inf = Dispositions::InForce(e, Dispositions::Classify(e, "H1", td));
Check("in_force = in-force + carried", inf.size() == 2 && inf[0].at("id") == "a" && inf[1].at("id") == "b");
const std::string blk = Dispositions::Block(inf, Dispositions::Classify(e, "H1", td), "repo-bug-prover.md", "H1");
Check("block text names the carried entry, nothing outside the register discharges, Gate 4 named", blk.contains("CARRIED to this hash") && blk.contains("- a [")
&& blk.contains("Nothing outside this list") && blk.contains("Gate 4") && !blk.contains("ledger"));
const std::vector<Json> e2 = {{{"id", "r"}, {"hash", "H1"}, {"kind", "refuted"}, {"lenses", {"repo-functional.md"}}}};
const std::vector<Json> rowsR = {{{"id", "r"}, {"status", "in-force"}}};
Check("refuted entry hidden from other lenses, shown to its own", Dispositions::Block(e2, rowsR, "repo-bug-prover.md", "H1").contains("(none)")
&& Dispositions::Block(e2, rowsR, Plan::FunctionalLens, "H1").contains("- r ["));
fs::remove_all(td);
return Failures ? 1 : 0;
}

View file

@ -0,0 +1,30 @@
From 1111111111111111111111111111111111111111 Mon Sep 17 00:00:00 2001
From: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
Date: Wed, 24 Sep 2026 10:00:00 +0200
Subject: [PATCH 1/3] add the greeting module
---
hello.py | 9 +++++++++
1 file changed, 9 insertions(+)
create mode 100644 hello.py
diff --git a/hello.py b/hello.py
new file mode 100644
index 0000000..a1b2c3d
--- /dev/null
+++ b/hello.py
@@ -0,0 +1,9 @@
+"""Greetings."""
+
+
+def greet():
+ return "hello"
+
+
+def farewell():
+ return "bye"
base-commit: 0000000000000000000000000000000000000000
--
2.51.0

View file

@ -0,0 +1,55 @@
From 2222222222222222222222222222222222222222 Mon Sep 17 00:00:00 2001
From: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
Date: Wed, 24 Sep 2026 10:05:00 +0200
Subject: [PATCH 2/3] greet by name and document it
The greeting takes a name now and the farewell mirrors it; the README
documents both calls. Callers that pass nothing still get the old text.
The format follows the greeting spec [1].
[1] https://example.org/greeting-spec
---
README.md | 4 ++++
hello.py | 8 ++++++--
2 files changed, 10 insertions(+), 2 deletions(-)
create mode 100644 README.md
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d4e5f6a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+# hello
+
+Call `greet(name)` or `farewell(name)`; without a name both return the
+plain text.
diff --git a/hello.py b/hello.py
index a1b2c3d..b7c8d9e 100644
--- a/hello.py
+++ b/hello.py
@@ -1,9 +1,13 @@
"""Greetings."""
-def greet():
- return "hello"
+def greet(name=None):
+ if name is None:
+ return "hello"
+ return "hello " + name
@@ -7,3 +11,5 @@ def greet():
-def farewell():
- return "bye"
+def farewell(name=None):
+ if name is None:
+ return "bye"
+ return "bye " + name
base-commit: 0000000000000000000000000000000000000000
--
2.51.0

View file

@ -0,0 +1,24 @@
From 3333333333333333333333333333333333333333 Mon Sep 17 00:00:00 2001
From: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
Date: Wed, 24 Sep 2026 10:10:00 +0200
Subject: [PATCH 3/3] add tests
Tests for greet.
---
tests/test_hello.py | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 tests/test_hello.py
diff --git a/tests/test_hello.py b/tests/test_hello.py
new file mode 100644
index 0000000..1111111
--- /dev/null
+++ b/tests/test_hello.py
@@ -0,0 +1,3 @@
+from hello import greet
+def test_greet():
+ assert greet("x") == "hello x"
base-commit: 0000000000000000000000000000000000000000
--
2.51.0

File diff suppressed because it is too large Load diff

76
tests/Merge/main.cpp Normal file
View file

@ -0,0 +1,76 @@
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
// Merging a lens's parts: the dedupe, the cross-unit rule, the strongest
// verdict, missing units; the matrix with units nobody reviews.
import std;
import Crafter.Review;
using namespace Crafter::Review;
namespace {
std::int32_t Failures = 0;
void Check(std::string_view name, bool cond, std::string_view detail = {}) {
std::println("{}{}{}", cond ? " ok " : " FAIL ", name, !cond && !detail.empty() ? std::format(" ({})", detail) : std::string());
if (!cond) ++Failures;
}
// {item: verdict} of a merged result.
std::map<std::string, std::string> Verdicts(const Json& merged) {
std::map<std::string, std::string> out;
for (const Json& c : merged.at("coverage")) out[c.at("item").get<std::string>()] = c.at("verdict").get<std::string>();
return out;
}
Json With(Json value, const Json& changes) {
value.update(changes);
return value;
}
}
int main() {
std::println("merge_parts: dedupe, cross-unit rule, strongest verdict, missing units; the matrix with not-reviewed units");
const std::string lens = "repo-bug-prover.md";
const Json note = {{"kind", "info"}, {"items", {"A", "B"}}, {"note", "shared"}};
const Json found = {{"finding", "x"}, {"items", {"A", "B"}}, {"discharged", true}, {"dischargedBy", "w1"}};
const std::vector<Json> parts = {
{{"partLabel", "a"}, {"expectedIds", {"A"}}, {"findings", {found}}, {"notes", {note, note}}, {"coverage", {{{"item", "A"}, {"verdict", "clear"}}}}},
{{"partLabel", "b"}, {"expectedIds", {"B"}}, {"findings", {found}}, {"notes", {note}}, {"coverage", {{{"item", "B"}, {"verdict", "clear"}}}}},
};
Json m = Normalize::MergeParts("L", lens, parts, {});
Check("one note and one finding after the merge, verdict unchanged", m.at("notes").size() == 1 && m.at("findings").size() == 1 && m.at("verdict") == "SEND-READY");
const Json pa = {{"partLabel", "L:block:A"}, {"partKind", "block"}, {"partUnit", "A"}, {"verdict", "FIX FIRST"},
{"findings", {{{"finding", "B contradicts A"}, {"evidence", "e"}, {"proposedAction", "p"}, {"items", {"A", "B"}}, {"discharged", false}}}},
{"notes", Json::array()}, {"coverage", {{{"item", "A"}, {"verdict", "defect"}, {"basis", "b"}}}}, {"coverageMissing", Json::array()}, {"undischarged", 1}};
const Json pb = {{"partLabel", "L:block:B"}, {"partKind", "block"}, {"partUnit", "B"}, {"verdict", "SEND-READY"}, {"findings", Json::array()}, {"notes", Json::array()},
{"coverage", {{{"item", "B"}, {"verdict", "clear"}, {"basis", "checked"}}}}, {"coverageMissing", Json::array()}};
m = Normalize::MergeParts("L", lens, std::vector<Json>{pa, pb}, {});
Json basisOfB;
for (const Json& c : m.at("coverage")) {
if (c.at("item") == "B") basisOfB = c;
}
const bool raised = std::ranges::any_of(m.at("normalizeLogs"), [](const Json& l) { return l.get<std::string>().contains("raised to defect"); });
Check("B raised to defect by the finding citing it; lens FIX FIRST", basisOfB.at("verdict") == "defect" && basisOfB.at("basis").get<std::string>().starts_with("cited by finding")
&& m.at("verdict") == "FIX FIRST" && raised);
Json dischargedFinding = pa.at("findings").at(0);
dischargedFinding["discharged"] = true;
dischargedFinding["dischargedBy"] = "w1";
const Json pc = With(pa, {{"findings", {dischargedFinding}}, {"coverage", {{{"item", "A"}, {"verdict", "clear"}, {"basis", "discharged"}}}}, {"verdict", "SEND-READY"}, {"undischarged", 0}});
Check("a discharged finding does not raise B", Verdicts(Normalize::MergeParts("L", lens, std::vector<Json>{pc, pb}, {}))["B"] == "clear");
const std::vector<std::string> missingA = {"A"};
const Json m3 = Normalize::MergeParts("L", lens, std::vector<Json>{pb}, missingA);
Check("missing unit -> coverageMissing + contract violation + verdict NOT RETURNED", m3.at("coverageMissing") == Json::array({"A"}) && !m3.at("contractViolations").empty()
&& m3.at("verdict") == "NOT RETURNED");
const Json pd = With(pb, {{"coverage", {{{"item", "B"}, {"verdict", "immaterial"}, {"basis", "w"}}}}});
Check("strongest verdict per unit wins (immaterial > clear)", Verdicts(Normalize::MergeParts("L", lens, std::vector<Json>{pb, pd}, {}))["B"] == "immaterial");
const Json result = Normalize::MergeParts("L", lens, std::vector<Json>{pa, pb}, {});
const std::vector<std::string> notReviewed = {"p1:msg"};
const Json mx = Normalize::Matrix(Json::array({{{"id", "A"}}, {{"id", "B"}}, {{"id", "C"}}, {{"id", "p1:msg"}}}), std::vector<Json>{result}, notReviewed);
std::set<std::string> defects;
for (auto it = mx.at("defects").begin(); it != mx.at("defects").end(); ++it) defects.insert(it.key());
Check("matrix: a unit without an entry is MISSING and a hole; a not-reviewed unit is listed, never a hole; defects per unit",
mx.at("matrix").at("C").at("L").at("verdict") == "MISSING" && mx.at("holes") == Json::array({"C"}) && mx.at("notReviewed") == Json::array({"p1:msg"})
&& defects == std::set<std::string>{"A", "B"}, DumpCompactSorted(mx.at("holes")));
return Failures ? 1 : 0;
}

70
tests/Normalize/main.cpp Normal file
View file

@ -0,0 +1,70 @@
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
// Normalising a pass: the synthesized finding, prefix resolution, discharge,
// contests, the vocabulary.
import std;
import Crafter.Review;
using namespace Crafter::Review;
namespace {
std::int32_t Failures = 0;
void Check(std::string_view name, bool cond, std::string_view detail = {}) {
std::println("{}{}{}", cond ? " ok " : " FAIL ", name, !cond && !detail.empty() ? std::format(" ({})", detail) : std::string());
if (!cond) ++Failures;
}
bool AnyKind(const Json& violations, std::string_view part) {
return std::ranges::any_of(violations, [&](const Json& v) { return v.at("kind").get<std::string>().contains(part); });
}
}
int main() {
std::println("normalize: synthesized finding, prefix resolution, discharge, contests, vocabulary");
const Json inv = Json::array({{{"id", "p1:x.py#1"}}, {{"id", "p1:src/components/a.tsx#1"}}, {{"id", "p1:src/components/a.tsx#2"}}});
std::vector<std::string> ids;
for (const Json& u : inv) ids.push_back(u.at("id").get<std::string>());
const std::string lens = "repo-bug-prover.md";
const Json first = Json::array({inv[0]});
auto run = [&](Json v, const std::vector<Json>& inForce, std::string_view lensFile = "repo-bug-prover.md") {
return Normalize::NormalizeOutput(v, inForce, lensFile, "h", first, ids);
};
const Json n = run({{"verdict", "SEND-READY"}, {"findings", Json::array()}, {"notes", Json::array()}, {"coverage", {{{"item", "p1:x.py#1"}, {"verdict", "defect"}, {"basis", "x"}}}}}, {});
Check("defect without finding -> synthesized undischarged finding, FIX FIRST", n.at("verdict") == "FIX FIRST" && !n.at("findings").empty()
&& Dyn::Truthy(Dyn::Get(n.at("findings").at(0), "synthesized")) && n.at("undischarged") == 1);
Check("declared SEND-READY over it -> contract violation", AnyKind(n.at("contractViolations"), "failed closed"));
Check("prefix: a unique prefix resolves, an ambiguous one stays", Normalize::ResolveId("p1:x", ids) == ids[0]
&& Normalize::ResolveId("p1:src/components/a.tsx#", ids) == "p1:src/components/a.tsx#");
const Json n2 = run({{"verdict", "SEND-READY"}, {"findings", Json::array()}, {"notes", Json::array()}, {"coverage", {{{"item", "p1:x"}, {"verdict", "clear"}, {"basis", "b"}}}}}, {});
Check("abbreviated coverage id resolved -> no MISSING", n2.at("coverageMissing").empty() && n2.at("coverage").at(0).at("item") == ids[0]);
const Json n3 = run({{"verdict", "SEND-READY"}, {"findings", Json::array()}, {"notes", Json::array()}, {"coverage", Json::array()}}, {});
Check("no coverage entry -> coverageMissing (NOT RETURNED)", n3.at("coverageMissing") == Json::array({ids[0]}) && !n3.at("contractViolations").empty());
const std::vector<Json> d = {{{"id", "w1"}, {"kind", "waiver-owner"}, {"hash", "h"}}};
auto finding = [&](const Json& extra) {
Json f = {{"finding", "f"}, {"evidence", "e"}, {"proposedAction", "p"}, {"items", {ids[0]}}};
f.update(extra);
return f;
};
const Json n4 = run({{"verdict", "SEND-READY"}, {"findings", {finding({{"dischargedBy", "w1"}})}}, {"notes", Json::array()},
{"coverage", {{{"item", ids[0]}, {"verdict", "clear"}, {"basis", "discharged by w1"}}}}}, d);
Check("dischargedBy an in-force id -> discharged, SEND-READY", n4.at("verdict") == "SEND-READY" && n4.at("findings").at(0).at("discharged") == true);
const Json n5 = run({{"verdict", "SEND-READY"}, {"findings", {finding({{"dischargedBy", "nope"}})}}, {"notes", Json::array()},
{"coverage", {{{"item", ids[0]}, {"verdict", "clear"}, {"basis", "b"}}}}}, d);
Check("dischargedBy an unknown id -> undischarged, FIX FIRST", n5.at("verdict") == "FIX FIRST" && n5.at("findings").at(0).at("discharged") == false);
const Json n6 = run({{"verdict", "FIX FIRST"}, {"findings", {finding({{"contests", {{"id", "w1"}, {"why", "w"}}}})}}, {"notes", Json::array()},
{"coverage", {{{"item", ids[0]}, {"verdict", "defect"}, {"basis", "b"}}}}}, d);
Check("contest of an unsettled entry blocks", n6.at("verdict") == "FIX FIRST" && !n6.at("contests").empty() && n6.at("contests").at(0).at("outcome").get<std::string>().contains("CONTESTED"));
const std::vector<Json> d2 = {{{"id", "w1"}, {"kind", "waiver-owner"}, {"hash", "h"}, {"reaffirmed", {{{"hash", "h"}}}}}};
const Json n7 = run({{"verdict", "FIX FIRST"}, {"findings", {finding({{"contests", {{"id", "w1"}, {"why", "w"}}}})}}, {"notes", Json::array()},
{"coverage", {{{"item", ids[0]}, {"verdict", "clear"}, {"basis", "b"}}}}}, d2);
Check("contest of a reaffirmed entry is recorded, finding discharged (silent veto logged)", n7.at("verdict") == "SEND-READY" && n7.at("findings").at(0).at("discharged") == true
&& AnyKind(n7.at("contractViolations"), "silent veto"));
const Json n8 = run({{"verdict", "SEND-READY"}, {"findings", Json::array()},
{"notes", {{{"kind", "unverified"}, {"note", "x"}, {"evidence", "e"}, {"action", "a"}, {"items", {ids[0]}}}}},
{"coverage", {{{"item", ids[0]}, {"verdict", "n/a"}, {"basis", "unverified: login failed"}}}}}, {}, Plan::FunctionalLens);
Check("functional: n/a with an unverified note is a pass of the contract (not clear, not a hole in the pass)", n8.at("verdict") == "SEND-READY" && n8.at("coverageMissing").empty()
&& std::ranges::contains(Schema::NoteKinds, std::string_view("unverified")) && !std::ranges::contains(Schema::NoteKinds, std::string_view("disposition-review")));
return Failures ? 1 : 0;
}

97
tests/Stream/main.cpp Normal file
View file

@ -0,0 +1,97 @@
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
// The claude stream: its envelopes.
import std;
import Crafter.Review;
namespace fs = std::filesystem;
using namespace Crafter::Review;
namespace {
std::int32_t Failures = 0;
void Check(std::string_view name, bool cond, std::string_view detail = {}) {
std::println("{}{}{}", cond ? " ok " : " FAIL ", name, !cond && !detail.empty() ? std::format(" ({})", detail) : std::string());
if (!cond) ++Failures;
}
// The result line mutated, or dropped when the mutation returns nullopt.
std::string Variant(const std::string& text, const std::function<std::optional<Json>(Json)>& mutate) {
std::vector<std::string> out;
for (const std::string& line : Dyn::SplitLines(text)) {
if (line.starts_with('{') && Dyn::Replace(line, " ", "").contains("\"type\":\"result\"")) {
const std::optional<Json> changed = mutate(Json::parse(line));
if (!changed) continue;
out.push_back(DumpSpaced(*changed));
} else {
out.push_back(line);
}
}
return Dyn::Join(out, "\n");
}
std::function<std::optional<Json>(Json)> Failed(const Json& update) {
return [update](Json o) -> std::optional<Json> {
o.update(update);
o.erase("structured_output");
return o;
};
}
std::string Status(const std::string& text, std::optional<std::int32_t> code, std::string_view err = {}) {
return Stream::Classify(Stream::ParseStream(text), code, err).first;
}
}
int main() {
std::println("stream envelopes");
const std::string text = Dyn::ReadText(fs::current_path() / "tests" / "Goldens" / "fixture" / "stream-success.jsonl");
const Json p = Stream::ParseStream(text);
Check("success: classify ok", Stream::Classify(p, 0, "") == std::pair<std::string, std::string>{"ok", ""});
const std::optional<Json> so = Stream::StructuredOutput(p.at("result"));
Check("success: structured_output parsed", so && so->at("verdict") == "SEND-READY" && so->at("coverage").at(0).at("item") == "R1");
Check("success: init parsed", Dyn::Truthy(p.at("init")) && Dyn::Get(p.at("init"), "mcp_servers") == Json::array());
Check("success: usage from the result line + firstTurn", p.at("turns") == 2 && p.at("firstTurn").at("cache_creation_input_tokens").get<std::int64_t>() > 0
&& p.at("usage").at("output").get<std::int64_t>() > 0 && !p.at("costUsd").is_null() && p.at("denials") == Json::array());
const Json w = Stream::Windows(p.at("rateLimit"));
Check("success: rate windows as percent", w.contains("five_hour") && w.at("five_hour").at("utilization").get<double>() > 0 && w.at("five_hour").at("utilization").get<double>() < 100
&& Dyn::Truthy(w.at("seven_day").at("resetsAt")), DumpCompactSorted(w));
Check("error_max_turns -> error", Status(Variant(text, Failed({{"subtype", "error_max_turns"}, {"is_error", true}, {"result", "Reached max turns (1)"}})), 1) == "error");
Check("429 -> limit", Status(Variant(text, Failed({{"subtype", "error_during_execution"}, {"is_error", true}, {"api_error_status", 429}, {"result", "API Error: 429 rate_limit_error"}})), 1) == "limit");
Check("401 -> auth", Status(Variant(text, Failed({{"subtype", "error_during_execution"}, {"is_error", true}, {"api_error_status", 401}, {"result", "API Error: 401 authentication_error"}})), 1) == "auth");
const std::string noResult = Variant(text, [](Json) -> std::optional<Json> { return std::nullopt; });
Check("403 in stderr, no result -> auth", Status(noResult, 1, "Error: 403 Forbidden") == "auth");
Check("no result line -> error", Status(noResult, 1) == "error");
Check("no result line + usage limit text -> limit", Status(noResult, 1, "You're out of extra usage") == "limit");
Check("timeout flag / exit 124 -> timeout", Stream::Classify(p, std::nullopt, "", true).first == "timeout" && Stream::Classify(p, 124, "").first == "timeout");
const std::string doubled = Variant(text, [](Json o) -> std::optional<Json> {
o["structured_output"] = DumpSpaced(o.at("structured_output"));
return o;
});
Check("double-encoded structured_output decoded", Stream::StructuredOutput(Stream::ParseStream(doubled).at("result")) == so);
const std::string inResult = Variant(text, [](Json o) -> std::optional<Json> {
const Json inner = o.at("structured_output");
o.erase("structured_output");
o["result"] = DumpSpaced(inner);
return o;
});
Check("structured output inside result text decoded", Stream::StructuredOutput(Stream::ParseStream(inResult).at("result")) == so);
const std::string noFindings = Variant(text, [](Json o) -> std::optional<Json> {
o["structured_output"] = {{"verdict", "SEND-READY"}};
return o;
});
Check("missing findings array -> error (no structured output)", Status(noFindings, 0) == "error");
Check("credit balance too low -> credits", Status(Variant(text, Failed({{"subtype", "error_during_execution"}, {"is_error", true}, {"api_error_status", 400},
{"result", "API Error: 400 Your credit balance is too low to access the Anthropic API."}})), 1) == "credits");
// The helper's spelling is a SQL call wherever it stands, and a stream
// line is cut at \n only: a raw U+2028 in a JSON string stays inside its
// line.
Check("this binary's helper is a SQL call, alone, after a path, in a compound command; another verb is not",
Stream::IsSqlCall("/app/bin/crafter-review sql <<'SQL'\nselect 1;\nSQL") && Stream::IsSqlCall("cd . && crafter-review sql 'select 1'")
&& Stream::IsSqlCall("/x/bin/crafter.review-exe-v/crafter-review sql") && !Stream::IsSqlCall("/app/bin/crafter-review sqlx") && !Stream::IsSqlCall("/app/bin/crafter-review totp f.json"));
const std::string split = "{\"type\":\"result\",\"subtype\":\"success\",\"is_error\":false,\"result\":\"a\u2028b\",\"structured_output\":{\"verdict\":\"SEND-READY\",\"findings\":[],\"notes\":[],\"coverage\":[]}}";
Check("a result line with a raw U+2028 in a string is still the result line", Stream::Classify(Stream::ParseStream(split), 0, "").first == "ok");
return Failures ? 1 : 0;
}