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

@ -12,10 +12,14 @@ import :Json;
export namespace Crafter::Review::Dispositions {
// The register's entries with an id; empty `path`: none.
std::vector<Json> Load(std::string_view path);
// [{id, status, detail}], statuses in-force | carried | lapsed.
std::vector<Json> Classify(std::span<const Json> entries, std::string_view hash, const std::filesystem::path& artifact);
// [{id, status, detail}], statuses in-force | carried | lapsed. The
// series and commits modes carry by anchor count; a reply never carries.
std::vector<Json> Classify(std::span<const Json> entries, std::string_view hash, const std::filesystem::path& artifact, std::string_view mode = "commits");
// The entries the rows say are in force or carried.
std::vector<Json> InForce(std::span<const Json> entries, std::span<const Json> rows);
// The DISPOSITIONS IN FORCE block for one lens.
std::string Block(std::span<const Json> inForce, std::span<const Json> rows, std::string_view lensFile, std::string_view hash);
// The DISPOSITIONS IN FORCE block for one lens: a kernel lens gets the
// kernel pipeline's rules (the ledger, captures, lore), a persona its
// disposition-review duty; a subsystem names the ledger whose waiver rows
// lapse when not listed.
std::string Block(std::span<const Json> inForce, std::span<const Json> rows, std::string_view lensFile, std::string_view hash, std::string_view subsystem = {});
}

View file

@ -5,13 +5,35 @@ export module Crafter.Review:Inventory;
import std;
import :Json;
// The partition of a frozen commit range — a `git format-patch` export — into
// review units for the coverage contract. It is MECHANICAL: no agent decides
// what the units are. Every non-blank line of the artifact belongs to
// exactly one unit, and the inventory fails when that does not hold. Fixed
// slots give what is NOT in the text a row of the coverage matrix too.
// The partition of a frozen artifact into review units for the coverage
// contract: a commit range (a `git format-patch` export, the commits mode),
// a kernel patch series (the same export with a cover letter, the series
// mode) or a mailing-list reply (the reply mode). It is MECHANICAL: no agent
// decides what the units are. Every non-blank line of the artifact belongs
// to exactly one unit, and the inventory fails when that does not hold.
// Fixed slots give what is NOT in the text a row of the coverage matrix too.
//
// Units (ids):
// Reply mode units (ids):
// nl:intro the author's own paragraphs before the first per-file header
// nl:<file> the paragraphs under the header naming that patch file
// anchor the dated anchor line(s) opening the block
// RN reversal paragraphs ("R2. ...")
// <sec>:N numbered item N of section <sec> (camss, ov13b10, dts, x)
// <sec>:Na lettered sub-item a of item N
// x:N bullet N of the cross-series section
// refs the reference list
// whole:* fixed slots (WholeReply)
// A plain on-list mail (no "Dates: every" anchor) is partitioned
// generically instead (PartitionMail):
// hdr the header block (From/To/Cc/Subject/In-Reply-To/References)
// quote:N the N-th quoted block, with its "On ... wrote:" line
// para:N the N-th paragraph of our own text
// trailers a paragraph made only of "Tag-Name: value" lines
// Series mode units:
// cover:header the cover letter's header, cover:N its paragraph N
// pK:... as below, every hunk whole (no documentation split)
// whole:* fixed slots (WholeSeries)
// Commits mode units (ids):
// pK:subject commit K's subject
// pK:msg commit K's message body (before the trailers), when not empty
// pK:trailers commit K's trailer block, when not empty
@ -30,6 +52,17 @@ export namespace Crafter::Review::Inventory {
std::string_view id;
std::string_view description;
};
constexpr std::array<WholeSlot, 3> WholeReply = {{
{"whole:route", "the decision or route the mail recommends, and whether its consequences are carried through every item that depends on it"},
{"whole:asks", "the total set of actions the mail asks of the reader — is it legible as one list, free of contradictions between items"},
{"whole:absent", "what this reviewer would expect the mail to say and it does not — a precedent, a test, a dependency, a recipient, a question left open"},
}};
constexpr std::array<WholeSlot, 4> WholeSeries = {{
{"whole:order", "patch ordering, bisectability, declared dependencies and base"},
{"whole:cover-vs-patches", "the cover's claims against what the patches actually do, including testing statements"},
{"whole:recipients", "To/Cc against get_maintainer and the people who reviewed the same construct recently"},
{"whole:absent", "what this reviewer would expect the series to contain and it does not"},
}};
constexpr std::array<WholeSlot, 3> WholeCommits = {{
{"whole:order", "commit ordering and bisectability — each commit a self-contained step that leaves the repository consistent (no later commit in the range repairs an earlier one, nothing is used before the commit that adds it), the declared base"},
{"whole:absent", "what a reviewer of this repository expects the range to contain and it does not — a test, a version/changelog bump, a doc or packaging update, a migration for existing installs, a message for a body-less commit"},
@ -49,7 +82,18 @@ export namespace Crafter::Review::Inventory {
// Units ({id, lines, excerpt}), the whole slots not yet among them.
Json units;
};
Series PartitionSeries(const std::filesystem::path& dir, bool splitDocs = true);
// A format-patch export, a cover letter (0000-) as cover:header and
// cover:N paragraphs; splitDocs cuts a documentation hunk at its
// headings (the commits mode only); kernel words the per-patch slots
// as the series mode does (a patch in a tree, not a commit in a
// repository).
Series PartitionSeries(const std::filesystem::path& dir, bool splitDocs = true, bool kernel = false);
// A plain on-list mail: the header block, each quoted block with its
// attribution line, each paragraph of our own text, a trailer paragraph.
Series PartitionMail(std::string_view text);
// The camera review's shape when the text has its dated anchor
// ("Dates: every"), else PartitionMail.
Series PartitionReply(std::string_view text);
// A partition that broke the invariant.
class InvariantError : public std::runtime_error {
@ -63,10 +107,12 @@ export namespace Crafter::Review::Inventory {
Json out;
std::vector<std::string> lines;
};
// `artifact` is kept as it is spelled: the inventory records it so.
// `artifact` is kept as it is spelled: the inventory records it so. mode:
// reply (a draft file), series or commits (a dir of *.patch).
Built Build(std::string_view mode, std::string_view artifact);
// `crafter-review inventory commits <dir>`: the partition as indented
// JSON on stdout. Exit 1 on a broken invariant or a bad command line.
// `crafter-review inventory reply|series|commits <path>`: the partition
// as indented JSON on stdout. Exit 1 on a broken invariant or a bad
// command line.
std::int32_t Main(std::span<const std::string_view> args);
}

View file

@ -11,18 +11,32 @@ import :Json;
// builds, both kept as JSON with their keys in a fixed order: the dump,
// progress.json and the prompts are made from them.
export namespace Crafter::Review::Plan {
// The three modes: a mailing-list reply, a kernel patch series (the
// kernel modes, with the persona gate), and a commit range about to be
// pushed.
constexpr std::array<std::string_view, 3> Modes = {"reply", "series", "commits"};
bool IsKernelMode(std::string_view mode);
constexpr std::string_view FunctionalLens = "repo-functional.md";
// The persona lens of the kernel modes: one instance per reviewer
// (--reviewers), its whole pass gating its blocks, in the gate after
// every other.
constexpr std::string_view PersonaLens = "maintainer-persona.md";
constexpr std::string_view PersonaGate = "Gate 4 — Personas";
// The commit-truth lens (2026-10-31, the fp6 pipeline's repo-facts): the
// messages and the documentation of the range.
constexpr std::string_view FactsLens = "repo-facts.md";
// The reasoning effort when --effort is not given (2026-10-02: the push
// gates run at xhigh).
// The reasoning effort when --effort is not given: the kernel modes
// stay at max; the push gates run at xhigh (2026-10-02).
constexpr std::string_view Effort = "xhigh";
constexpr std::string_view KernelEffort = "max";
// Clean reads a code unit of a lens marked `reads` needs before its
// verdict is reused, one per round (--reads). 2026-10-06: a static lens
// is not deterministic — a second and third read of unchanged code still
// found defects; "3 is fine".
// found defects; "3 is fine". A reply has no code: its paragraphs are
// read once.
constexpr std::int32_t Reads = 3;
constexpr std::int32_t ReplyReads = 1;
std::int32_t ReadsFor(std::string_view mode);
// The key's last slot held the run's effort from 2026-09-22 to
// 2026-10-02; it is frozen at the value every stored verdict was built
// with, so a clean verdict bought at one effort is reused at any other
@ -44,6 +58,9 @@ export namespace Crafter::Review::Plan {
constexpr std::array<std::string_view, 1> BehaviourSlots = {"whole:behaviour"};
constexpr std::array<std::string_view, 1> CodeBlocks = {"code"};
constexpr std::array<std::string_view, 3> TruthBlocks = {"text", "doc", "slot"};
constexpr std::array<std::string_view, 2> TextSlotBlocks = {"text", "slot"};
constexpr std::array<std::string_view, 2> TextCodeBlocks = {"text", "code"};
constexpr std::array<std::string_view, 2> CodeSlotBlocks = {"code", "slot"};
// One row of LENSES. whole: "" (none), "first-repo" (the approach) or
// "functional" (the range exercised in the running application); every
@ -63,6 +80,10 @@ export namespace Crafter::Review::Plan {
// used the instrument is not evidence. perCommitSlots false: the lens
// judges the per-commit slots in its whole pass. env: the default
// environment; a verdict bought in any other carries it in its key.
// kernel: a lens of the kernel modes - its whole pass and its slot
// passes cover every whole:* slot of the artifact, a hunk of a
// documentation file is code to it, and its keys and prompts are the
// kernel pipeline's.
struct Lens {
std::string_view file;
std::int32_t gate;
@ -77,9 +98,11 @@ export namespace Crafter::Review::Plan {
bool empirical;
bool perCommitSlots;
std::string_view env;
bool kernel = false;
};
// In the order the dump and the prompts list them.
constexpr std::array<Lens, 5> Lenses = {{
// In the order the dump and the prompts list them: the push gates'
// lenses, then the kernel modes'.
constexpr std::array<Lens, 15> Lenses = {{
{"repo-better-solution.md", 1, "first-repo", ApproachSlots, CodeBlocks, "fable", true, true, true, "code", false, true, "none"},
{FunctionalLens, 2, "functional", BehaviourSlots, CodeBlocks, "opus", true, true, false, "functional", true, false, "browser"},
{"repo-bug-prover.md", 3, "", {}, CodeBlocks, "opus", true, false, true, "code", false, true, "none"},
@ -94,6 +117,23 @@ export namespace Crafter::Review::Plan {
// once the code they describe has settled. One read: its units are
// prose, keyed on their text and the code they describe.
{FactsLens, 4, "", ApproachSlots, TruthBlocks, "opus", true, false, false, "text", false, true, "none"},
// The kernel modes (reply, series). whole: "first" judges the series
// as an approach, "all" the consistency of the whole, "persona" reads
// it as one reviewer. better-solution's code blocks are reused and
// need the clean reads like the other code lenses (2026-10-06.7); its
// whole pass has no key: it runs on every hash against what is on
// the list today. A persona's block passes are reusable, keyed with
// the persona's name (2026-10-06.6); its whole pass runs every round.
{"better-solution.md", 1, "first", {}, CodeBlocks, "fable", true, false, true, "code", false, true, "none", true},
{"code-quality.md", 2, "", {}, CodeBlocks, "opus", true, false, true, "code", false, true, "none", true},
{"minimalism.md", 2, "", {}, CodeSlotBlocks, "opus", true, false, true, "code", false, true, "none", true},
{"bug-prover.md", 2, "", {}, CodeBlocks, "opus", true, false, true, "code", false, true, "none", true},
{"facts.md", 3, "", {}, TextSlotBlocks, "opus", true, false, false, "code", false, true, "none", true},
{"consistency.md", 3, "all", {}, TextCodeBlocks, "opus", false, false, false, "code", false, true, "none", true},
{"process-docs.md", 3, "", {}, TextSlotBlocks, "opus", true, false, false, "code", false, true, "none", true},
{"etiquette.md", 3, "", {}, TextSlotBlocks, "opus", true, false, false, "code", false, true, "none", true},
{"conciseness.md", 3, "", {}, TextSlotBlocks, "fable", true, false, false, "code", false, true, "none", true},
{PersonaLens, 4, "persona", {}, TextCodeBlocks, "fable", true, false, false, "code", false, true, "none", true},
}};
bool IsLens(std::string_view file);
// LENSES[file]; Dyn::Error (KeyError) for a file that is no lens.
@ -103,7 +143,12 @@ export namespace Crafter::Review::Plan {
std::string_view title;
std::vector<std::string_view> lenses;
};
const std::vector<Gate>& Gates();
// The gates of a mode: reply runs Gate 3 alone (facts, etiquette,
// consistency, conciseness); series Gate 1 (better-solution), Gate 2
// (code-quality, minimalism, bug-prover), Gate 3 (facts, consistency,
// process-docs, etiquette, conciseness); the persona gate follows them
// when reviewers are given. commits: the push gates.
const std::vector<Gate>& Gates(std::string_view mode = "commits");
// What a lens's processes get beyond the read-only tools, by the
// consumer's lenses.<name>.environment: mcp, the server rendered into
@ -155,6 +200,11 @@ export namespace Crafter::Review::Plan {
Budget BudgetFor(std::string_view kind, std::string_view unitKind, std::string_view lensFile);
std::string_view DefaultEffort();
std::string_view DefaultEffort(std::string_view mode);
// A text unit that is a bare section header of the camera review
// (`camss-milos`, `ov13b10-of`, `milos-dts-camss`, `across the three
// series`): nothing to judge, a mechanical n/a for every text lens.
bool IsBareHeader(const Json& unit, std::string_view text);
// "slot", "code" or "text".
std::string_view UnitKind(const Json& unit);
std::string_view UnitKindOf(std::string_view unitId);
@ -178,15 +228,18 @@ export namespace Crafter::Review::Plan {
std::string UnitText(std::span<const std::string> lines, const Json& unit);
// sha256 of the text's UTF-8, in hex.
std::string Sha(std::string_view text);
// lens_file.replace('.md', '')
std::string LensName(std::string_view lensFile);
// Every lens of the gates.
std::vector<std::string> ModeLenses();
// lens_file.replace('.md', ''); `persona:<name>` for a persona's
// instance.
std::string LensName(std::string_view lensFile, std::string_view persona = {});
// Every lens of the gates, and the persona lens in the kernel modes.
std::vector<std::string> ModeLenses(std::string_view mode = "commits");
// The gates with their lens files. off: the lenses the config turns off;
// only keeps (test runs), without drops (an owner ruling); an empty span
// filters nothing, and a gate left empty disappears.
std::vector<Gate> GatesFor(std::span<const std::string> only = {}, std::span<const std::string> without = {},
std::span<const std::string> off = {});
std::vector<Gate> GatesFor(std::string_view mode, std::span<const std::string> only, std::span<const std::string> without,
std::span<const std::string> off);
// The whole job (null when the lens has none) and the block jobs.
struct LensPlan {
@ -194,13 +247,16 @@ export namespace Crafter::Review::Plan {
std::vector<Json> blocks;
};
// unitsFilter: --units (empty: every unit); exclude: the functional
// lens's path globs.
// lens's path globs; persona: the reviewer a persona instance reads as;
// excludeUnits: the unit ids a persona's whole pass marked not-mine.
LensPlan PlanLens(std::string_view lensFile, const Json& units, std::span<const std::string> lines,
std::span<const std::string> unitsFilter = {}, std::span<const std::string> exclude = {});
std::span<const std::string> unitsFilter = {}, std::span<const std::string> exclude = {}, std::string_view persona = {},
std::span<const std::string> excludeUnits = {});
// The model is part of the key (a verdict bought from one model is not
// evidence for another); the effort is not. ident replaces the unit id for
// a unit that is keyed on its content. nullopt: the job is not reusable.
// a unit that is keyed on its content; a persona's block carries the
// persona's name. nullopt: the job is not reusable.
std::optional<std::string> ReuseKey(const Json& job, std::string_view lensFile, std::string_view unitText, std::vector<std::string> deps,
std::vector<std::string> inForceIds, std::string_view charterSha, std::int32_t promptVersion,
std::string_view ident = {});
@ -223,7 +279,10 @@ export namespace Crafter::Review::Plan {
std::vector<std::string> docs;
std::string message;
};
PatchParts PartsOf(std::string_view patchText);
// docFiles false (a kernel series): nothing is documentation - a .md or
// .rst hunk is part of the code of its patch, as the kernel lenses read
// it.
PatchParts PartsOf(std::string_view patchText, bool docFiles = true);
// What the code keys of a range are built from, by commit number K.
struct CommitCode {
@ -234,14 +293,18 @@ export namespace Crafter::Review::Plan {
std::string range;
std::map<std::int64_t, std::string> upto;
std::string base;
bool kernel = false;
};
CommitCode CommitCodeOf(const std::map<std::int64_t, std::string>& patches);
// kernel: the series mode's code - nothing is documentation, and the
// message of each patch is kept for its `absent` slot (a kernel reviewer
// looks there for a trailer or a test statement).
CommitCode CommitCodeOf(const std::map<std::int64_t, std::string>& patches, bool kernel = false);
// (ident, deps) of a code unit: a hunk or a file header is keyed on its
// file and its own normalised text, the two per-commit slots on that
// commit's position and code, the code of the whole range, its
// documentation files, and for `absent` whether it has a body. Every code
// unit depends on the base.
// documentation files, and for `absent` whether it has a body (a kernel
// series: its message). Every code unit depends on the base.
std::pair<std::string, std::vector<std::string>> CodeKeyParts(std::string_view unitId, const CommitCode& cc);
std::optional<std::string> CodeReuseKey(const Json& job, std::string_view lensFile, std::string_view unitText, const CommitCode& cc,
std::span<const std::string> inForceIds, std::string_view charterSha, std::int32_t promptVersion,
@ -278,6 +341,14 @@ export namespace Crafter::Review::Plan {
std::optional<std::string> WholeCodeKey(const Json& job, std::string_view lensFile, const CommitCode& cc, std::span<const std::string> inForceIds,
std::string_view charterSha, std::int32_t promptVersion, std::string_view environment = {});
// The dependencies of a text unit's verdict in the kernel modes: the
// URLs of every [n] it cites, the anchor text when it carries a dated or
// versioned statement (otherwise every anchor-date bump would invalidate
// every unit), and every R-unit it names verbatim. refs: the parsed
// reference table ({"n": url} rows).
std::vector<std::string> TextDeps(std::string_view unitText, const Json& units, std::span<const std::string> lines,
std::span<const std::pair<std::string, std::string>> refs, std::string_view anchorText);
// How many clean reads a stored result stands for: one per round that
// read its key clean.
std::int64_t ReadsOf(const Json& entry);

View file

@ -15,32 +15,57 @@ export namespace Crafter::Review::Prompt {
constexpr std::string_view SepRun = "\n\n==================== THIS RUN ====================\n\n";
extern const std::string_view GenericVocab;
// The kernel modes' vocabularies: every lens's, and a maintainer or
// recipient persona's.
extern const std::string_view GenericVocabKernel;
extern const std::string_view PersonaVocab;
// WHOLE_TEXT['first-repo'] and WHOLE_TEXT['functional'].
extern const std::string_view WholeFirstRepo;
extern const std::string_view WholeFunctional;
// WHOLE_TEXT['first'], ['all'] and ['persona']: the kernel modes' whole
// passes - the series as an approach, the consistency of the whole, the
// artifact read as one reviewer.
extern const std::string_view WholeFirst;
extern const std::string_view WholeAll;
extern const std::string_view WholePersona;
extern const std::string_view FunctionalBlockHint;
extern const std::string_view ShotRules;
// KIND_HINTS_REPO
extern const std::string_view HintOutside;
extern const std::string_view HintAbsent;
// KIND_HINTS: the kernel modes' hints - the anchor unit, the reference
// list, and the two per-patch slots.
extern const std::string_view HintAnchor;
extern const std::string_view HintRefs;
extern const std::string_view HintOutsideKernel;
extern const std::string_view HintAbsentKernel;
// The block hints of the commit-truth lens (2026-10-31): a message unit,
// and a unit of a documentation file.
// and a unit of a documentation file; and the kernel modes' text hint.
extern const std::string_view TextBlockHintRepo;
extern const std::string_view DocBlockHintRepo;
extern const std::string_view TextBlockHint;
// SLOT_DUTIES: what a slot pass of a lens covers beyond the slot's
// question; empty for every other lens and slot.
std::string_view SlotDuty(std::string_view lensFile, std::string_view unitId);
// _common-repo.md with the artifact kind filled in.
std::string SystemText();
// _common-repo.md with the artifact kind filled in; the kernel modes
// _common.md (a mailing-list reply, a patch series).
std::string SystemText(std::string_view mode = "commits");
// The lens's charter, read with universal newlines.
std::string Charter(std::string_view lensFile);
// INPUTS, the artifact with line numbers, the inventory. trees: as they
// are spelled to the lens; contextPaths may be empty.
// are spelled to the lens; contextPaths may be empty; a subsystem names
// the journal's ledger and captures the lenses may read.
std::string RunLayer(std::string_view artifactRel, std::string_view base, std::string_view date, std::span<const std::string> trees, std::string_view hash,
std::span<const std::string> lines, const Json& units, std::span<const std::string> contextPaths = {},
std::string_view repoName = "the repository");
std::string_view repoName = "the repository", std::string_view subsystem = {});
// The kernel modes' run layer: the journal's inputs, the trees, the
// reviewers with a persona, and the references digest after the
// inventory.
std::string RunLayerKernel(std::string_view mode, std::string_view artifactRel, std::string_view subsystem, std::string_view base, std::span<const std::string> reviewers,
std::string_view date, std::span<const std::string> trees, std::string_view hash, std::span<const std::string> lines, const Json& units,
std::string_view refDigest, std::span<const std::string> contextPaths = {});
// APPLICATION UNDER TEST: what the processes of a lens with the browser
// environment are told about the running application — from the
@ -63,18 +88,22 @@ export namespace Crafter::Review::Prompt {
std::string DbBlock(const Json& supabase, std::string_view sqlCommand, const std::optional<std::string>& dbUser, bool withApp = false,
const std::optional<std::string>& checked = std::nullopt, std::optional<bool> bypass = std::nullopt);
// The charter, the application and database blocks when there are
// any, the dispositions, the whole pass's summary.
// any, the dispositions, the whole pass's summary; for a persona first
// who they are and the calibration map (the first 6000 characters of
// their lore listing).
std::string LensLayer(std::string_view lensFile, std::string_view charterText, std::string_view dispositionsBlock, std::string_view app = {},
std::string_view wholeSummary = {}, std::string_view db = {});
std::string_view wholeSummary = {}, std::string_view db = {}, std::string_view persona = {}, std::string_view corpus = {});
// The scope of one pass: a whole pass's units, a slot's question, or a
// block's unit text.
// block's unit text. A kernel lens's job gets the kernel texts.
std::string Tail(const Json& job, const Json& units, std::span<const std::string> lines, std::string_view wholeSummary = {});
std::string BudgetLine(const Json& job);
// kernel: the kernel modes' line, which also says to read the prefetched
// raw files instead of fetching.
std::string BudgetLine(const Json& job, bool kernel = false);
// The appended system prompt of a pass: the common charter text, the run
// layer and the lens layer, byte-identical for every pass of a lens in a
// run.
std::string SystemPrompt(std::string_view systemText, std::string_view run, std::string_view lens);
// The stdin user message of a pass: only what differs between passes. A
// null job adds no budget line.
std::string UserPrompt(std::string_view tail, const Json& job = nullptr, std::string_view stack = {});
std::string UserPrompt(std::string_view tail, const Json& job = nullptr, std::string_view stack = {}, bool kernel = false);
}

View file

@ -0,0 +1,57 @@
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
export module Crafter.Review:Refs;
import std;
import :Json;
// The kernel modes' references: gate 0's prefetch of the lore messages an
// artifact cites (a cache addressed by message id, shared across hashes),
// the per-hash index, the digest the run layer carries, and a light persona
// corpus (a lore search listing). lore.kernel.org is reached only through
// `python3 utilities/lore.py` in the working directory, the one way past its
// proof-of-work wall; a fetch never raises - a failure is a `failed` index
// row the lenses are told about, never synthesized.
export namespace Crafter::Review::Refs {
// {reference: url}, in order of first appearance: the [n] lines of a
// reply's reference list ("1", "2", ...), else every lore URL in the text
// ("L1", "L2", ...).
using Table = std::vector<std::pair<std::string, std::string>>;
Table ParseRefs(std::string_view text);
// The url reference n names, when the table is a numbered list.
std::optional<std::string> UrlOf(const Table& refs, std::int64_t n);
// The message id a lore URL names: https?://lore.kernel.org/(r|all|<list>)/<msgid>/
std::optional<std::string> MsgidOf(std::string_view url);
// The file a message id is cached under: every character outside
// [A-Za-z0-9._@-] an underscore, 200 characters at most.
std::string SafeName(std::string_view msgid);
// From, Date and Subject of a raw mail's header block, the first of each.
std::map<std::string, std::string> Headers(std::string_view raw);
using Log = std::function<void(const std::string&)>;
// (status, error) of one fetch: "fetched" with the bytes written to
// `path`, or "failed" with the last error. The test seam: a fetcher set
// here replaces the lore.py subprocess.
using Fetcher = std::function<std::pair<std::string, std::string>(const std::filesystem::path& repo, const std::string& url, const std::filesystem::path& path)>;
void SetFetcher(Fetcher fetcher);
// `python3 utilities/lore.py fetch <url>/raw` in `repo`, three tries.
std::pair<std::string, std::string> Fetch(const std::filesystem::path& repo, const std::string& url, const std::filesystem::path& path);
// The index of every reference - {n: {url, msgid, path, status, error?,
// From?, Date?, Subject?, bytes?}} - written to indexPath, the raw mails
// under refsDir; `workers` fetches at once.
Json Prefetch(const std::filesystem::path& repo, const Table& refs, const std::filesystem::path& refsDir, const std::filesystem::path& indexPath, const Log& log,
std::int32_t workers = 4);
// REFERENCES prefetched: one line per entry, numbered ones first.
std::string Digest(const Json& index);
// A lore search listing (subjects, dates, links) of the persona's own
// mail since `since` (YYYYMMDD): the calibration map, cached under
// refsDir as persona-<slug>.txt. (nullopt, "") when lore has none - an
// empty corpus is never cached, so the next run retries.
using Searcher = std::function<std::pair<std::string, std::string>(const std::filesystem::path& repo, const std::string& query)>;
void SetSearcher(Searcher searcher);
std::pair<std::optional<std::filesystem::path>, std::string> PersonaCorpus(const std::filesystem::path& repo, std::string_view name, const std::filesystem::path& refsDir,
std::string_view since, const Log& log);
// YYYYMMDD of the day 365 days before an ISO date.
std::string SinceOf(std::string_view isoDate);
}

View file

@ -11,18 +11,23 @@ import :Memory;
import :PgWire;
import :State;
import :Platform;
import :Refs;
// Run: the driver for a commit range about to be pushed
// (process/prepush.md). One claude process per inventory unit per lens,
// whole passes where a lens has one (each gating its block passes),
// fail-closed coverage per unit, the matrix, per-unit reuse across hashes, a
// seat pool.
// (process/prepush.md), and for the kernel modes - a mailing-list reply or
// a patch series about to be sent. One claude process per inventory unit
// per lens, whole passes where a lens has one (each gating its block
// passes), fail-closed coverage per unit, the matrix, per-unit reuse across
// hashes, a seat pool.
//
// Gates: 1 repo-better-solution (approach) -> 2 repo-functional (the range
// exercised in the running application) -> 3 repo-bug-prover and
// repo-security -> 4 the owner. SEND-READY = push-ready. Exit codes: 0
// SEND-READY, 1 FIX FIRST, 2 INCOMPLETE / PARTIAL / CONFIRMING (gates clean,
// code units still owe clean reads - run the same line again), 3 preflight.
// Gates (commits): 1 repo-better-solution (approach) -> 2 repo-functional
// (the range exercised in the running application) -> 3 repo-bug-prover
// and repo-security -> 4 the owner. SEND-READY = push-ready. The kernel
// modes run their own gates (Plan::Gates), then the persona gate for every
// reviewer given; gates clean without a persona pass is GATES CLEAN, not
// SEND-READY. Exit codes: 0 SEND-READY, 1 FIX FIRST, 2 INCOMPLETE / PARTIAL
// / GATES CLEAN / CONFIRMING (gates clean, code units still owe clean reads
// - run the same line again), 3 preflight.
//
// A Run is built empty and filled field by field, as the tests fill one, so
// its attributes are public; Main builds and runs the real one.
@ -34,8 +39,15 @@ export namespace Crafter::Review::Run {
struct Arguments {
std::optional<std::string> config;
std::string mode = "commits";
// The journal subsystem: required in the kernel modes (its ledger,
// captures and threads are the lenses' evidence); optional in
// commits mode.
std::optional<std::string> subsystem;
std::string artifact;
std::optional<std::string> hash;
// The reviewers a persona reads as, comma-separated (the kernel
// modes).
std::string reviewers;
std::optional<std::string> dispositions;
std::string base;
std::string date;
@ -55,6 +67,9 @@ export namespace Crafter::Review::Run {
std::int64_t reads = Plan::Reads;
bool noReuse = false;
bool noSalvage = false;
// pruned: a persona's block passes skip the units its whole pass
// marked not-mine; all: every unit.
std::string personaBlocks = "pruned";
bool dryRun = false;
std::optional<std::string> claudeBin;
std::string models;
@ -154,6 +169,11 @@ export namespace Crafter::Review::Run {
Json PartFrom(const Json& job, const Json& rec, Json raw);
static Json DryPart(const Json& job);
std::vector<Json> RunGate(std::string_view title, std::vector<std::string> lensFiles);
// (lens file, persona) per lens instance: a persona's instance reads
// as that reviewer.
std::vector<Json> RunGate(std::string_view title, std::vector<std::pair<std::string, std::string>> specs);
// The kernel modes (reply, series).
bool Kernel() const;
std::unique_ptr<Pool::Admission> Admission(std::span<const std::string> lensFiles);
std::optional<Json> Resumable(const std::string& name, const std::string& lensFile, std::string_view title);
void Checkpoint(std::string_view title);
@ -191,6 +211,16 @@ export namespace Crafter::Review::Run {
std::map<std::string, std::int64_t> lensLayersBytes;
std::vector<Json> jobsRun;
std::map<std::string, std::string> summaries;
// The kernel modes: the reviewers with a persona, their calibration
// maps, the references the artifact cites and their digest, the
// anchor unit's text, and whether the persona gate passed.
std::vector<std::string> reviewers;
std::map<std::string, std::string> corpora;
Refs::Table refUrls;
Json refIndex = Json::object();
std::string refDigest;
std::string anchorText;
bool personaRan = false;
std::optional<std::string> abort;
Pool::Scheduler* sched = nullptr;
std::optional<Plan::CommitCode> commitCode;

View file

@ -10,23 +10,38 @@ import :Json;
// lens's vocabulary, so the tool block ahead of the user message is
// byte-identical across lenses and the prompt cache is shared. The bytes are
// fixed: SchemaJson() is the schema in DumpCompactSorted's form, and the
// goldens hold the descriptions.
// goldens hold the descriptions. The kernel modes have a schema of their
// own: their note kinds, the persona vocabulary, and the whole pass's
// predicted review and not-mine list.
export namespace Crafter::Review::Schema {
constexpr std::array<std::string_view, 10> NoteKinds = {
"reserve-answer", "invariant", "pre-existing", "residual-risk", "hold", "info", "immaterial",
"send-time-check", "refutation-verified", "unverified",
};
constexpr std::array<std::string_view, 4> CoverageVerdicts = {"clear", "defect", "immaterial", "n/a"};
constexpr std::array<std::string_view, 1> CoverageBlocking = {"defect"};
constexpr std::array<std::string_view, 2> CoverageUnowned = {"n/a", "MISSING"};
constexpr std::array<std::string_view, 10> KernelNoteKinds = {
"reserve-answer", "ledger-row", "pre-existing", "residual-risk", "hold", "info", "immaterial",
"disposition-review", "send-time-check", "refutation-verified",
};
// The vocabulary of a maintainer or recipient persona.
constexpr std::array<std::string_view, 9> PersonaVerdicts = {"would-require", "no-comment", "would-object", "incomplete", "not-mine", "can-act", "ambiguous", "would-push-back", "already-known"};
constexpr std::array<std::string_view, 4> GenericVerdicts = {"clear", "defect", "immaterial", "n/a"};
constexpr std::array<std::string_view, 4> CoverageVerdicts = GenericVerdicts;
// The union, for the kernel modes' schema.
constexpr std::array<std::string_view, 13> KernelCoverageVerdicts = {"would-require", "no-comment", "would-object", "incomplete", "not-mine", "can-act", "ambiguous", "would-push-back", "already-known",
"clear", "defect", "immaterial", "n/a"};
// Blocking and unowned, across both vocabularies: a lens that speaks the
// generic one never emits a persona verdict, so the union judges either.
constexpr std::array<std::string_view, 5> CoverageBlocking = {"would-object", "incomplete", "ambiguous", "would-push-back", "defect"};
constexpr std::array<std::string_view, 3> CoverageUnowned = {"not-mine", "n/a", "MISSING"};
constexpr std::array<std::string_view, 2> VerdictEnum = {"FIX FIRST", "SEND-READY"};
Json Finding();
Json Note();
Json Coverage();
Json BlockSchema();
Json WholeSchema();
// kernel: the kernel modes' texts and vocabulary.
Json Finding(bool kernel = false);
Json Note(bool kernel = false);
Json Coverage(bool kernel = false);
Json BlockSchema(bool kernel = false);
Json WholeSchema(bool kernel = false);
// The schema of `kind` ("block", anything else the whole one) as it goes
// to claude.
std::string SchemaJson(std::string_view kind);
// to claude, for a mode (reply, series: the kernel schema).
std::string SchemaJson(std::string_view kind, std::string_view mode = "commits");
}

View file

@ -20,16 +20,17 @@ export namespace Crafter::Review::State {
// win over sets; an entry of `sets` that is not a whole pass gets its
// clean-read count from the FILE's previous entry (or the one-read
// synthesis of an entry stored before the count was kept) plus `read`
// unless that (hash, round) is counted already. Garbage on disk is the
// empty state. `sets` is an object; a null `read` counts nothing.
Json MergeState(const Json& disk, const Json& sets, std::span<const std::string> drops = {}, const Json& read = nullptr);
// unless that (hash, round) is counted already; `persona` sets
// personaPass when given. Garbage on disk is the empty state. `sets` is
// an object; a null `read` counts nothing.
Json MergeState(const Json& disk, const Json& sets, std::span<const std::string> drops = {}, const Json& read = nullptr, const Json& persona = nullptr);
// The whole state written as it is, through `<path>.tmp`.
void SaveWhole(const std::filesystem::path& path, const Json& state);
// Merge-on-write: the file re-read under `<path>.lock`, this run's deltas
// applied (MergeState), written atomically; returns the merged state. Two
// drivers on one gate-state dir would otherwise overwrite each other's
// stores, and a --no-reuse run would wipe the file from its empty object.
Json SaveState(const std::filesystem::path& path, const Json& sets, std::span<const std::string> drops = {}, const Json& read = nullptr);
Json SaveState(const std::filesystem::path& path, const Json& sets, std::span<const std::string> drops = {}, const Json& read = nullptr, const Json& persona = nullptr);
// ---- one full run per hash -------------------------------------------------
// Another full run holds this hash's lock; what() names the holder.

View file

@ -22,6 +22,7 @@ export import :Normalize;
export import :PgWire;
export import :Totp;
export import :Stream;
export import :Refs;
export import :Prompt;
export import :Config;
export import :Memory;