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.
745 lines
72 KiB
C++
745 lines
72 KiB
C++
// SPDX-License-Identifier: AGPL-3.0-only
|
|
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
|
|
|
// The kernel modes (reply, series): the inventory of a review draft, a plain
|
|
// mail and a series with a cover letter; the gates, efforts, reads and
|
|
// budgets per mode; the plans (every slot, the bare headers, a persona
|
|
// instance); the text and series reuse keys; the dispositions' carry and
|
|
// block texts; the prompts (common text, run layer, tails, lens layer); the
|
|
// kernel schema; the persona vocabulary in normalize and the matrix; the
|
|
// references; the state's persona pass; the allowlist; and dry runs of both
|
|
// modes through this binary, the persona gate included.
|
|
|
|
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;
|
|
}
|
|
|
|
void Write(const fs::path& path, std::string_view text) {
|
|
fs::create_directories(path.parent_path());
|
|
std::ofstream(path, std::ios::binary) << text;
|
|
}
|
|
|
|
std::vector<std::string> Ids(const Json& units) {
|
|
std::vector<std::string> out;
|
|
for (const Json& u : units) out.push_back(Dyn::Str(u.at("id")));
|
|
return out;
|
|
}
|
|
|
|
std::string Show(const std::vector<std::string>& values) {
|
|
Json out = Json::array();
|
|
for (const std::string& v : values) out.push_back(v);
|
|
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));
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
std::vector<std::string> LensesOf(const Plan::Gate& gate) {
|
|
return {gate.lenses.begin(), gate.lenses.end()};
|
|
}
|
|
|
|
std::map<std::string, std::int64_t> Kinds(const std::vector<Json>& jobs) {
|
|
std::map<std::string, std::int64_t> out;
|
|
for (const Json& j : jobs) ++out[Dyn::Str(j.at("kind"))];
|
|
return out;
|
|
}
|
|
|
|
std::set<std::string> Units(const std::vector<Json>& jobs, std::string_view kind = {}) {
|
|
std::set<std::string> out;
|
|
for (const Json& j : jobs) {
|
|
if (kind.empty() || j.at("kind") == kind) out.insert(Dyn::Str(j.at("unit")));
|
|
}
|
|
return out;
|
|
}
|
|
|
|
// claude for the dry runs' sibling full run: the units from its prompt,
|
|
// every unit clear; a persona's whole pass marks nothing not-mine.
|
|
constexpr std::string_view FakeClaude = R"sh(#!/bin/sh
|
|
set -f
|
|
[ "$1" = --version ] && { echo '2.1.314 (Claude Code)'; exit 0; }
|
|
model=
|
|
sid=
|
|
prev=
|
|
for a in "$@"; do
|
|
case $prev in
|
|
--model) model=$a ;;
|
|
--session-id) sid=$a ;;
|
|
esac
|
|
prev=$a
|
|
done
|
|
ids=$(sed -n -e 's/^SCOPE - BLOCK PASS, unit "\([^"]*\)".*/\1/p' -e 's/^SCOPE - SLOT PASS, unit "\([^"]*\)".*/\1/p' -e 's/^- \(whole:[a-z-]*\) \[.*/\1/p')
|
|
echo "$$" $ids >> "$FAKE_DIR/calls"
|
|
echo "{\"type\":\"system\",\"subtype\":\"init\",\"session_id\":\"$sid\",\"model\":\"$model\",\"mcp_servers\":[],\"tools\":[\"Read\"]}"
|
|
echo '{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"reading"}]}}'
|
|
coverage=
|
|
summary=
|
|
for id in $ids; do
|
|
coverage="$coverage${coverage:+,}{\"item\":\"$id\",\"verdict\":\"clear\",\"basis\":\"no record\"}"
|
|
case $id in whole:*) summary=',"summary":"the approach holds","predictedReview":"Looks fine.","notMine":[]' ;; esac
|
|
done
|
|
echo "{\"type\":\"result\",\"subtype\":\"success\",\"is_error\":false,\"result\":\"ok\",\"structured_output\":{\"verdict\":\"SEND-READY\",\"findings\":[],\"notes\":[],\"coverage\":[$coverage]$summary},\"usage\":{\"input_tokens\":1,\"output_tokens\":1,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0},\"total_cost_usd\":0.01,\"num_turns\":1}"
|
|
)sh";
|
|
|
|
std::map<std::string, std::string> Env;
|
|
|
|
std::map<std::string, std::string> With(std::map<std::string, std::string> env, const std::map<std::string, std::string>& changes) {
|
|
for (const auto& [k, v] : changes) env[k] = v;
|
|
return env;
|
|
}
|
|
|
|
// A reply's or series' reference index as a run leaves it, so a dry run
|
|
// fetches nothing: every entry failed.
|
|
void FailedIndex(const fs::path& gateDir, std::string_view h8, const Refs::Table& refs) {
|
|
Json index = Json::object();
|
|
for (const auto& [n, url] : refs) {
|
|
const std::optional<std::string> mid = Refs::MsgidOf(url);
|
|
index[n] = {{"url", url}, {"msgid", mid.value_or("")}, {"path", std::format("gate-state/refs/{}.raw", Refs::SafeName(mid.value_or("")))}, {"status", "failed"}, {"error", "no lore here"}};
|
|
}
|
|
Write(gateDir / std::format("refs-{}.json", h8), DumpFile(index));
|
|
}
|
|
|
|
std::string SystemFiles(const fs::path& dry) {
|
|
std::string all;
|
|
std::error_code error;
|
|
for (fs::directory_iterator it(dry, error), end; !error && it != end; it.increment(error)) {
|
|
const std::string name = it->path().filename().string();
|
|
if (name.starts_with("system-") && name.ends_with(".md")) all += Platform::ReadFile(it->path()).value_or("");
|
|
}
|
|
return all;
|
|
}
|
|
|
|
std::string Prompts(const fs::path& dry) {
|
|
std::string all;
|
|
std::error_code error;
|
|
for (fs::directory_iterator it(dry / "prompts", error), end; !error && it != end; it.increment(error)) {
|
|
if (it->path().extension() == ".txt") all += Platform::ReadFile(it->path()).value_or("");
|
|
}
|
|
return all;
|
|
}
|
|
|
|
std::int32_t Checks() {
|
|
std::println("kernel modes: the inventory, gates, plans, keys, dispositions, prompts, schema, normalize, refs, state, allowlist, dry runs");
|
|
Platform::SetMode(Platform::Mode::Native);
|
|
const fs::path checkout = fs::current_path();
|
|
SetHome(checkout);
|
|
const fs::path fix = checkout / "tests" / "KernelModes" / "fixture";
|
|
const fs::path base = fs::temp_directory_path() / std::format("crafter-review-kernel-{}", Platform::PublishedPid());
|
|
fs::remove_all(base);
|
|
fs::create_directories(base);
|
|
for (const auto& [k, v] : Platform::Environment()) {
|
|
const bool review = k.starts_with("REVIEW_GATES_") || k.starts_with("CRAFTER_REVIEW_") || k == "SEATS_ENV_FILE";
|
|
const bool seat = k.starts_with("CLAUDE_CODE_OAUTH_TOKEN") || k.starts_with("ANTHROPIC_API_KEY") || k.starts_with("OPENROUTER_KEY") || k.starts_with("FAKE_");
|
|
if (!review && !seat) Env[k] = v;
|
|
}
|
|
Env = With(Env, {{"GIT_CONFIG_GLOBAL", "/dev/null"}, {"GIT_CONFIG_SYSTEM", "/dev/null"}, {"CRAFTER_REVIEW_HOME", checkout.string()}, {"REVIEW_GATES_BOARD", (base / "board.json").string()}});
|
|
|
|
// ---- the inventory --------------------------------------------------------------
|
|
const Inventory::Built draft = Inventory::Build("reply", (fix / "draft-camera.txt").string());
|
|
const std::vector<std::string> draftIds = Ids(draft.out.at("units"));
|
|
Check("reply, the camera review's shape: the author's groups, the anchor, the reversals, the sections' items and sub-items, the bullets, the refs, the three slots",
|
|
draftIds == std::vector<std::string>{"nl:intro", "nl:ov13b10-of/0001-media-i2c-ov13b10.patch", "nl:ov13b10-of/0000-cover-letter.patch", "anchor", "reversals:header", "R1", "R2",
|
|
"camss:header", "camss:1", "camss:1a", "camss:1b", "camss:2", "ov13b10:header", "ov13b10:1", "x:header", "x:1", "x:2", "refs", "whole:route",
|
|
"whole:asks", "whole:absent"}
|
|
&& Dyn::Str(draft.out.at("hash8")) == Artifact::Sha256File(fix / "draft-camera.txt").substr(0, 8) && Dyn::Str(draft.out.at("mode")) == "reply",
|
|
Show(draftIds));
|
|
const Json& draftUnits = draft.out.at("units");
|
|
Check("...a reversal holds its continuation line, a sub-item is one line, the refs run to the end",
|
|
Plan::UnitText(draft.lines, Unit(draftUnits, "R1")).starts_with("R1. The active-array origin") && Plan::UnitText(draft.lines, Unit(draftUnits, "R1")).contains("withdrawn.")
|
|
&& Dyn::Str(Unit(draftUnits, "camss:1a").at("excerpt")) == "a. The error path leaves the clock enabled." && Plan::UnitText(draft.lines, Unit(draftUnits, "refs")).ends_with("example.org/"),
|
|
Plan::UnitText(draft.lines, Unit(draftUnits, "R1")));
|
|
const Inventory::Built mail = Inventory::Build("reply", (fix / "reply.eml").string());
|
|
const std::vector<std::string> mailIds = Ids(mail.out.at("units"));
|
|
Check("reply, a plain mail: the header block, each quoted block with its attribution, our paragraphs, the trailer paragraph, the slots",
|
|
mailIds == std::vector<std::string>{"hdr", "quote:1", "para:1", "quote:2", "para:2", "para:3", "trailers", "whole:route", "whole:asks", "whole:absent"}
|
|
&& Unit(mail.out.at("units"), "quote:1").at("lines") == Json({8, 11}) && Unit(mail.out.at("units"), "trailers").at("lines") == Json({21, 22})
|
|
&& Unit(mail.out.at("units"), "hdr").at("lines") == Json({1, 6}),
|
|
Show(mailIds));
|
|
const Inventory::Built series = Inventory::Build("series", (fix / "series").string());
|
|
const std::vector<std::string> seriesIds = Ids(series.out.at("units"));
|
|
std::vector<std::string> coverIds;
|
|
for (const std::string& id : seriesIds) {
|
|
if (id.starts_with("cover:")) coverIds.push_back(id);
|
|
}
|
|
Check("series: the cover letter's header and its six paragraphs (the base-commit line and the signature are one), every patch's units, a .md hunk whole, the four series slots",
|
|
coverIds == std::vector<std::string>{"cover:header", "cover:1", "cover:2", "cover:3", "cover:4", "cover:5", "cover:6"}
|
|
&& std::ranges::contains(seriesIds, std::string("p1:drivers/nfc/widget.c#1")) && std::ranges::contains(seriesIds, std::string("p2:README.md#1"))
|
|
&& !std::ranges::contains(seriesIds, std::string("p2:README.md#1.1")) && std::ranges::contains(seriesIds, std::string("p1:trailers"))
|
|
&& seriesIds.back() == "whole:absent" && std::ranges::contains(seriesIds, std::string("whole:cover-vs-patches")) && std::ranges::contains(seriesIds, std::string("whole:recipients"))
|
|
&& Unit(series.out.at("units"), "cover:header").at("lines") == Json({1, 5}),
|
|
Show(seriesIds));
|
|
Check("series: a .md hunk is code to the kernel lenses and doc to the push gates' commit-truth lens",
|
|
Plan::KindFor("bug-prover.md", Unit(series.out.at("units"), "p2:README.md#1")) == "code" && Plan::KindFor("facts.md", Unit(series.out.at("units"), "p2:README.md#1")) == "code"
|
|
&& Plan::KindFor(Plan::FactsLens, Unit(series.out.at("units"), "p2:README.md#1")) == "doc");
|
|
|
|
// ---- gates, effort, reads, budgets ---------------------------------------------
|
|
Check("gates: reply runs Gate 3 alone; series Gates 1-3; --only and --without filter within the mode; a foreign lens filters to nothing",
|
|
Titles(Plan::Gates("reply")) == std::vector<std::string>{"Gate 3 — Artifact truth"} && LensesOf(Plan::Gates("reply")[0]) == std::vector<std::string>{"facts.md", "etiquette.md", "consistency.md", "conciseness.md"}
|
|
&& Titles(Plan::Gates("series")) == std::vector<std::string>{"Gate 1 — Structure", "Gate 2 — Code", "Gate 3 — Artifact truth"}
|
|
&& LensesOf(Plan::GatesFor("reply", std::vector<std::string>{"facts.md"}, {}, {})[0]) == std::vector<std::string>{"facts.md"}
|
|
&& Titles(Plan::GatesFor("series", std::vector<std::string>{"bug-prover.md"}, {}, {}))[0].starts_with("Gate 2")
|
|
&& LensesOf(Plan::GatesFor("reply", {}, std::vector<std::string>{"conciseness.md"}, {})[0]) == std::vector<std::string>{"facts.md", "etiquette.md", "consistency.md"}
|
|
&& Plan::GatesFor("reply", std::vector<std::string>{"facts.md"}, std::vector<std::string>{"facts.md"}, {}).empty()
|
|
&& Plan::GatesFor("commits", std::vector<std::string>{"facts.md"}, {}, {}).empty() && Plan::GatesFor().size() == Plan::Gates("commits").size());
|
|
Check("mode lenses: the kernel modes have the persona lens, commits does not; effort max for the kernel modes, xhigh for commits; reads 1 for a reply, 3 otherwise",
|
|
std::ranges::contains(Plan::KindLenses("series"), std::string(Plan::PersonaLens)) && std::ranges::contains(Plan::KindLenses("reply"), std::string(Plan::PersonaLens))
|
|
&& !std::ranges::contains(Plan::KindLenses("commits"), std::string(Plan::PersonaLens)) && Plan::DefaultEffort("reply") == "max" && Plan::DefaultEffort("series") == "max"
|
|
&& Plan::DefaultEffort("commits") == "xhigh" && Plan::DefaultEffort() == "xhigh" && Plan::ReadsFor("reply") == 1 && Plan::ReadsFor("series") == 3 && Plan::ReadsFor("commits") == 3);
|
|
auto budget = [](std::string_view kind, std::string_view unitKind, std::string_view lens) {
|
|
const Plan::Budget b = Plan::BudgetFor(kind, unitKind, lens);
|
|
return std::pair(b.maxTurns, b.timeoutMin);
|
|
};
|
|
Check("budgets: facts text 80/50, other text 50/30, slot 50/35, whole 180/105",
|
|
budget("block", "text", "facts.md") == std::pair(80, 50) && budget("block", "text", "etiquette.md") == std::pair(50, 30) && budget("slot", "slot", "facts.md") == std::pair(50, 35)
|
|
&& budget("whole", "", "consistency.md") == std::pair(180, 105) && budget("block", "text", Plan::FactsLens) == std::pair(60, 40));
|
|
|
|
// ---- plans ----------------------------------------------------------------------
|
|
const Plan::LensPlan facts = Plan::PlanLens("facts.md", draftUnits, draft.lines);
|
|
const std::map<std::string, std::int64_t> factsKinds = Kinds(facts.blocks);
|
|
Check("facts on the draft: no whole pass; a block per unit - 3 slot passes, 3 bare headers mechanical n/a, the rest blocks",
|
|
facts.whole.is_null() && factsKinds.at("slot") == 3 && factsKinds.at("auto-na") == 3 && facts.blocks.size() == draftUnits.size() && Units(facts.blocks, "auto-na") == std::set<std::string>{"camss:header", "ov13b10:header", "x:header"},
|
|
DumpCompact(Json(factsKinds)));
|
|
const Plan::LensPlan consistency = Plan::PlanLens("consistency.md", draftUnits, draft.lines);
|
|
std::vector<std::string> wholeSlots;
|
|
for (const Json& id : consistency.whole.at("expectedIds")) wholeSlots.push_back(Dyn::Str(id));
|
|
Check("consistency: a whole pass over every whole:* slot, blocks that exclude the slots, no persona key on a plain job",
|
|
wholeSlots == std::vector<std::string>{"whole:route", "whole:asks", "whole:absent"} && !Units(consistency.blocks, "slot").size() && !Dyn::Has(consistency.whole, "persona"));
|
|
const Plan::LensPlan persona = Plan::PlanLens(Plan::PersonaLens, draftUnits, draft.lines, {}, {}, "Thomas Colliers", std::vector<std::string>{"R1"});
|
|
Check("persona: the instance is persona:<name>, its jobs carry the persona after the lens file, the whole pass on the fable tier, no block for an excluded unit",
|
|
Dyn::Str(persona.whole.at("lens")) == "persona:Thomas Colliers" && Dyn::Str(persona.whole.at("persona")) == "Thomas Colliers" && !Units(persona.blocks).contains("R1")
|
|
&& Units(persona.blocks).contains("R2") && Dyn::Str(persona.whole.at("model")) == Plan::ModelFor("fable") && Dyn::Str(persona.whole.at("label")) == "persona:Thomas Colliers:whole"
|
|
&& std::ranges::all_of(persona.blocks, [](const Json& j) { return j.at("persona") == "Thomas Colliers"; }),
|
|
DumpCompact(persona.whole));
|
|
Check("the same keys in the same order as a plain job, the persona after lensFile",
|
|
[&] {
|
|
std::vector<std::string> keys;
|
|
for (auto it = persona.whole.begin(); it != persona.whole.end(); ++it) keys.push_back(it.key());
|
|
return keys == std::vector<std::string>{"label", "lens", "lensFile", "persona", "kind", "unit", "expectedIds", "model", "maxTurns", "timeoutMin"};
|
|
}());
|
|
|
|
// ---- text reuse keys ------------------------------------------------------------
|
|
const Refs::Table refs = Refs::ParseRefs(Dyn::ReadText(fix / "draft-camera.txt"));
|
|
Check("refs: the draft's two numbered references", refs.size() == 2 && refs[0].first == "1" && refs[0].second.starts_with("https://lore.kernel.org/r/") && refs[1].first == "2");
|
|
const std::string r2 = Plan::UnitText(draft.lines, Unit(draftUnits, "R2"));
|
|
const std::string anchor = Plan::UnitText(draft.lines, Unit(draftUnits, "anchor"));
|
|
const Json job = {{"kind", "block"}, {"unit", "R2"}};
|
|
const std::vector<std::string> deps = Plan::TextDeps(r2, draftUnits, draft.lines, refs, anchor);
|
|
Check("text deps of R2: the [2] it cites, the anchor (a dated unit), the R1 it names - and itself, as a reversal names its own number",
|
|
deps.size() == 4 && deps[0].starts_with("ref[2]=https://lore.kernel.org/all/") && deps[1] == std::format("anchor={}", Plan::Sha(anchor)) && deps[2].starts_with("unit[R1]=")
|
|
&& deps[3] == std::format("unit[R2]={}", Plan::Sha(r2)),
|
|
Show(deps));
|
|
const std::optional<std::string> k1 = Plan::ReuseKey(job, "facts.md", r2, deps, {"w1"}, "cs", 1);
|
|
Refs::Table refs2 = refs;
|
|
refs2[1].second = "https://lore.kernel.org/all/changed@example/";
|
|
const std::string undated = "A sentence with no date, version, state word or citation at all.";
|
|
const Json jd = {{"kind", "block"}, {"unit", "R9"}};
|
|
Check("same unit, same deps -> same key; a cited URL, the anchor of a dated unit, or the dispositions in force change it; the anchor leaves an undated unit's key alone",
|
|
k1 == Plan::ReuseKey(job, "facts.md", r2, Plan::TextDeps(r2, draftUnits, draft.lines, refs, anchor), {"w1"}, "cs", 1)
|
|
&& k1 != Plan::ReuseKey(job, "facts.md", r2, Plan::TextDeps(r2, draftUnits, draft.lines, refs2, anchor), {"w1"}, "cs", 1)
|
|
&& k1 != Plan::ReuseKey(job, "facts.md", r2, Plan::TextDeps(r2, draftUnits, draft.lines, refs, std::format("{}x", anchor)), {"w1"}, "cs", 1)
|
|
&& Plan::ReuseKey(jd, "facts.md", undated, Plan::TextDeps(undated, draftUnits, draft.lines, refs, anchor), {"w1"}, "cs", 1)
|
|
== Plan::ReuseKey(jd, "facts.md", undated, Plan::TextDeps(undated, draftUnits, draft.lines, refs, std::format("{}x", anchor)), {"w1"}, "cs", 1)
|
|
&& k1 != Plan::ReuseKey(job, "facts.md", r2, deps, {"w1", "w2"}, "cs", 1));
|
|
std::vector<std::string> sortedDeps = deps;
|
|
std::ranges::sort(sortedDeps);
|
|
Check("the key is the formula it always was: lens | unit | its text | its deps | the dispositions | the charter | the prompt version | the model | effort slot max",
|
|
k1 == Plan::Sha(Dyn::Join(std::vector<std::string>{"facts.md", "R2", Plan::Sha(r2), Plan::Sha(Dyn::Join(sortedDeps, "\n")), "w1", "cs", "1", "", "max"}, "|")).substr(0, 16)
|
|
&& Plan::ReuseKeyEffortSlot == "max");
|
|
const Json px = {{"kind", "block"}, {"unit", "R2"}, {"persona", "X"}};
|
|
const Json py = {{"kind", "block"}, {"unit", "R2"}, {"persona", "Y"}};
|
|
const std::optional<std::string> kp = Plan::ReuseKey(px, Plan::PersonaLens, r2, {}, {}, "c", 1);
|
|
Check("a persona block has a key of its own per persona; a persona whole pass, a consistency block and a slot never get one",
|
|
kp && kp != Plan::ReuseKey(py, Plan::PersonaLens, r2, {}, {}, "c", 1) && kp == Plan::ReuseKey(px, Plan::PersonaLens, r2, {}, {}, "c", 1)
|
|
&& !Plan::ReuseKey({{"kind", "whole"}, {"unit", nullptr}, {"persona", "X"}}, Plan::PersonaLens, r2, {}, {}, "c", 1) && !Plan::ReuseKey(job, "consistency.md", r2, {}, {}, "c", 1)
|
|
&& !Plan::ReuseKey({{"kind", "slot"}, {"unit", "whole:route"}}, "facts.md", r2, {}, {}, "c", 1));
|
|
|
|
// ---- series code keys (the push gates' rule, on the commits fixture) ------------
|
|
const fs::path src = checkout / "tests" / "Goldens" / "fixture" / "commits";
|
|
const std::vector<std::string> names = Artifact::PatchFiles(src);
|
|
std::map<std::string, std::string> orig;
|
|
for (const std::string& f : names) orig[f] = Dyn::ReadText(src / f);
|
|
auto variant = [&](std::string_view name, const std::function<std::string(std::int64_t, const std::string&)>& edit, std::string_view prefix = {}) {
|
|
const fs::path d = base / name;
|
|
fs::create_directories(d);
|
|
for (std::size_t i = 0; i < names.size(); ++i) Write(d / std::format("{}{}", prefix, names[i]), edit(static_cast<std::int64_t>(i) + 1, orig[names[i]]));
|
|
return d;
|
|
};
|
|
auto skeys = [&](const fs::path& d, std::string_view lens = "bug-prover.md", std::string_view who = {}) {
|
|
const Inventory::Built inv = Inventory::Build("series", d.string());
|
|
const Plan::CommitCode cc = Plan::CommitCodeOf(Artifact::PatchTexts(d), true);
|
|
std::map<std::string, std::string> out;
|
|
for (const Json& u : inv.out.at("units")) {
|
|
if (Plan::KindFor(lens, u) != "code") continue;
|
|
Json j = {{"kind", "block"}, {"unit", u.at("id")}, {"unitKind", "code"}};
|
|
if (!who.empty()) j["persona"] = who;
|
|
out[Dyn::Str(u.at("id"))] = Plan::CodeReuseKey(j, lens, Plan::UnitText(inv.lines, u), cc, {}, "c", 1).value_or("");
|
|
}
|
|
return out;
|
|
};
|
|
const std::map<std::string, std::string> s0 = skeys(src);
|
|
auto changed = [&](const std::map<std::string, std::string>& k) {
|
|
std::set<std::string> out;
|
|
for (const auto& [id, key] : s0) {
|
|
if (k.at(id) != key) out.insert(id);
|
|
}
|
|
return out;
|
|
};
|
|
Check("series: every code unit of a code lens has a key - the README hunk too (documentation is code to the kernel lenses) and the two slots",
|
|
std::ranges::all_of(s0, [](const auto& e) { return !e.second.empty(); }) && s0.contains("p2:README.md#1") && s0.contains("p2:absent") && s0.contains("p1:outside"));
|
|
auto sub = [](std::string t, std::string_view a, std::string_view b) { return Dyn::Replace(t, a, b); };
|
|
const std::map<std::string, std::string> reword = skeys(variant("reword", [&](std::int64_t n, const std::string& t) {
|
|
return n == 2 ? sub(sub(sub(t, "The greeting takes a name now", "greet() takes a name now"), "From 2222", "From 3333"), "[PATCH 2/2]", "[PATCH v2 2/2]") : t;
|
|
}));
|
|
const std::set<std::string> rewordChanged = changed(reword);
|
|
Check("series: a reworded message, a new commit id and a re-roll counter -> no hunk and no `outside` asked again; that patch's `absent` is (it looks into the message)",
|
|
rewordChanged == std::set<std::string>{"p2:absent"}, Show({rewordChanged.begin(), rewordChanged.end()}));
|
|
Check("series: a re-roll that changes only the [PATCH vN n/m] counter and the commit ids asks nothing again",
|
|
skeys(variant("counter", [&](std::int64_t, const std::string& t) { return sub(sub(sub(t, "[PATCH 2/2]", "[PATCH v2 2/2]"), "[PATCH 1/2]", "[PATCH v2 1/2]"), "From 2222", "From 3333"); })) == s0);
|
|
const std::map<std::string, std::string> code = skeys(variant("code", [&](std::int64_t n, const std::string& t) { return n == 2 ? sub(t, "+ return \"hello \" + name", "+ return \"hi \" + name") : t; }));
|
|
const std::set<std::string> codeChanged = changed(code);
|
|
Check("series: one changed line -> its hunk and the four slots", codeChanged == std::set<std::string>{"p2:hello.py#1", "p1:outside", "p1:absent", "p2:outside", "p2:absent"},
|
|
Show({codeChanged.begin(), codeChanged.end()}));
|
|
Check("series: the three code lenses and each persona have keys of their own; better-solution's blocks too; consistency's never",
|
|
std::set<std::string>{skeys(src, "bug-prover.md").at("p2:hello.py#1"), skeys(src, "code-quality.md").at("p2:hello.py#1"), skeys(src, "minimalism.md").at("p2:hello.py#1"),
|
|
skeys(src, Plan::PersonaLens, "A B").at("p2:hello.py#1"), skeys(src, Plan::PersonaLens, "C D").at("p2:hello.py#1")}
|
|
.size() == 5
|
|
&& skeys(src, Plan::PersonaLens, "A B") == skeys(src, Plan::PersonaLens, "A B") && !skeys(src, "better-solution.md").at("p2:hello.py#1").empty()
|
|
&& skeys(src, "better-solution.md").at("p2:hello.py#1") != s0.at("p2:hello.py#1") && skeys(src, "consistency.md").at("p2:hello.py#1").empty());
|
|
const Plan::CommitCode cmt = Plan::CommitCodeOf(Artifact::PatchTexts(src));
|
|
const Plan::CommitCode ker = Plan::CommitCodeOf(Artifact::PatchTexts(src), true);
|
|
Check("series: nothing is documentation (a .md hunk is part of its patch's code); the message is kept for `absent`; the push gates' code moves not",
|
|
ker.docs.at(1).empty() && ker.docs.at(2).empty() && ker.code.at(2) != cmt.code.at(2) && ker.kernel && !cmt.kernel && ker.message.at(2) != ker.message.at(1) && !cmt.docs.at(2).empty());
|
|
const fs::path reroll = variant("reroll", [](std::int64_t, const std::string& t) { return t; }, "v3-");
|
|
Check("a re-rolled series (v3-0002-...) is numbered like 0002-...: the partition and the patch texts",
|
|
Artifact::PatchTexts(reroll).size() == 2 && Artifact::PatchTexts(reroll).contains(2) && Ids(Inventory::Build("series", reroll.string()).out.at("units")) == Ids(Inventory::Build("series", src.string()).out.at("units")));
|
|
const Inventory::Built sinv = Inventory::Build("series", src.string());
|
|
const std::string tm = Plan::UnitText(sinv.lines, Unit(sinv.out.at("units"), "p2:msg"));
|
|
const Json jm = {{"kind", "block"}, {"unit", "p2:msg"}, {"unitKind", "text"}};
|
|
std::vector<std::string> tdeps = Plan::TextDeps(tm, sinv.out.at("units"), sinv.lines, {}, "");
|
|
std::ranges::sort(tdeps);
|
|
Check("series: a text unit keeps the key it always had, and a persona reading it has its own",
|
|
Plan::ReuseKey(jm, "facts.md", tm, Plan::TextDeps(tm, sinv.out.at("units"), sinv.lines, {}, ""), {}, "c", 1)
|
|
== Plan::Sha(Dyn::Join(std::vector<std::string>{"facts.md", "p2:msg", Plan::Sha(tm), Plan::Sha(Dyn::Join(tdeps, "\n")), "", "c", "1", "", "max"}, "|")).substr(0, 16)
|
|
&& Plan::ReuseKey({{"kind", "block"}, {"unit", "p2:msg"}, {"unitKind", "text"}, {"persona", "A B"}}, Plan::PersonaLens, tm, Plan::TextDeps(tm, sinv.out.at("units"), sinv.lines, {}, ""), {}, "c", 1)
|
|
!= Plan::ReuseKey(jm, "facts.md", tm, Plan::TextDeps(tm, sinv.out.at("units"), sinv.lines, {}, ""), {}, "c", 1));
|
|
|
|
// ---- dispositions --------------------------------------------------------------
|
|
{
|
|
const fs::path td = base / "disp";
|
|
Write(td / "0001-a.patch", "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"}}};
|
|
auto statuses = [](const std::vector<Json>& rows) {
|
|
std::map<std::string, std::string> out;
|
|
for (const Json& r : rows) out[Dyn::Str(r.at("id"))] = Dyn::Str(r.at("status"));
|
|
return out;
|
|
};
|
|
const std::vector<Json> seriesRows = Dispositions::Classify(e, "H1", td, "series");
|
|
Check("series: hash match in-force, count match carried, count mismatch lapsed, no carry lapsed; a reply never carries",
|
|
statuses(seriesRows) == std::map<std::string, std::string>{{"a", "in-force"}, {"b", "carried"}, {"c", "lapsed"}, {"d", "lapsed"}}
|
|
&& statuses(Dispositions::Classify(e, "H1", td, "reply")) == std::map<std::string, std::string>{{"a", "in-force"}, {"b", "lapsed"}, {"c", "lapsed"}, {"d", "lapsed"}}
|
|
&& Dyn::Str(Dispositions::Classify(e, "H1", td, "reply")[1].at("detail")).contains("no carry in reply mode"));
|
|
const std::vector<Json> inf = Dispositions::InForce(e, seriesRows);
|
|
const std::string blk = Dispositions::Block(inf, seriesRows, "facts.md", "H1", "camera");
|
|
Check("a kernel lens's block: the ledger sentence, the carried entry, lore in the rules, the owner as Gate 5, no disposition-review duty",
|
|
blk.contains("journal/camera/ledger.md for your lens that is NOT listed here is LAPSED") && blk.contains("CARRIED to this hash") && blk.contains("- a [") && blk.contains("lore via utilities/lore.py")
|
|
&& blk.contains("Gate 5 must see it") && blk.contains("do NOT emit disposition-review notes"),
|
|
blk);
|
|
const std::string repoBlk = Dispositions::Block(inf, seriesRows, Plan::FactsLens, "H1");
|
|
Check("a repo lens's block is as it was: no journal line, no lore, the owner as Gate 4", repoBlk.contains("Nothing outside this list") && !repoBlk.contains("lore") && repoBlk.contains("Gate 4 must see it"));
|
|
const std::vector<Json> e2 = {{{"id", "r"}, {"hash", "H1"}, {"kind", "refuted"}, {"lenses", {"etiquette.md"}}}};
|
|
const std::vector<Json> rows2 = {{{"id", "r"}, {"status", "in-force"}}};
|
|
const std::string personaBlk = Dispositions::Block(e2, rows2, Plan::PersonaLens, "H1", "camera");
|
|
Check("a refuted entry is hidden from other lenses, shown to its own and to a persona, whose rules ask for a disposition-review note per entry",
|
|
Dispositions::Block(e2, rows2, "facts.md", "H1", "camera").contains("(none)") && Dispositions::Block(e2, rows2, "etiquette.md", "H1", "camera").contains("- r [")
|
|
&& personaBlk.contains("- r [") && personaBlk.contains("kind=disposition-review") && personaBlk.contains("wouldComment"));
|
|
}
|
|
|
|
// ---- prompts --------------------------------------------------------------------
|
|
const std::string st = Prompt::SystemText("commits");
|
|
const std::string stk = Prompt::SystemText("series");
|
|
Check("system text: the kernel variant (a patch series, lore.py, the milos clone); the reply variant; the repo variant without them",
|
|
stk.contains("patch series") && stk.contains("lore.py") && stk.contains("milos") && Prompt::SystemText("reply").contains("mailing-list reply") && st.contains("commit range") && !st.contains("lore.py")
|
|
&& Prompt::SystemText() == st);
|
|
const std::string h = Dyn::Str(series.out.at("hash8"));
|
|
const std::vector<std::string> trees = {"work/linux", "work/milos-linux"};
|
|
const std::vector<std::string> reviewers = {"A B", "C D"};
|
|
const std::string rl = Prompt::RunLayerKernel("series", "journal/x/patch/y/v1", "camera", "work/linux @ abc", reviewers, "2026-10-31", trees, h, series.lines, series.out.at("units"),
|
|
"REFERENCES prefetched: (none - the artifact cites no lore URLs)");
|
|
Check("run layer (series): the artifact, the ledger, the thread mboxes, the captures, the base, the trees, the archaeology and lore rules, the reviewers, the date; the inventory names the partition tool; the digest last",
|
|
rl.contains(std::format("- The frozen artifact (review THIS, at hash {}): the *.patch files in journal/x/patch/y/v1 - inlined below in sorted order; nothing else in that dir is an input", h))
|
|
&& rl.contains("- Ledger: journal/camera/ledger.md (the only mutable journal file you may read)") && rl.contains("- Thread mboxes: journal/camera/captures/threads/")
|
|
&& rl.contains("- Evidence/proof artifacts: journal/camera/captures/ (index in the ledger)") && rl.contains("- Target tree at the submission base: work/linux @ abc")
|
|
&& rl.contains("- Trees: work/linux, work/milos-linux") && rl.contains("- History archaeology ONLY in work/milos-linux") && rl.contains("- lore.kernel.org ONLY via utilities/lore.py")
|
|
&& rl.contains("- Reviewers with a persona in this run: A B, C D") && rl.contains("- Run date: 2026-10-31") && rl.contains("(utilities/inventory.py: every non-blank line")
|
|
&& rl.ends_with("REFERENCES prefetched: (none - the artifact cites no lore URLs)") && rl.contains("\n0001| From 3333"),
|
|
Dyn::Slice(rl, 0, 1200));
|
|
const std::string rlr = Prompt::RunLayerKernel("reply", "journal/x/draft.txt", "camera", "", {}, "2026-10-31", trees, "H", draft.lines, draftUnits, "R");
|
|
Check("run layer (reply): the draft file, the base from the artifact, no reviewers line",
|
|
rlr.contains("the reply draft file journal/x/draft.txt - inlined below; nothing else is an input") && rlr.contains("- Target tree at the submission base: see the artifact") && !rlr.contains("Reviewers with a persona"));
|
|
const std::string rlc = Prompt::RunLayer("journal/ims/push/x", "abc123", "2026-09-25", std::vector<std::string>{"work/prepush-trees/imsd-1234"}, h, series.lines, series.out.at("units"), {}, "imsd", "ims");
|
|
const std::string rlcInputs = rlc.substr(0, rlc.find("\n\nARTIFACT (frozen"));
|
|
Check("run layer (commits, a subsystem): the ledger and captures lines in INPUTS, nothing of lore or milos there",
|
|
rlcInputs.contains("- Ledger: journal/ims/ledger.md") && rlcInputs.contains("- Evidence/proof artifacts: journal/ims/captures/") && !rlcInputs.contains("lore") && !rlcInputs.contains("milos")
|
|
&& !Prompt::RunLayer("x", "", "d", std::vector<std::string>{"."}, h, series.lines, series.out.at("units")).substr(0, 400).contains("journal/"));
|
|
const Plan::LensPlan bs = Plan::PlanLens("better-solution.md", series.out.at("units"), series.lines);
|
|
const Plan::LensPlan bp = Plan::PlanLens("bug-prover.md", series.out.at("units"), series.lines);
|
|
const Plan::LensPlan et = Plan::PlanLens("etiquette.md", draftUnits, draft.lines);
|
|
auto jobOf = [](const std::vector<Json>& jobs, std::string_view unit) -> const Json& {
|
|
for (const Json& j : jobs) {
|
|
if (j.at("unit") == unit) return j;
|
|
}
|
|
throw std::runtime_error(std::format("no job {}", unit));
|
|
};
|
|
const std::string tailWholeFirst = Prompt::Tail(bs.whole, series.out.at("units"), series.lines);
|
|
const std::string tailWholeAll = Prompt::Tail(consistency.whole, draftUnits, draft.lines);
|
|
const std::string tailPersona = Prompt::Tail(persona.whole, draftUnits, draft.lines);
|
|
Check("whole tails: the approach (first), the consistency whole (all) and the persona whole, each listing the slots, with the kernel vocabulary - the persona's its own",
|
|
tailWholeFirst.contains("judge the artifact as an APPROACH") && tailWholeFirst.contains("- whole:cover-vs-patches [0-0]") && tailWholeFirst.contains("Coverage vocabulary for this lens: clear")
|
|
&& tailWholeAll.contains("WHOLE PASS of the consistency lens") && tailWholeAll.contains("whole:route") && tailPersona.contains("predictedReview") && tailPersona.contains("`notMine`")
|
|
&& tailPersona.contains("Coverage vocabulary for a maintainer or reviewer: would-require") && !tailPersona.contains("Coverage vocabulary for this lens"));
|
|
Check("block tails: the anchor and refs hints, the kernel outside/absent hints, the text hint, the one scope phrase for every unit kind",
|
|
Prompt::Tail(jobOf(facts.blocks, "anchor"), draftUnits, draft.lines).contains("fixes the as-of date") && Prompt::Tail(jobOf(facts.blocks, "refs"), draftUnits, draft.lines).contains("numbering is contiguous")
|
|
&& Prompt::Tail(jobOf(bp.blocks, "p1:outside"), series.out.at("units"), series.lines).contains("at the submission base, outside its own diff")
|
|
&& Prompt::Tail(jobOf(bp.blocks, "p1:absent"), series.out.at("units"), series.lines).contains("a trailer, a test statement, a Cc")
|
|
&& Prompt::Tail(jobOf(facts.blocks, "R2"), draftUnits, draft.lines).contains("prefetched raw files") && !Prompt::Tail(jobOf(facts.blocks, "R2"), draftUnits, draft.lines).contains("INPUTS names")
|
|
&& Prompt::Tail(jobOf(facts.blocks, "R2"), draftUnits, draft.lines).contains("a claim at its source, a sentence against its proof obligation, a hunk against the standard")
|
|
&& Prompt::Tail(jobOf(bp.blocks, "p1:drivers/nfc/widget.c#1"), series.out.at("units"), series.lines).contains("a claim at its source, a sentence against its proof obligation, a hunk against the standard"));
|
|
Check("slot tails: each lens's duty on its slot",
|
|
Prompt::Tail(jobOf(et.blocks, "whole:asks"), draftUnits, draft.lines).contains("Step 0 and the point map") && Prompt::Tail(jobOf(facts.blocks, "whole:route"), draftUnits, draft.lines).contains("claims that span units")
|
|
&& Prompt::Tail(jobOf(Plan::PlanLens("minimalism.md", series.out.at("units"), series.lines).blocks, "whole:absent"), series.out.at("units"), series.lines).contains("series-level minimalism")
|
|
&& Prompt::Tail(jobOf(Plan::PlanLens("conciseness.md", draftUnits, draft.lines).blocks, "whole:absent"), draftUnits, draft.lines).contains("growth calibration")
|
|
&& Prompt::SlotDuty("process-docs.md", "whole:asks").contains("get_maintainer") && Prompt::SlotDuty("process-docs.md", "whole:absent").empty()
|
|
&& !Prompt::Tail(jobOf(facts.blocks, "whole:asks"), draftUnits, draft.lines).contains("for this lens that"));
|
|
const std::string ll = Prompt::LensLayer("facts.md", "CHARTER TEXT", "DISPOSITIONS BLOCK");
|
|
const std::string llp = Prompt::LensLayer(Plan::PersonaLens, "CHARTER TEXT", "DISPOSITIONS BLOCK", {}, "the digest", {}, "Thomas Colliers", "lore.py search ...\nlisting");
|
|
Check("lens layer: a persona's layer opens with who they are and the calibration map, then the charter, the dispositions and the whole pass's digest; a plain one with the charter",
|
|
llp.starts_with("Your persona: Thomas Colliers. You ARE this person") && llp.contains("\n\nCALIBRATION MAP - a lore search listing of this person's recent mail (fetch the bodies you need):\nlore.py search ...\nlisting\n\nYOUR CHARTER (process/lenses/maintainer-persona.md), verbatim:\n\nCHARTER TEXT\n\nDISPOSITIONS BLOCK\n\nThe whole pass of this lens on this hash concluded")
|
|
&& ll.starts_with("YOUR CHARTER (process/lenses/facts.md), verbatim:\n\nCHARTER TEXT\n\nDISPOSITIONS BLOCK") && Prompt::LensLayer("facts.md", "C", "D", {}, {}, {}, "P", {}).contains("Your persona: P.")
|
|
&& !Prompt::LensLayer("facts.md", "C", "D", {}, {}, {}, "P", {}).contains("CALIBRATION MAP"));
|
|
const std::string up = Prompt::UserPrompt("TAIL", Json({{"maxTurns", 60}}), {}, true);
|
|
Check("user prompt: the kernel budget line says to read the prefetched raw files; the push gates' does not",
|
|
up.contains("Tool-call budget for this pass: 60") && up.contains("Batch independent reads into one call, read the prefetched raw files instead of fetching, and return the structured output")
|
|
&& Prompt::UserPrompt("TAIL", Json({{"maxTurns", 60}})).contains("Batch independent reads into one call and return the structured output"));
|
|
|
|
// ---- the schema -----------------------------------------------------------------
|
|
const std::string wholeK = Schema::SchemaJson("whole", "series");
|
|
Check("the kernel schema: the persona vocabulary, the kernel note kinds, wouldComment, predictedReview and notMine; the push gates' schema as it was",
|
|
wholeK.contains("\"would-require\"") && wholeK.contains("\"ledger-row\"") && wholeK.contains("\"disposition-review\"") && wholeK.contains("\"wouldComment\"") && wholeK.contains("\"predictedReview\"")
|
|
&& wholeK.contains("\"notMine\"") && wholeK.contains("objective wrongness, or a predicted reviewer comment") && Schema::SchemaJson("block", "reply").contains("\"not-mine\"")
|
|
&& !Schema::SchemaJson("block", "commits").contains("would-require") && Schema::SchemaJson("block") == Schema::SchemaJson("block", "commits") && !Schema::SchemaJson("whole").contains("notMine"));
|
|
|
|
// ---- normalize and the matrix ----------------------------------------------------
|
|
{
|
|
const Json inv = Json::array({{{"id", "R1"}}, {{"id", "R2"}}, {{"id", "whole:route"}}});
|
|
const std::vector<std::string> ids = {"R1", "R2", "whole:route"};
|
|
Json v = {{"verdict", "SEND-READY"}, {"findings", Json::array()}, {"notes", Json::array()}, {"coverage", {{{"item", "R1"}, {"verdict", "would-object"}, {"basis", "x"}}}}};
|
|
const Json n = Normalize::NormalizeOutput(v, {}, std::string(Plan::PersonaLens), "h", Json::array({inv[0]}), ids);
|
|
Json v2 = {{"verdict", "SEND-READY"}, {"findings", Json::array()}, {"notes", Json::array()}, {"coverage", {{{"item", "R1"}, {"verdict", "no-comment"}, {"basis", "no record"}}}}, {"predictedReview", "r"}, {"notMine", {"R2"}}};
|
|
const Json n2 = Normalize::NormalizeOutput(v2, {}, std::string(Plan::PersonaLens), "h", Json::array({inv[0]}), ids);
|
|
Json v3 = {{"verdict", "SEND-READY"}, {"findings", Json::array()}, {"notes", Json::array()}, {"coverage", {{{"item", "R1"}, {"verdict", "defect"}, {"basis", "x"}}}}};
|
|
const Json n3 = Normalize::NormalizeOutput(v3, {}, std::string(Plan::PersonaLens), "h", Json::array({inv[0]}), ids);
|
|
Json v4 = {{"verdict", "SEND-READY"}, {"findings", Json::array()}, {"notes", Json::array()}, {"coverage", {{{"item", "R1"}, {"verdict", "would-object"}, {"basis", "x"}}}}};
|
|
const Json n4 = Normalize::NormalizeOutput(v4, {}, "facts.md", "h", Json::array({inv[0]}), ids);
|
|
Check("a persona's would-object without a finding is synthesized (fail closed), its no-comment accepted, a generic verdict logged as outside its vocabulary; the persona's review and not-mine list come through",
|
|
n.at("verdict") == "FIX FIRST" && Dyn::Truthy(n.at("findings")[0].at("synthesized")) && n2.at("verdict") == "SEND-READY" && !Dyn::Truthy(n2.at("normalizeLogs")) && n2.at("predictedReview") == "r"
|
|
&& n2.at("notMine") == Json({"R2"}) && Dyn::Str(n3.at("normalizeLogs")[0]).contains("not in the vocabulary (would-require/no-comment/") && n3.at("verdict") == "FIX FIRST"
|
|
&& !Dyn::Has(n4, "predictedReview") && Dyn::Str(n4.at("normalizeLogs")[0]).contains("(clear/defect/immaterial/n/a)"),
|
|
DumpCompact(n3.at("normalizeLogs")));
|
|
const Json pa = {{"partLabel", "persona:X:block:R1"}, {"partKind", "block"}, {"partUnit", "R1"}, {"verdict", "FIX FIRST"},
|
|
{"findings", {{{"finding", "R2 contradicts R1"}, {"evidence", "e"}, {"proposedAction", "p"}, {"items", {"R1", "R2"}}, {"discharged", false}}}},
|
|
{"notes", Json::array()}, {"coverage", {{{"item", "R1"}, {"verdict", "would-object"}, {"basis", "b"}}}}, {"coverageMissing", Json::array()}, {"undischarged", 1}};
|
|
const Json pb = {{"partLabel", "persona:X:block:R2"}, {"partKind", "block"}, {"partUnit", "R2"}, {"verdict", "SEND-READY"}, {"findings", Json::array()}, {"notes", Json::array()},
|
|
{"coverage", {{{"item", "R2"}, {"verdict", "no-comment"}, {"basis", "m1@x"}}}}, {"coverageMissing", Json::array()}};
|
|
const Json m = Normalize::MergeParts("persona:X", std::string(Plan::PersonaLens), std::vector<Json>{pa, pb}, {});
|
|
std::map<std::string, std::string> cov;
|
|
for (const Json& c : m.at("coverage")) cov[Dyn::Str(c.at("item"))] = Dyn::Str(c.at("verdict"));
|
|
Check("merge: a unit a persona's undischarged finding cites is raised to would-object, not defect", cov.at("R2") == "would-object" && Dyn::Str(m.at("normalizeLogs")[0]).contains("raised to would-object"), DumpCompact(m.at("coverage")));
|
|
const Json pw = {{"partLabel", "persona:X:whole"}, {"partKind", "whole"}, {"partUnit", nullptr}, {"verdict", "SEND-READY"}, {"findings", Json::array()}, {"notes", Json::array()},
|
|
{"coverage", {{{"item", "whole:route"}, {"verdict", "no-comment"}, {"basis", "w"}}}}, {"coverageMissing", Json::array()}};
|
|
const Json pbk = {{"partLabel", "persona:X:block:R1"}, {"partKind", "block"}, {"partUnit", "R1"}, {"verdict", "SEND-READY"}, {"findings", Json::array()}, {"notes", Json::array()},
|
|
{"coverage", {{{"item", "R1"}, {"verdict", "immaterial"}, {"basis", "w"}}}}, {"coverageMissing", Json::array()}};
|
|
Json pbk2 = pbk;
|
|
pbk2["coverage"] = {{{"item", "R1"}, {"verdict", "no-comment"}, {"basis", "later"}}};
|
|
const Json m4 = Normalize::MergeParts("persona:X", std::string(Plan::PersonaLens), std::vector<Json>{pw, pbk, pbk2}, {});
|
|
std::map<std::string, std::string> cov4;
|
|
for (const Json& c : m4.at("coverage")) cov4[Dyn::Str(c.at("item"))] = Dyn::Str(c.at("verdict"));
|
|
const Json mx = Normalize::Matrix(inv, std::vector<Json>{m4});
|
|
Json fy = m4;
|
|
fy["lens"] = "persona:Y";
|
|
fy["coverage"] = {{{"item", "R1"}, {"verdict", "no-comment"}, {"basis", " No Record "}}, {{"item", "R2"}, {"verdict", "not-mine"}, {"basis", "no record"}}};
|
|
const Json mx2 = Normalize::Matrix(inv, std::vector<Json>{m4, fy});
|
|
Check("the strongest verdict per unit wins (immaterial > no-comment); the matrix: a unit without an entry is MISSING, a hole and a persona hole; a not-mine is unowned too; thin where one persona owns it; modelled where every persona rests on \"no record\"",
|
|
cov4.at("R1") == "immaterial" && mx.at("matrix").at("R2").at("persona:X").at("verdict") == "MISSING" && mx.at("holes") == Json({"R2"}) && mx.at("personaHoles") == Json({"R2"})
|
|
&& mx.at("thin") == Json({"R1", "whole:route"}) && mx.at("modelled") == Json::array() && mx2.at("thin") == Json({"whole:route"}) && mx2.at("modelled") == Json::array()
|
|
&& mx2.at("personaHoles") == Json({"R2"}) && mx2.at("holes") == Json({"R2"}) && Dyn::Has(mx2, "notReviewed"),
|
|
DumpCompact(mx2));
|
|
Json gy = fy;
|
|
gy["coverage"] = {{{"item", "R1"}, {"verdict", "no-comment"}, {"basis", "no record"}}, {{"item", "R2"}, {"verdict", "no-comment"}, {"basis", "NO RECORD"}}, {{"item", "whole:route"}, {"verdict", "no-comment"}, {"basis", "m"}}};
|
|
const Json mx3 = Normalize::Matrix(inv, std::vector<Json>{gy});
|
|
Check("...modelled: every persona entry of the unit says no record, whatever the case or spacing", mx3.at("modelled") == Json({"R1", "R2"}) && mx3.at("thin") == Json({"R1", "R2", "whole:route"}));
|
|
const Json keys = Normalize::Matrix(inv, std::vector<Json>{});
|
|
std::vector<std::string> order;
|
|
for (auto it = keys.begin(); it != keys.end(); ++it) order.push_back(it.key());
|
|
Check("the matrix's keys, in order", order == std::vector<std::string>{"units", "lenses", "holes", "personaHoles", "thin", "modelled", "notReviewed", "defects", "matrix"}, Show(order));
|
|
}
|
|
|
|
// ---- references ---------------------------------------------------------------------
|
|
{
|
|
const Refs::Table lore = Refs::ParseRefs("see https://lore.kernel.org/all/a@b/ and <https://lore.kernel.org/linux-arm-msm/c@d> again https://lore.kernel.org/all/a@b/ http://lore.kernel.org/r/e@f");
|
|
Check("refs: without a numbered list every lore URL once, in order, L1..; the message id of each spelling; a trailing slash kept in the whole match",
|
|
lore.size() == 3 && lore[0].first == "L1" && lore[0].second == "https://lore.kernel.org/all/a@b/" && lore[1].second == "https://lore.kernel.org/linux-arm-msm/c@d" && lore[2].first == "L3"
|
|
&& lore[2].second == "http://lore.kernel.org/r/e@f" && Refs::MsgidOf("https://lore.kernel.org/r/7927bdab@kernel.org/") == "7927bdab@kernel.org"
|
|
&& Refs::MsgidOf("https://lore.kernel.org/all/x@y") == "x@y" && !Refs::MsgidOf("https://example.org/all/x@y/") && Refs::UrlOf(refs, 2) == refs[1].second && !Refs::UrlOf(refs, 3),
|
|
DumpCompact(Json(lore)));
|
|
Check("refs: a numbered list wins over lore URLs in the body, the last line of a number wins, a non-URL token is the reference as given",
|
|
Refs::ParseRefs("[1] https://a/\nhttps://lore.kernel.org/all/z@z/\n[2] foo\n[1] https://b/").size() == 2 && Refs::ParseRefs("[1] https://a/\n[1] https://b/")[0].second == "https://b/"
|
|
&& Refs::ParseRefs("[2] foo")[0].second == "foo" && Refs::ParseRefs("[2]foo").empty());
|
|
Check("safe names and headers", Refs::SafeName("a/b c@d.e-f_g") == "a_b_c@d.e-f_g" && Refs::SafeName(std::string(300, 'x')).size() == 200
|
|
&& Refs::Headers("From: A <a@x>\nSubject: s\nFrom: B\nDate: d\n\nbody From: C") == std::map<std::string, std::string>{{"From", "A <a@x>"}, {"Subject", "s"}, {"Date", "d"}});
|
|
const fs::path refsDir = base / "refs";
|
|
std::vector<std::string> calls;
|
|
std::mutex callsLock;
|
|
Refs::SetFetcher([&](const fs::path&, const std::string& url, const fs::path& path) -> std::pair<std::string, std::string> {
|
|
{
|
|
// The fetchers run on the prefetch's worker threads.
|
|
const std::lock_guard guard(callsLock);
|
|
calls.push_back(url);
|
|
}
|
|
if (url.contains("fail")) return {"failed", "no such message"};
|
|
Write(path, "From: Mark <m@x>\nDate: Mon\nSubject: Re: the thing\n\nbody\n");
|
|
return {"fetched", ""};
|
|
});
|
|
std::vector<std::string> logs;
|
|
const Refs::Table table = {{"1", "https://lore.kernel.org/all/one@x/"}, {"2", "https://lore.kernel.org/all/fail@x/"}, {"3", "https://example.org/not-lore"}};
|
|
const Json index = Refs::Prefetch(base, table, refsDir, base / "refs-h8.json", [&](const std::string& l) { logs.push_back(l); }, 2);
|
|
Check("prefetch: a lore reference fetched and its headers read, a failure listed with its error, a non-lore URL not prefetched; the index written; the log",
|
|
index.at("1").at("status") == "fetched" && index.at("1").at("From") == "Mark <m@x>" && index.at("1").at("Subject") == "Re: the thing" && index.at("1").at("bytes") == 56
|
|
&& index.at("2").at("status") == "failed" && index.at("2").at("error") == "no such message" && index.at("3").at("status") == "not-prefetched (not a lore URL)" && calls.size() == 2
|
|
&& fs::exists(base / "refs-h8.json") && logs.size() == 1 && logs[0] == "refs: 1/3 lore references available locally (1 fetched now)",
|
|
DumpCompact(index));
|
|
calls.clear();
|
|
const Json again = Refs::Prefetch(base, table, refsDir, base / "refs-h8.json", [&](const std::string&) {}, 2);
|
|
Check("...a cached reference is not fetched again", again.at("1").at("status") == "cached" && calls == std::vector<std::string>{table[1].second});
|
|
const std::string digest = Refs::Digest(index);
|
|
Check("the digest: the header, then one line per reference - numbered ones first - with the headers and the path, FETCH FAILED, or the status",
|
|
digest.starts_with("REFERENCES prefetched (read the raw file instead of fetching; a FETCH FAILED line means the reference must be fetched by you or reported as unverifiable):\n[1] one@x | From: Mark <m@x> | Date: Mon | Subject: Re: the thing | ")
|
|
&& digest.contains(" (56 B)\n[2] fail@x | FETCH FAILED | https://lore.kernel.org/all/fail@x/\n[3] https://example.org/not-lore | not-prefetched (not a lore URL)"),
|
|
digest);
|
|
Json mixed = Json::object();
|
|
mixed["L2"] = {{"url", "u2"}, {"status", "x"}};
|
|
mixed["10"] = {{"url", "u10"}, {"status", "x"}};
|
|
mixed["L1"] = {{"url", "u1"}, {"status", "x"}};
|
|
mixed["2"] = {{"url", "u2"}, {"status", "x"}};
|
|
Check("...numbered references in numeric order, then the others", Refs::Digest(mixed).contains("\n[2] u2 | x\n[10] u10 | x\n[L1] u1 | x\n[L2] u2 | x"));
|
|
Refs::SetSearcher([&](const fs::path&, const std::string& q) -> std::pair<std::string, std::string> {
|
|
calls.push_back(q);
|
|
return q.contains("Nobody") ? std::pair(std::string("No results.\n"), std::string("err")) : std::pair(std::string("2026-09-01 Re: x\n2026-09-03 Re: y\n"), std::string());
|
|
});
|
|
logs.clear();
|
|
const auto [none, emptyCorpus] = Refs::PersonaCorpus(base, "Nobody Here", refsDir, "20251031", [&](const std::string& l) { logs.push_back(l); });
|
|
const auto [path, corpus] = Refs::PersonaCorpus(base, "Mark Brown", refsDir, "20251031", [&](const std::string& l) { logs.push_back(l); });
|
|
calls.clear();
|
|
const auto [again2, corpus2] = Refs::PersonaCorpus(base, "Mark Brown", refsDir, "20251031", [&](const std::string&) {});
|
|
Check("a persona corpus: no results is not cached and says so; results are cached under the slug with the query line, and read from the cache after",
|
|
!none && emptyCorpus.empty() && logs[0] == "persona corpus: Nobody Here -> NO RESULTS for 'f:\"Nobody Here\" AND d:20251031..' (stderr: err) - the whole pass calibrates by its own searches"
|
|
&& path && path->filename() == "persona-mark-brown.txt" && corpus.starts_with("lore.py search 'f:\"Mark Brown\" AND d:20251031..' (") && corpus.ends_with("2026-09-01 Re: x\n2026-09-03 Re: y\n")
|
|
&& logs[1].starts_with("persona corpus: Mark Brown -> ") && logs[1].ends_with(" (33 chars)") && calls.empty() && corpus2 == corpus,
|
|
Dyn::Join(logs, " | "));
|
|
Check("since: 365 days before the run date", Refs::SinceOf("2026-10-31") == "20251031" && Refs::SinceOf("2024-03-01") == "20230302");
|
|
Refs::SetFetcher(nullptr);
|
|
Refs::SetSearcher(nullptr);
|
|
}
|
|
|
|
// ---- the state's persona pass ---------------------------------------------------
|
|
{
|
|
const Json disk = {{"version", 1}, {"entries", Json::object()}, {"personaPass", nullptr}};
|
|
const Json m = State::MergeState(disk, Json::object(), {}, nullptr, Json({{"hash", "H2"}}));
|
|
Check("personaPass set when given and kept when not", m.at("personaPass") == Json({{"hash", "H2"}}) && State::MergeState(m, Json::object()).at("personaPass") == Json({{"hash", "H2"}}));
|
|
}
|
|
|
|
// ---- the allowlist --------------------------------------------------------------
|
|
{
|
|
const std::vector<std::string> al = Pool::AllowedTools("facts.md", "block", std::vector<std::string>{"work/milos-linux"}, {}, std::nullopt, "none");
|
|
const std::vector<std::string> bp = Pool::AllowedTools("bug-prover.md", "block", std::vector<std::string>{"work/milos-linux"}, {}, std::nullopt, "none");
|
|
const std::vector<std::string> rp = Pool::AllowedTools(std::string(Plan::FactsLens), "block", std::vector<std::string>{"."}, {}, std::nullopt, "none");
|
|
const std::vector<std::string> pp = Pool::AllowedTools(std::string(Plan::PersonaLens), "block", std::vector<std::string>{"work/milos-linux"}, {}, std::nullopt, "none");
|
|
auto has = [](const std::vector<std::string>& l, std::string_view x) { return std::ranges::contains(l, std::string(x)); };
|
|
Check("allowlist: a kernel lens fetches from lore, a lens that judges against the list searches too, a repo lens has no lore; the tree's git verbs in both spellings",
|
|
has(al, "Bash(python3 utilities/lore.py fetch:*)") && has(al, "Bash(python3 utilities/lore.py search:*)") && has(al, "Bash(git -C work/milos-linux show:*)") && has(al, "Bash(cd:*)")
|
|
&& has(bp, "Bash(python3 utilities/lore.py fetch:*)") && !has(bp, "Bash(python3 utilities/lore.py search:*)") && std::ranges::none_of(rp, [](const std::string& x) { return x.contains("lore.py"); })
|
|
&& has(pp, "Bash(python3 utilities/lore.py search:*)") && std::ranges::none_of(al, [](const std::string& x) { return x.starts_with("Read("); }));
|
|
}
|
|
|
|
// ---- dry runs of both modes through the binary -------------------------------------
|
|
{
|
|
const fs::path bench = base / "bench";
|
|
fs::create_directories(bench / ".review-gates");
|
|
// The bench's modes: a series runs every kernel lens, a reply its
|
|
// kind's five.
|
|
Json modes = {{"series", {{"lenses", Json::array()}}}, {"reply", {{"lenses", Json::array()}}}};
|
|
const std::array<std::string_view, 10> on = {"facts.md", "etiquette.md", "consistency.md", "conciseness.md", "better-solution.md", "code-quality.md", "minimalism.md", "bug-prover.md", "process-docs.md", Plan::PersonaLens};
|
|
for (const std::string_view lf : on) modes["series"]["lenses"].push_back(Plan::LensName(lf));
|
|
const std::array<std::string_view, 5> replyOn = {"facts.md", "etiquette.md", "consistency.md", "conciseness.md", Plan::PersonaLens};
|
|
for (const std::string_view lf : replyOn) modes["reply"]["lenses"].push_back(Plan::LensName(lf));
|
|
// A mode of the bench's own name: a reply with the facts lens alone, at effort high.
|
|
modes["quick"] = {{"kind", "reply"}, {"lenses", Json::array({"facts"})}, {"effort", "high"}};
|
|
// The bench's own section for the facts lens (lenses.facts.section).
|
|
Write(bench / "process" / "lens-sections" / "facts.md", "## The bench's own section\n\n- 2026-10-31: a section of the bench repository, appended to the facts charter.\n");
|
|
Write(bench / ".review-gates" / "config.json", DumpFile(Json({{"modes", modes}, {"lenses", {{"facts", {{"section", "process/lens-sections/facts.md"}}}}}})));
|
|
const fs::path fakeDir = base / "fake";
|
|
const fs::path fake = fakeDir / "claude";
|
|
Write(fake, FakeClaude);
|
|
fs::permissions(fake, fs::perms::owner_all | fs::perms::group_read | fs::perms::group_exec | fs::perms::others_read | fs::perms::others_exec);
|
|
const std::map<std::string, std::string> env = With(Env, {{"FAKE_DIR", fakeDir.string()}});
|
|
// The reply: a draft under the journal, its references indexed
|
|
// as failed (no lore here), a reviewer with no corpus.
|
|
const fs::path draftPath = bench / "journal" / "x" / "draft.eml";
|
|
Write(draftPath, Dyn::ReadText(fix / "reply.eml"));
|
|
const std::string replyHash = Artifact::Sha256File(draftPath);
|
|
FailedIndex(bench / "journal" / "x" / "gate-state", replyHash.substr(0, 8), Refs::ParseRefs(Dyn::ReadText(draftPath)));
|
|
const fs::path scratch = base / "scratch";
|
|
auto run = [&](std::vector<std::string> args) {
|
|
std::vector<std::string> argv = {Platform::SelfExe().string(), "run"};
|
|
std::ranges::move(args, std::back_inserter(argv));
|
|
argv.insert(argv.end(), {"--date", "2026-10-31", "--claude-bin", fake.string(), "--scratch", scratch.string(), "--seats", "local", "--no-probe"});
|
|
return Process::Run(argv, bench, std::chrono::seconds(120), {}, env);
|
|
};
|
|
const Process::Captured replyDry = run({"--mode", "reply", "--subsystem", "x", "--artifact", "journal/x/draft.eml", "--hash", replyHash, "--reviewers", "Some Maintainer", "--dry-run"});
|
|
const fs::path replyDryDir = scratch / std::format("{}-run-1-dry", replyHash.substr(0, 8));
|
|
Json replyPlan;
|
|
try {
|
|
replyPlan = Dyn::LoadJsonFile(replyDryDir / "dry-run.json");
|
|
} catch (const std::exception&) {
|
|
}
|
|
const std::string replySystems = SystemFiles(replyDryDir);
|
|
const std::string replyPrompts = Prompts(replyDryDir);
|
|
const Json& byLens = replyPlan.is_object() ? replyPlan.at("dryRun").at("jobsByLens") : Json::object();
|
|
Check("a reply dry run: exit 2; Gate 3's four lenses and the persona instance planned (whole + blocks); the common text is the reply's; the run layer names the ledger and carries the failed reference; the persona's system file names them and the private-contributor text; no lore fetch attempted",
|
|
replyDry.returnCode == 2 && replyPlan.is_object() && Dyn::Has(byLens, "facts") && Dyn::Has(byLens, "etiquette") && Dyn::Has(byLens, "consistency") && Dyn::Has(byLens, "conciseness")
|
|
&& Dyn::Has(byLens, "persona:Some Maintainer") && byLens.at("persona:Some Maintainer").get<std::int64_t>() > 1 && replySystems.contains("pre-send pipeline for a Linux kernel mailing-list reply")
|
|
&& replySystems.contains("- Ledger: journal/x/ledger.md (the only mutable journal file you may read)") && replySystems.contains("| FETCH FAILED | https://lore.kernel.org/all/20260815.4567@example.org/")
|
|
&& replySystems.contains("Your persona: Some Maintainer.") && replySystems.contains("lore.py search for mail from Some Maintainer since 20251031: NO RESULTS") && replySystems.contains("journal/x/ledger.md indexes every message")
|
|
&& replyPrompts.contains("read the whole artifact as they would") && replyPrompts.contains("Coverage vocabulary for a maintainer or reviewer")
|
|
&& replyPlan.at("overall") == "DRY RUN - NOT SEND-READY (no process launched)" && replyPlan.at("mode") == "reply" && replyPlan.at("refs").at("failed") == Json({"1"})
|
|
&& Dyn::Has(replyPlan.at("coverage"), "personaHoles") && replyPlan.at("lensesOff") == Json::array() && Dyn::Truthy(replyPlan.at("dryRun").at("ok")),
|
|
std::format("exit {}; {}{}", replyDry.returnCode, Dyn::Slice(replyDry.out, -1500), replyDry.err));
|
|
const std::string replyLog = Platform::ReadFile(replyDryDir / "log.txt").value_or("");
|
|
Check("...the log: the mode, the index loaded without a fetch, the persona corpus missing, the gates and the persona gate",
|
|
replyLog.contains(": reply journal/x/draft.eml @ ") && replyLog.contains("refs: 1 entries loaded from the existing index (dry run, no fetch)") && replyLog.contains("persona corpus: Some Maintainer -> NO RESULTS")
|
|
&& replyLog.contains("Gate 3 — Artifact truth: facts, etiquette, consistency, conciseness") && replyLog.contains("Gate 4 — Personas: persona:Some Maintainer"),
|
|
replyLog);
|
|
Check("...the bench's section: appended to the facts charter in the system files, after the shipped text, and the log names the lens",
|
|
replySystems.contains("- 2026-10-31: a section of the bench repository, appended to the facts charter.")
|
|
&& replySystems.find("# Lens: facts") < replySystems.find("- 2026-10-31: a section of the bench repository")
|
|
&& replyLog.contains("lenses: the repository's sections appended to facts"), replyLog);
|
|
// The series: the fixture under the journal, its cover's
|
|
// reference indexed.
|
|
const fs::path seriesDir = bench / "journal" / "x" / "patch" / "widget" / "v1";
|
|
for (const std::string& f : Artifact::PatchFiles(fix / "series")) Write(seriesDir / f, Dyn::ReadText(fix / "series" / f));
|
|
const std::string seriesHash = Artifact::ArtifactHash("series", seriesDir);
|
|
FailedIndex(seriesDir / "gate-state", seriesHash.substr(0, 8), Refs::ParseRefs(Dyn::Join(Inventory::Build("series", seriesDir.string()).lines, "\n")));
|
|
const Process::Captured seriesDry = run({"--mode", "series", "--subsystem", "x", "--artifact", "journal/x/patch/widget/v1", "--base", "work/linux @ abc", "--dry-run"});
|
|
const fs::path seriesDryDir = scratch / std::format("{}-run-1-dry", seriesHash.substr(0, 8));
|
|
Json seriesPlan;
|
|
try {
|
|
seriesPlan = Dyn::LoadJsonFile(seriesDryDir / "dry-run.json");
|
|
} catch (const std::exception&) {
|
|
}
|
|
const std::string seriesSystems = SystemFiles(seriesDryDir);
|
|
const Json& sByLens = seriesPlan.is_object() ? seriesPlan.at("dryRun").at("jobsByLens") : Json::object();
|
|
Check("a series dry run: exit 2; the three gates' nine lenses planned, no persona without reviewers; the series common text, the base and the default trees in the run layer; every record carries its reuse key",
|
|
seriesDry.returnCode == 2 && seriesPlan.is_object() && sByLens.size() == 9 && Dyn::Has(sByLens, "better-solution") && Dyn::Has(sByLens, "minimalism") && Dyn::Has(sByLens, "process-docs")
|
|
&& seriesSystems.contains("pre-send pipeline for a Linux kernel patch series") && seriesSystems.contains("- Target tree at the submission base: work/linux @ abc")
|
|
&& seriesSystems.contains("- Trees: work/linux, work/milos-linux") && seriesSystems.contains("| FETCH FAILED | https://lore.kernel.org/all/20260901.1234@example.org/")
|
|
&& seriesPlan.at("args").at("effort") == "max" && seriesPlan.at("args").at("reads") == 3 && seriesPlan.at("mode") == "series"
|
|
&& std::ranges::any_of(seriesPlan.at("processes"), [](const Json& r) { return r.at("kind") == "block" && Dyn::Get(r, "unit") == "p1:drivers/nfc/widget.c#1" && Dyn::Truthy(Dyn::Get(r, "reuseKey")); })
|
|
&& std::ranges::any_of(seriesPlan.at("processes"), [](const Json& r) { return Dyn::Get(r, "unit") == "cover:2" && Dyn::Truthy(Dyn::Get(r, "reuseKey")); }),
|
|
std::format("exit {}; {}{}", seriesDry.returnCode, Dyn::Slice(seriesDry.out, -1500), seriesDry.err));
|
|
const Process::Captured noSubsystem = run({"--mode", "series", "--artifact", "journal/x/patch/widget/v1", "--dry-run"});
|
|
const Process::Captured withReviewers = run({"--mode", "commits", "--artifact", "journal/x/patch/widget/v1", "--reviewers", "A", "--dry-run"});
|
|
const Process::Captured replyReads = run({"--mode", "reply", "--subsystem", "x", "--artifact", "journal/x/draft.eml", "--reads", "3", "--dry-run"});
|
|
Check("preflight: a kernel mode needs --subsystem; commits refuses --reviewers; a reply is read once",
|
|
noSubsystem.returnCode == 3 && noSubsystem.err.contains("series mode: --subsystem is required") && withReviewers.returnCode == 3 && withReviewers.err.contains("no persona gate")
|
|
&& replyReads.returnCode == 3 && replyReads.err.contains("a reply has no code units and is read once"),
|
|
noSubsystem.err + withReviewers.err + replyReads.err);
|
|
// A full run of the reply on the stand-in: gates clean without a
|
|
// persona pass is GATES CLEAN; with the reviewer, SEND-READY and
|
|
// the persona pass recorded.
|
|
const Process::Captured plain = run({"--mode", "reply", "--subsystem", "x", "--artifact", "journal/x/draft.eml", "--no-reuse", "--no-salvage"});
|
|
const Json plainDump = Dyn::LoadJsonFile(bench / "journal" / "x" / "gate-state" / std::format("reply-{}-run-1.json", replyHash.substr(0, 8)));
|
|
const Process::Captured withPersona = run({"--mode", "reply", "--subsystem", "x", "--artifact", "journal/x/draft.eml", "--reviewers", "Some Maintainer", "--no-reuse", "--no-salvage"});
|
|
const Json personaDump = Dyn::LoadJsonFile(bench / "journal" / "x" / "gate-state" / std::format("reply-{}-run-2.json", replyHash.substr(0, 8)));
|
|
const Json state = Dyn::LoadJsonFile(bench / "journal" / "x" / "gate-state" / "unit-state.json");
|
|
std::int64_t personaEntries = 0;
|
|
for (auto it = state.at("entries").begin(); it != state.at("entries").end(); ++it) {
|
|
if (Dyn::Get(it.value(), "persona") == "Some Maintainer") ++personaEntries;
|
|
}
|
|
Check("a full reply run on the stand-in: gates clean without a reviewer is GATES CLEAN (exit 2); with the reviewer the persona gate runs and passes - SEND-READY, exit 0, personaPass recorded, the persona's blocks stored with its name, the matrix without persona holes",
|
|
plain.returnCode == 2 && plainDump.at("overall") == "GATES CLEAN - NOT SEND-READY (no reviewers passed, Gate 4 did not run)" && withPersona.returnCode == 0 && personaDump.at("overall") == "SEND-READY"
|
|
&& Dyn::Get(state, "personaPass").is_object() && state.at("personaPass").at("reviewers") == Json({"Some Maintainer"}) && personaEntries > 0
|
|
&& personaDump.at("coverage").at("personaHoles") == Json::array() && std::ranges::contains(personaDump.at("coverage").at("lenses"), Json("persona:Some Maintainer"))
|
|
&& !Dyn::Has(state.at("entries").begin().value(), "reads"),
|
|
std::format("exit {} / {}; {} / {}; {}{}", plain.returnCode, withPersona.returnCode, Dyn::StrOr(Dyn::Get(plainDump, "overall"), "?"), Dyn::StrOr(Dyn::Get(personaDump, "overall"), "?"),
|
|
Dyn::Slice(withPersona.out, -800), withPersona.err));
|
|
// A mode of the repository's own name (2026-11-01): `quick`, a
|
|
// reply with the facts lens alone at effort high. The run takes
|
|
// its kind, lenses and effort from the config and the dump says
|
|
// mode and kind; the run lock, the run number and the dump name
|
|
// are the kind's, so this is the artifact's third reply dump. An
|
|
// unknown mode is refused with the configured names.
|
|
const Process::Captured quick = run({"--mode", "quick", "--subsystem", "x", "--artifact", "journal/x/draft.eml", "--no-reuse", "--no-salvage"});
|
|
Json quickDump;
|
|
try {
|
|
quickDump = Dyn::LoadJsonFile(bench / "journal" / "x" / "gate-state" / std::format("reply-{}-run-3.json", replyHash.substr(0, 8)));
|
|
} catch (const std::exception&) {
|
|
}
|
|
const Process::Captured nope = run({"--mode", "nope", "--subsystem", "x", "--artifact", "journal/x/draft.eml", "--dry-run"});
|
|
const Json quickLenses = quickDump.is_object() ? quickDump.at("coverage").at("lenses") : Json::array();
|
|
Check("a named mode: its kind, lenses and effort from the config, mode and kind in the dump, the dump numbered and named by the kind; an unknown mode is refused naming the configured ones",
|
|
quick.returnCode == 2 && quickDump.is_object() && quickDump.at("mode") == "quick" && quickDump.at("kind") == "reply" && quickDump.at("args").at("effort") == "high"
|
|
&& quickLenses == Json({"facts"}) && !quickDump.at("lensesOff").empty() && quick.out.contains("quick (a reply)") && nope.returnCode == 3
|
|
&& nope.err.contains("--mode nope: no such mode in the config (modes: ") && nope.err.contains("quick"),
|
|
std::format("exit {} / {}; {}{}", quick.returnCode, nope.returnCode, Dyn::Slice(quick.out, -600), nope.err));
|
|
}
|
|
fs::remove_all(base);
|
|
return Failures ? 1 : 0;
|
|
}
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
const std::vector<std::string_view> args(argv, argv + argc);
|
|
// With arguments this binary is crafter-review itself, in native mode.
|
|
if (args.size() > 1) {
|
|
Platform::SystemPaths paths = Platform::Paths();
|
|
paths.flatpakInfo = "/nonexistent/.flatpak-info";
|
|
Platform::SetPaths(paths);
|
|
return Crafter::Review::Main(args);
|
|
}
|
|
try {
|
|
return Checks();
|
|
} catch (const std::exception& error) {
|
|
std::println(std::cerr, "kernel modes: {}", error.what());
|
|
return 1;
|
|
}
|
|
}
|