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.
This commit is contained in:
Jorijn van der Graaf 2026-10-31 22:56:12 +01:00
commit 339f4b9b40
Signed by: jorijnvdgraaf
GPG key ID: 2937E59CDCC1BCFB
46 changed files with 5732 additions and 362 deletions

View file

@ -0,0 +1,44 @@
Hallo Thomas,
Dank je voor de nieuwe versie.
ov13b10-of/0001-media-i2c-ov13b10.patch
De eerste patch ziet er goed uit; zie punt 1 hieronder.
ov13b10-of/0000-cover-letter.patch
De cover noemt de test op 2026-09-10; dat klopt met wat ik zag.
Dates: every "still", "unmerged", "in review" and "latest version" below is
as of 2026-09-21 (work/linux at v7.1-rc2, work/milos-linux at v7.1.2-milos).
Reversals of the 2026-08-18 review (two points where that review was wrong):
R1. The active-array origin (ov13b10 3/3). The 08-18 review said the origin
was (0, 0); the datasheet [1] gives (8, 8), so the earlier ask is withdrawn.
R2. The link-frequencies constraint (ov13b10 1/3) depends on R1 and on the
binding example in [2]; it is still open as of 2026-09-21.
camss-milos
1. num_clients (camss 2/5). The field is read once; the v3 series on the
list [1] does the same.
a. The error path leaves the clock enabled.
b. The comment above it names the wrong register.
2. Required supplies (camss 1/5). Matches the binding in [2].
ov13b10-of
1. The crop default. A plain paragraph item without a citation at all.
across the three series
- Every series cites the same datasheet; the three cover letters should
agree on its revision.
- The Cc lists differ between the series.
[1] https://lore.kernel.org/r/7927bdab-9c3b-460d-a2ba-2e5b06ee8804@kernel.org/
[2] https://lore.kernel.org/all/20260815.4567@example.org/

View file

@ -0,0 +1,22 @@
From: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
To: Some Maintainer <maintainer@example.org>
Cc: linux-example@vger.kernel.org
Subject: Re: [PATCH v2] nfc: widget: add the driver
In-Reply-To: <20260920.111@example.org>
References: <20260920.111@example.org>
On Sat, 20 Sep 2026 at 10:00, Some Maintainer wrote:
> The probe should not read the revision twice.
>
> Please drop the second read.
Agreed, v3 reads it once and keeps it for the attribute.
> And the binding needs the example.
The example is in 2/2 of v3; it follows [1].
[1] https://lore.kernel.org/all/20260815.4567@example.org/
Tested-by: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
Reviewed-by: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>

View file

@ -0,0 +1,25 @@
From 3333333333333333333333333333333333333333 Mon Sep 17 00:00:00 2001
From: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
Date: Thu, 25 Sep 2026 09:00:00 +0200
Subject: [PATCH 0/2] nfc: widget: add the driver and its binding
This series adds the widget driver and documents its binding.
The driver was tested on the board described in [1]; the binding follows
the example the maintainer asked for in the v1 review, which is still
unmerged as of 2026-09-21.
[1] https://lore.kernel.org/all/20260901.1234@example.org/
Jorijn van der Graaf (2):
nfc: widget: add the driver
dt-bindings: nfc: document the widget
Documentation/devicetree/bindings/nfc/widget.yaml | 18 ++++++++++++++++++
README.md | 3 +++
drivers/nfc/widget.c | 12 ++++++++++++
3 files changed, 33 insertions(+)
base-commit: 0000000000000000000000000000000000000000
--
2.51.0

View file

@ -0,0 +1,37 @@
From 4444444444444444444444444444444444444444 Mon Sep 17 00:00:00 2001
From: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
Date: Thu, 25 Sep 2026 09:01:00 +0200
Subject: [PATCH 1/2] nfc: widget: add the driver
The widget answers on I2C and reports its revision at probe. The probe
reads the revision register once and keeps it for the sysfs attribute.
Tested on the board as of 2026-09-20 with v1 of the firmware.
Signed-off-by: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
Reviewed-by: Some Reviewer <reviewer@example.org>
---
drivers/nfc/widget.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
create mode 100644 drivers/nfc/widget.c
diff --git a/drivers/nfc/widget.c b/drivers/nfc/widget.c
new file mode 100644
index 0000000..a1b2c3d
--- /dev/null
+++ b/drivers/nfc/widget.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/i2c.h>
+
+static int widget_probe(struct i2c_client *client)
+{
+ int rev;
+
+ rev = i2c_smbus_read_byte_data(client, 0x00);
+ if (rev < 0)
+ return rev;
+ return 0;
+}
--
2.51.0

View file

@ -0,0 +1,51 @@
From 5555555555555555555555555555555555555555 Mon Sep 17 00:00:00 2001
From: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
Date: Thu, 25 Sep 2026 09:02:00 +0200
Subject: [PATCH 2/2] dt-bindings: nfc: document the widget
Document the widget's compatible and its one supply; the README gains a
line about the binding.
Signed-off-by: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
---
Documentation/devicetree/bindings/nfc/widget.yaml | 18 ++++++++++++++++++
README.md | 3 +++
2 files changed, 21 insertions(+)
create mode 100644 Documentation/devicetree/bindings/nfc/widget.yaml
diff --git a/Documentation/devicetree/bindings/nfc/widget.yaml b/Documentation/devicetree/bindings/nfc/widget.yaml
new file mode 100644
index 0000000..c3d4e5f
--- /dev/null
+++ b/Documentation/devicetree/bindings/nfc/widget.yaml
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/nfc/widget.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Widget NFC controller
+
+properties:
+ compatible:
+ const: example,widget
+ vdd-supply: true
+
+required:
+ - compatible
+ - vdd-supply
+
+additionalProperties: false
diff --git a/README.md b/README.md
index d4e5f6a..e5f6a7b 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,5 @@
# hello
+The widget binding lives under Documentation/devicetree/bindings/nfc/.
+It names one supply.
+
--
2.51.0

714
tests/KernelModes/main.cpp Normal file
View file

@ -0,0 +1,714 @@
// 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::ModeLenses("series"), std::string(Plan::PersonaLens)) && std::ranges::contains(Plan::ModeLenses("reply"), std::string(Plan::PersonaLens))
&& !std::ranges::contains(Plan::ModeLenses("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");
Json lenses = Json::object();
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) lenses[Plan::LensName(lf)] = {{"run", true}};
Write(bench / ".review-gates" / "config.json", DumpFile(Json({{"lenses", lenses}})));
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);
// 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));
}
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;
}
}