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

@ -6,6 +6,7 @@ import :Normalize;
import :Json;
import :Text;
import :Schema;
import :Plan;
import std;
namespace Crafter::Review::Normalize {
@ -208,13 +209,16 @@ namespace Crafter::Review::Normalize {
for (const Json& n : notes) {
for (const Json& item : Dyn::Or(Dyn::Get(n, "items"), Dyn::EmptyList())) noted.insert(KeyOf(item));
}
const std::string vocabulary = Dyn::Join(std::vector<std::string>(Schema::CoverageVerdicts.begin(), Schema::CoverageVerdicts.end()), "/");
// A persona speaks its own vocabulary; every other lens the generic one.
const bool isPersona = lensFile == Plan::PersonaLens;
const std::span<const std::string_view> allowed = isPersona ? std::span<const std::string_view>(Schema::PersonaVerdicts) : std::span<const std::string_view>(Schema::GenericVerdicts);
const std::string vocabulary = Dyn::Join(std::vector<std::string>(allowed.begin(), allowed.end()), "/");
for (const std::string& id : ids) {
const Json* c = byItem.Find(Json(id));
if (!c || !Dyn::Truthy(*c)) continue;
const Json& verdict = Dyn::At(*c, "verdict");
const std::string key = KeyOf(Json(id));
if (!InVocabulary(verdict, Schema::CoverageVerdicts)) {
if (!InVocabulary(verdict, allowed)) {
logs.push_back(std::format("{}: coverage verdict \"{}\" on {} is not in the vocabulary ({}) - kept as given", lensFile, Dyn::Str(verdict), id, vocabulary));
}
if (verdict == "immaterial" && !noted.contains(key)) {
@ -260,7 +264,7 @@ namespace Crafter::Review::Normalize {
{"kind", "FIX FIRST declared with every finding discharged and no blocking contest (silent veto) - derived pass stands"}});
}
}
return {
Json out = {
{"verdict", derived},
{"declaredVerdict", declared},
{"findings", std::move(findings)},
@ -274,6 +278,13 @@ namespace Crafter::Review::Normalize {
{"normalizeLogs", std::move(logs)},
{"summary", Dyn::Get(v, "summary")},
};
if (lensFile == Plan::PersonaLens) {
// The review this person would send, and the units that are not
// theirs: what the persona's whole pass hands its block passes.
out["predictedReview"] = Dyn::Get(v, "predictedReview");
out["notMine"] = Dyn::Get(v, "notMine");
}
return out;
}
std::vector<Json> Dedupe(std::span<const Json> items) {
@ -312,6 +323,9 @@ namespace Crafter::Review::Normalize {
if (!previous || !Dyn::Truthy(*previous) || rank(Dyn::At(c, "verdict")) >= rank(Dyn::At(*previous, "verdict"))) merged.Set(item, c);
}
}
// A persona's blocking verdict is would-object; every other lens's
// defect.
const std::string blocking = lensFile == Plan::PersonaLens ? "would-object" : "defect";
Json crossLogs = Json::array();
for (const Json& f : findings) {
if (Dyn::Truthy(Dyn::Get(f, "discharged"))) continue;
@ -321,8 +335,8 @@ namespace Crafter::Review::Normalize {
const Json& finding = Dyn::Get(f, "finding");
const std::string cited = Dyn::Truthy(finding) ? Dyn::Slice(finding.get<std::string>(), 0, 80) : std::string();
const std::string was = Dyn::Str(Dyn::At(*entry, "verdict"));
merged.Set(item, {{"item", item}, {"verdict", "defect"}, {"basis", std::format("cited by finding: {}", cited)}});
crossLogs.push_back(std::format("{}: {} raised to defect - cited by an undischarged finding while its own entry was {}", lens, Dyn::Str(item), was));
merged.Set(item, {{"item", item}, {"verdict", blocking}, {"basis", std::format("cited by finding: {}", cited)}});
crossLogs.push_back(std::format("{}: {} raised to {} - cited by an undischarged finding while its own entry was {}", lens, Dyn::Str(item), blocking, was));
}
}
}
@ -426,6 +440,29 @@ namespace Crafter::Review::Normalize {
const Json& row = mx[id];
if (!std::ranges::any_of(row, owned)) holes.push_back(id);
}
// The persona columns: a unit no persona owns is a persona hole, one
// a single persona owns is thin, and one whose every persona entry
// rests on "no record" is modelled, not calibrated.
std::vector<std::string> personaColumns;
for (const Json& column : columns) {
if (column.is_string() && column.get_ref<const std::string&>().starts_with("persona:")) personaColumns.push_back(column.get<std::string>());
}
Json personaHoles = Json::array();
Json thin = Json::array();
Json modelled = Json::array();
for (const Json& unit : inventory) {
const std::string id = Dyn::At(unit, "id").get<std::string>();
std::vector<const Json*> entries;
for (const std::string& column : personaColumns) {
const Json& entry = Dyn::Get(mx[id], column);
if (Dyn::Truthy(entry) && owned(entry)) entries.push_back(&entry);
}
if (personaColumns.empty()) continue;
if (entries.empty()) personaHoles.push_back(id);
if (entries.size() == 1) thin.push_back(id);
const bool noRecord = std::ranges::all_of(entries, [](const Json* e) { return Dyn::Lower(Dyn::Strip(Dyn::Str(Dyn::Get(*e, "basis")))) == "no record"; });
if (!entries.empty() && noRecord) modelled.push_back(id);
}
Json defects = Json::object();
for (const Json& unit : inventory) {
const std::string id = Dyn::At(unit, "id").get<std::string>();
@ -442,6 +479,9 @@ namespace Crafter::Review::Normalize {
{"units", inventory.size()},
{"lenses", std::move(columns)},
{"holes", std::move(holes)},
{"personaHoles", std::move(personaHoles)},
{"thin", std::move(thin)},
{"modelled", std::move(modelled)},
{"notReviewed", std::move(sortedExcused)},
{"defects", std::move(defects)},
{"matrix", std::move(mx)},