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:
parent
4fc15fe1c2
commit
339f4b9b40
46 changed files with 5732 additions and 362 deletions
|
|
@ -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)},
|
||||
|
|
|
|||
Loading…
Reference in a new issue