feat(config): modes - a mode is an artifact kind, the lenses it runs and its effort

The config defines the modes (modes.<name>): the kind of artifact the mode
reviews - reply, series or commits, the three shipped partitions; a kind
is a mode of its own name - the lenses it runs, in its kind's gate order,
and the effort it runs at, else the kind's default (max for a reply or a
series, xhigh for a commit range). `run --mode` names any of them, the
dump records mode and kind, and everything that decided by the mode's
name - the partition and hash, the prompts, the schemas, the disposition
and reuse-key rules, what the CLI requires - decides by the kind.

Every lens stays off until a mode lists it; lenses.<name>.run is retired
with a warning that says so, and a lens of another kind, a name that is
no lens, a lens listed twice and an unknown effort are config problems.
The defaults run no lens; the template ships the three kinds as modes
with every lens of their kind at the kind's effort, to trim (the owner's
ruling of 2026-11-01), and init says so as it writes it - a kept config
is the repository's own, and only one that lists no lens gets a step.

The readers of the retired key follow: the CI smoke config, the shipped
skill, the procedure (its description of the config as well as its
mechanics), the manifest template, the run's messages and the comments
that named the three modes. The run lock, the run numbers and the dump
names are the kind's, so one full run per hash holds across the modes
of a kind and their rounds count on together; the dump says which mode
ran. The database broker starts for a lens the mode runs, as every other
reader of the lenses off reads them. A named mode runs through the CLI
in the kernel-modes test.
This commit is contained in:
Jorijn van der Graaf 2026-11-01 01:31:40 +01:00
commit d5e54ffeb3
Signed by: jorijnvdgraaf
GPG key ID: 2937E59CDCC1BCFB
25 changed files with 1277 additions and 711 deletions

View file

@ -40,8 +40,7 @@ export namespace Crafter::Review::Config {
// repository's git identity.
std::vector<std::string> Authors(const Json& config, const std::filesystem::path& repo);
std::string GitUserEmail(const std::filesystem::path& repo);
// The lens files the config turns off (lenses.<name>.run = false), in gate
// order.
// The lens files no mode lists (what runs nowhere: the slots and init).
std::vector<std::string> LensesOff(const Json& config);
// {lens file: environment} for every lens of the table, as the config
// holds it (validated elsewhere).
@ -66,6 +65,23 @@ export namespace Crafter::Review::Config {
// What is wrong with those files: one missing, or one
// Prompt::ReadSection cannot use.
std::vector<std::string> SectionProblems(const Json& config, const std::filesystem::path& repo);
// A mode as the config defines it (modes.<name>, 2026-11-01): the
// artifact kind it reviews, the lens files it runs in the table's order,
// and the effort it runs at when set. nullopt: no such mode, or one
// whose kind or lens list cannot be read; lenient on the rest Validate
// refuses (a lens of another kind dropped, an unknown effort kept),
// which no command reaches: each stops on the config's problems first.
struct Mode {
std::string name;
std::string kind;
std::vector<std::string> lensFiles;
std::optional<std::string> effort;
};
std::optional<Mode> ModeOf(const Json& config, std::string_view name);
// The configured mode names, in the config's order.
std::vector<std::string> ModeNames(const Json& config);
// The lens files of the mode's kind that the mode does not list.
std::vector<std::string> LensesOff(const Json& config, std::string_view mode);
// The example a consumer starts from (process/templates/config.json is
// this, rendered).
Json Example();

View file

@ -11,11 +11,11 @@ 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);
// The artifact kinds. A mode (the config's modes.<name>) is a kind plus
// the lenses it runs and the effort it runs at; a kind is a mode of its
// own name.
constexpr std::array<std::string_view, 3> Kinds = {"reply", "series", "commits"};
bool IsKernelKind(std::string_view kind);
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
@ -29,6 +29,8 @@ export namespace Crafter::Review::Plan {
// stay at max; the push gates run at xhigh (2026-10-02).
constexpr std::string_view Effort = "xhigh";
constexpr std::string_view KernelEffort = "max";
// The efforts a mode may run at (modes.<name>.effort).
constexpr std::array<std::string_view, 5> Efforts = {"low", "medium", "high", "xhigh", "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
@ -36,7 +38,7 @@ export namespace Crafter::Review::Plan {
// read once.
constexpr std::int32_t Reads = 3;
constexpr std::int32_t ReplyReads = 1;
std::int32_t ReadsFor(std::string_view mode);
std::int32_t ReadsFor(std::string_view kind);
// 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
@ -148,7 +150,7 @@ export namespace Crafter::Review::Plan {
// (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");
const std::vector<Gate>& Gates(std::string_view kind = "commits");
// What a lens's processes get beyond the read-only tools, by the
// consumer's lenses.<name>.environment: mcp, the server rendered into
@ -200,7 +202,7 @@ 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);
std::string_view DefaultEffort(std::string_view kind);
// 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.
@ -232,13 +234,13 @@ export namespace Crafter::Review::Plan {
// 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");
std::vector<std::string> KindLenses(std::string_view kind = "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::vector<Gate> GatesFor(std::string_view kind, 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.

View file

@ -38,7 +38,11 @@ export namespace Crafter::Review::Run {
// this order.
struct Arguments {
std::optional<std::string> config;
// The mode's name (the config's modes.<name>) and its artifact
// kind (reply, series, commits): the kind decides the partition, the
// prompts, the schemas and the rules; the mode the lenses and effort.
std::string mode = "commits";
std::string kind = "commits";
// The journal subsystem: required in the kernel modes (its ledger,
// captures and threads are the lenses' evidence); optional in
// commits mode.

View file

@ -43,7 +43,7 @@ export namespace Crafter::Review::State {
Json ReadLockInfo(const std::filesystem::path& path);
std::string Describe(const Json& info);
// An exclusive, non-blocking flock on gate-state/.run-<mode>-<hash8>.lock,
// An exclusive, non-blocking flock on gate-state/.run-<kind>-<hash8>.lock,
// kept for the life of the process: the kernel releases it when the
// holder dies, and no child inherits it. The file is never unlinked: an
// unlink and re-create would hand two drivers one hash.