Crafter.Review/tests/CommitsMode/main.cpp

344 lines
30 KiB
C++
Raw Permalink Normal View History

// 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]);
const std::string Facts(Plan::FactsLens);
Check("gates: 1 approach, 2 functional, 3 proof - the bug-prover and the security lens side by side (2026-10-22) - 4 commit truth (2026-10-31); the owner is Gate 5",
gateNames == std::vector<std::string>{"Gate 1", "Gate 2", "Gate 3", "Gate 4"} && 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"} && g[3] == std::pair<std::string, std::vector<std::string>>{"Gate 4 — Commit truth", {Facts}});
const std::vector<std::string> onlyFn = {Fn};
const std::vector<std::string> security = {"repo-security.md"};
std::set<std::string> lensSet;
feat(modes): the kernel modes - a patch series and a mailing-list reply Beside the push gates, crafter-review runs the pre-send review of a kernel contributor's workbench: --mode series over a format-patch export with its cover letter, --mode reply over a mail draft. The inventory partitions a cover letter, a plain mail and the camera review's shape; ten kernel lenses join the table with their gates (structure, code, artifact truth) and the persona gate - one maintainer-persona instance per --reviewers entry, its whole pass gating its blocks and pruning the units that are not theirs. A series' code units are keyed on the code like the push gates'; every text unit on its text, the references it cites and the anchor when it is dated. The references an artifact cites are prefetched through utilities/lore.py into the gate-state, with a digest in the run layer, and a persona's calibration map is its lore listing. The kernel schema carries the persona vocabulary, the kernel note kinds, the predicted review and the not-mine list; the matrix the persona holes, the thin and the modelled units; gates clean without a persona pass is GATES CLEAN. A reply's gate-state sits beside the draft and is read once; a kernel mode needs --subsystem, defaults the effort to max and the trees to work/linux,work/milos-linux, and excuses no unit from the holes. The kernel lenses and the common text ship verbatim; the table lists them after the push gates' lenses and the config template holds them off, so a repository that never turns them on changes nothing. Prompts, keys and plans were checked byte for byte against the pipeline these modes replace, on its own artifacts: two replies and a series, every system prompt, user prompt, job and reuse key identical.
2026-10-31 22:56:12 +01:00
for (const Plan::Lens& lens : Plan::Lenses) {
// The push gates' lenses; the kernel modes' are another mode's.
if (!lens.kernel) lensSet.emplace(lens.file);
}
using GateList = std::vector<std::pair<std::string, std::vector<std::string>>>;
Check("mode_lenses = the five; --only keeps, --without drops, an emptied gate disappears; Gate 3 with one of its lenses dropped keeps the other",
feat(config): modes - a mode is an artifact kind, the lenses it runs and its effort The config defines the modes (modes.<name>): the kind of artifact the mode reviews - reply, series or commits, the three shipped partitions; a kind is a mode of its own name - the lenses it runs, in its kind's gate order, and the effort it runs at, else the kind's default (max for a reply or a series, xhigh for a commit range). `run --mode` names any of them, the dump records mode and kind, and everything that decided by the mode's name - the partition and hash, the prompts, the schemas, the disposition and reuse-key rules, what the CLI requires - decides by the kind. Every lens stays off until a mode lists it; lenses.<name>.run is retired with a warning that says so, and a lens of another kind, a name that is no lens, a lens listed twice and an unknown effort are config problems. The defaults run no lens; the template ships the three kinds as modes with every lens of their kind at the kind's effort, to trim (the owner's ruling of 2026-11-01), and init says so as it writes it - a kept config is the repository's own, and only one that lists no lens gets a step. The readers of the retired key follow: the CI smoke config, the shipped skill, the procedure (its description of the config as well as its mechanics), the manifest template, the run's messages and the comments that named the three modes. The run lock, the run numbers and the dump names are the kind's, so one full run per hash holds across the modes of a kind and their rounds count on together; the dump says which mode ran. The database broker starts for a lens the mode runs, as every other reader of the lenses off reads them. A named mode runs through the CLI in the kernel-modes test.
2026-11-01 01:31:40 +01:00
Set(Plan::KindLenses()) == lensSet && Gates(Plan::GatesFor(onlyFn)) == GateList{{"Gate 2 — Functional", {Fn}}}
&& Titles(Plan::GatesFor({}, onlyFn)) == std::vector<std::string>{"Gate 1 — Approach", "Gate 3 — Proof", "Gate 4 — Commit truth"} && Plan::GatesFor(onlyFn, onlyFn).empty()
&& Gates(Plan::GatesFor({}, security))[2] == 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"};
const std::vector<std::string> onlyFacts = {Facts};
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", "Gate 4 — Commit truth"}
&& Plan::GatesFor(onlyFn, {}, onlyFn).empty() && Gates(Plan::GatesFor({}, {}, threeOff)) == GateList{{"Gate 2 — Functional", {Fn}}, {"Gate 4 — Commit truth", {Facts}}}
&& Gates(Plan::GatesFor({}, {}, bugOff))[2] == std::pair<std::string, std::vector<std::string>>{"Gate 3 — Proof", {"repo-security.md"}}
&& Gates(Plan::GatesFor(onlyFacts)) == GateList{{"Gate 4 — Commit truth", {Facts}}} && Titles(Plan::GatesFor({}, {}, onlyFacts)).size() == 3);
const std::vector<std::string> allLenses(lensSet.begin(), lensSet.end());
const std::set<std::string> textAndDoc = {"p1:subject", "p1:diffstat", "p2:subject", "p2:msg", "p2:diffstat", "p2:README.md#hdr", "p2:README.md#1"};
feat(config): modes - a mode is an artifact kind, the lenses it runs and its effort The config defines the modes (modes.<name>): the kind of artifact the mode reviews - reply, series or commits, the three shipped partitions; a kind is a mode of its own name - the lenses it runs, in its kind's gate order, and the effort it runs at, else the kind's default (max for a reply or a series, xhigh for a commit range). `run --mode` names any of them, the dump records mode and kind, and everything that decided by the mode's name - the partition and hash, the prompts, the schemas, the disposition and reuse-key rules, what the CLI requires - decides by the kind. Every lens stays off until a mode lists it; lenses.<name>.run is retired with a warning that says so, and a lens of another kind, a name that is no lens, a lens listed twice and an unknown effort are config problems. The defaults run no lens; the template ships the three kinds as modes with every lens of their kind at the kind's effort, to trim (the owner's ruling of 2026-11-01), and init says so as it writes it - a kept config is the repository's own, and only one that lists no lens gets a step. The readers of the retired key follow: the CI smoke config, the shipped skill, the procedure (its description of the config as well as its mechanics), the manifest template, the run's messages and the comments that named the three modes. The run lock, the run numbers and the dump names are the kind's, so one full run per hash holds across the modes of a kind and their rounds count on together; the dump says which mode ran. The database broker starts for a lens the mode runs, as every other reader of the lenses off reads them. A named mode runs through the CLI in the kernel-modes test.
2026-11-01 01:31:40 +01:00
const std::set<std::string> nrAll = Set(Plan::NotReviewed(units, lines, Plan::KindLenses()));
std::vector<std::string> withoutFacts;
for (const Plan::Lens& lens : Plan::Lenses) {
feat(modes): the kernel modes - a patch series and a mailing-list reply Beside the push gates, crafter-review runs the pre-send review of a kernel contributor's workbench: --mode series over a format-patch export with its cover letter, --mode reply over a mail draft. The inventory partitions a cover letter, a plain mail and the camera review's shape; ten kernel lenses join the table with their gates (structure, code, artifact truth) and the persona gate - one maintainer-persona instance per --reviewers entry, its whole pass gating its blocks and pruning the units that are not theirs. A series' code units are keyed on the code like the push gates'; every text unit on its text, the references it cites and the anchor when it is dated. The references an artifact cites are prefetched through utilities/lore.py into the gate-state, with a digest in the run layer, and a persona's calibration map is its lore listing. The kernel schema carries the persona vocabulary, the kernel note kinds, the predicted review and the not-mine list; the matrix the persona holes, the thin and the modelled units; gates clean without a persona pass is GATES CLEAN. A reply's gate-state sits beside the draft and is read once; a kernel mode needs --subsystem, defaults the effort to max and the trees to work/linux,work/milos-linux, and excuses no unit from the holes. The kernel lenses and the common text ship verbatim; the table lists them after the push gates' lenses and the config template holds them off, so a repository that never turns them on changes nothing. Prompts, keys and plans were checked byte for byte against the pipeline these modes replace, on its own artifacts: two replies and a series, every system prompt, user prompt, job and reuse key identical.
2026-10-31 22:56:12 +01:00
if (lens.kernel) continue;
if (lens.file != Facts) withoutFacts.emplace_back(lens.file);
}
Check("not reviewed (every lens runs): only the documentation file's header, a mechanical n/a of the commit-truth lens; without that lens the text units and the documentation units",
nrAll == std::set<std::string>{"p2:README.md#hdr"} && Set(Plan::NotReviewed(units, lines, withoutFacts)) == textAndDoc, Show(nrAll));
std::vector<std::string> statics;
for (const Plan::Lens& lens : Plan::Lenses) {
feat(modes): the kernel modes - a patch series and a mailing-list reply Beside the push gates, crafter-review runs the pre-send review of a kernel contributor's workbench: --mode series over a format-patch export with its cover letter, --mode reply over a mail draft. The inventory partitions a cover letter, a plain mail and the camera review's shape; ten kernel lenses join the table with their gates (structure, code, artifact truth) and the persona gate - one maintainer-persona instance per --reviewers entry, its whole pass gating its blocks and pruning the units that are not theirs. A series' code units are keyed on the code like the push gates'; every text unit on its text, the references it cites and the anchor when it is dated. The references an artifact cites are prefetched through utilities/lore.py into the gate-state, with a digest in the run layer, and a persona's calibration map is its lore listing. The kernel schema carries the persona vocabulary, the kernel note kinds, the predicted review and the not-mine list; the matrix the persona holes, the thin and the modelled units; gates clean without a persona pass is GATES CLEAN. A reply's gate-state sits beside the draft and is read once; a kernel mode needs --subsystem, defaults the effort to max and the trees to work/linux,work/milos-linux, and excuses no unit from the holes. The kernel lenses and the common text ship verbatim; the table lists them after the push gates' lenses and the config template holds them off, so a repository that never turns them on changes nothing. Prompts, keys and plans were checked byte for byte against the pipeline these modes replace, on its own artifacts: two replies and a series, every system prompt, user prompt, job and reuse key identical.
2026-10-31 22:56:12 +01:00
if (lens.kernel) continue;
if (lens.file != Fn) statics.emplace_back(lens.file);
}
std::set<std::string> plusBehaviour = nrAll;
plusBehaviour.insert("whole:behaviour");
std::set<std::string> plusStatic = textAndDoc;
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, the file headers and the prose (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"); }));
// The commit-truth lens (2026-10-31): the prose of the range and its two
// whole slots, in passes of their own.
const Plan::LensPlan truth = Plan::PlanLens(Facts, units, lines);
std::map<std::string, std::string> truthKinds;
for (const Json& j : truth.blocks) truthKinds[j.at("unit").get<std::string>()] = j.at("kind").get<std::string>();
const std::map<std::string, std::string> wantTruth = {{"p1:subject", "block"}, {"p1:diffstat", "block"}, {"p2:subject", "block"}, {"p2:msg", "block"}, {"p2:diffstat", "block"},
{"p2:README.md#hdr", "auto-na"}, {"p2:README.md#1", "block"}, {"whole:order", "slot"}, {"whole:absent", "slot"}};
const Json& truthMsg = Job(truth.blocks, "block", "p2:msg");
const Json& truthDoc = Job(truth.blocks, "block", "p2:README.md#1");
const Json& truthOrder = Job(truth.blocks, "slot", "whole:order");
const Plan::Lens& t = Plan::LensOf(Facts);
Check("repo-facts: no whole pass; the text units, the documentation units and its two whole slots (whole:behaviour is the functional lens's); a doc file header is a mechanical n/a with its own basis; "
"text and doc blocks 60/40, a slot 50/35, one read, opus, Gate 4, none by default",
truth.whole.is_null() && truthKinds == wantTruth && Job(truth.blocks, "auto-na", "p2:README.md#hdr").at("basis") == "mechanical: diff header of a documentation file"
&& truthMsg.at("unitKind") == "text" && truthMsg.at("maxTurns") == 60 && truthMsg.at("timeoutMin") == 40 && truthDoc.at("unitKind") == "doc" && truthDoc.at("maxTurns") == 60
&& truthOrder.at("unitKind") == "slot" && truthOrder.at("maxTurns") == 50 && truthOrder.at("timeoutMin") == 35 && truthOrder.at("label") == "repo-facts:slot:whole:order"
&& truthOrder.at("expectedIds") == Json::array({"whole:order"}) && !t.reads && t.gate == 4 && t.model == "opus" && t.env == "none" && !t.empirical && t.whole.empty()
&& std::ranges::all_of(truth.blocks, [](const Json& j) { return j.at("kind") == "auto-na" || j.at("model") == Plan::ModelFor("opus"); }),
Dyn::ReprValue(Json(truthKinds)));
const Plan::Budget textElsewhere = Plan::BudgetFor("block", "text", "repo-bug-prover.md");
const Plan::Budget docBudget = Plan::BudgetFor("block", "doc", Facts);
Check("budgets: a text block of another lens 50/30, a doc block 60/40, a slot 50/35; an unknown unit kind is still a KeyError", textElsewhere.maxTurns == 50 && textElsewhere.timeoutMin == 30
&& docBudget.maxTurns == 60 && docBudget.timeoutMin == 40 && Plan::BudgetFor("slot", "slot", Facts).maxTurns == 50 && [] {
try {
Plan::BudgetFor("block", "nope", "repo-bug-prover.md");
return false;
} catch (const Dyn::Error&) {
return true;
}
}());
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"));
const std::string hunkTail = Prompt::Tail(Job(prover.blocks, "block", "p2:hello.py#1"), units, lines);
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") && !hunkTail.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 msgTail = Prompt::Tail(truthMsg, units, lines);
const std::string docTail = Prompt::Tail(truthDoc, units, lines);
const std::string orderTail = Prompt::Tail(truthOrder, units, lines);
const std::string absentTail = Prompt::Tail(Job(truth.blocks, "slot", "whole:absent"), units, lines);
Check("repo-facts tails: a message block judges a claim at its source with the text hint, a doc block with the documentation hint, each slot pass the slot's question with this lens's duty; "
"a code block's tail stays a hunk against the standard, and a slot of another lens would carry no duty",
msgTail.contains("a claim at its source, a sentence against its proof obligation") && msgTail.contains("verify every claim against this commit's diff") && !msgTail.contains("a hunk against the standard")
&& docTail.contains("a claim at its source") && docTail.contains("This unit is documentation the commit adds or changes") && docTail.contains("UNIT TEXT (verbatim")
&& orderTail.starts_with("SCOPE - SLOT PASS, unit \"whole:order\"") && orderTail.contains("Answer it for your lens; for this lens that means each message describes its own commit alone")
&& absentTail.contains("documentation the range leaves stale") && absentTail.ends_with(Prompt::GenericVocab)
&& hunkTail.contains("a hunk against the standard") && !hunkTail.contains("a claim at its source") && Prompt::SlotDuty("repo-bug-prover.md", "whole:order").empty()
&& Prompt::SlotDuty(Facts, "whole:behaviour").empty());
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"));
feat(config): lens sections - a repository's own text appended to a shipped charter A lens has one charter, the one this pipeline ships; what a repository knows that is not general to the lens - its platform and layers, the record it keeps its decisions in, what its users already have, the calibration entries that are its own - goes in a file the config names for the lens (lenses.<name>.section), and Charter appends it after one blank line. The charter sha in every reuse key covers the combined text. The config refuses a path outside the repository - absolute, or climbing out with `..` - by the check slots.dir had, now one both settings share; a file missing or one the run could not use (a dangling link, unreadable, not UTF-8) is a problem too, checked through the reader Charter uses (Prompt::ReadSection), so a section is never dropped in silence, and a name that is no lens is the unknown key it always was. Nothing lists a directory, so what lies beside the files is no concern of the config's: a directory listed would have to tolerate or refuse every foreign entry. The run logs which charters got one. The better-solution charter gains a consumer's general entry (2026-10-31: a mechanism reworked a second time in one submission is the signal to derive the fix at every layer the data crosses). The procedure names the section wherever it names the charter: among the config's keys, in the lens inputs, in the reuse rule (an edit to either re-runs the lens) and in the feedback loop, which routes a repository's own lesson to its section and a general one to the shipped charter.
2026-11-01 01:30:22 +01:00
// A repository's own section (2026-10-31): its file for a lens is
// appended to the shipped charter after one blank line - the shipped
// text first and unchanged - so the charter's hash, in every reuse key,
// covers it; another lens's charter stays as shipped.
const fs::path sectionsDir = fs::temp_directory_path() / std::format("crafter-review-sections-{}", Platform::PublishedPid());
fs::remove_all(sectionsDir);
fs::create_directories(sectionsDir);
const std::string section = "## This repository's section\n\n- 2026-10-31: SECTION MARKER.\n";
std::ofstream(sectionsDir / "repo-security.md", std::ios::binary) << section;
Prompt::SetLensSections({{"repo-security.md", sectionsDir / "repo-security.md"}});
const std::string withSection = Prompt::Charter("repo-security.md");
const std::string untouched = Prompt::Charter("repo-facts.md");
Prompt::SetLensSections({});
Check("a repository's section is appended to the shipped charter after one blank line, the shipped text first and unchanged; a lens without a file stays as shipped; the hash differs",
withSection == std::format("{}\n\n{}", charter.substr(0, charter.find_last_not_of('\n') + 1), section) && untouched == Prompt::Charter("repo-facts.md") && Plan::Sha(withSection) != Plan::Sha(charter)
&& Prompt::Charter("repo-security.md") == charter);
fs::remove_all(sectionsDir);
return Failures ? 1 : 0;
}