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

@ -87,12 +87,23 @@ echo '{"type":"result","subtype":"success","is_error":false,"result":"ok","total
const Process::Captured first = Self({"init"}, r);
const std::string skill = Read(r / ".claude" / "skills" / "prepush" / "SKILL.md");
const std::size_t frontmatter = shipped.find("\n---\n", 3) + 5;
Check("init: the config from the template (every lens off), the artifact and worktree dirs ignored, the skill with init's marker after its frontmatter, the next steps (turn lenses on; no browser line while none drives the application)",
Check("init: the config from the template (the three kinds as modes), the artifact and worktree dirs ignored, the skill with init's marker after its frontmatter, the next steps (trim each mode's lenses; the browser line, since the template lists the functional lens)",
first.returnCode == 0 && Read(r / ".review-gates" / "config.json") == Read(checkout / "process" / "templates" / "config.json")
&& Read(r / ".gitignore") == ".review-gates/push/\n.review-gates/trees/\n" && skill.starts_with(shipped.substr(0, frontmatter))
&& skill.substr(frontmatter).starts_with(marker) && skill.ends_with(shipped.substr(frontmatter)) && first.out.contains(" config - until it reports no problem")
&& first.out.contains("turn lenses on in .review-gates/config.json") && !first.out.contains(" browser install - "),
&& first.out.contains("trim each to what this repository runs") && first.out.contains(" browser install - "),
first.out + first.err);
// A kept config has no trim step (that line is the template's own,
// printed as init writes it); one that lists no lens is told to list
// them.
Write(r / ".review-gates" / "config.json", R"({"modes": {"commits": {"lenses": ["repo-facts"]}}})");
const Process::Captured listed = Self({"init"}, r);
Write(r / ".review-gates" / "config.json", R"({"base": "origin/dev"})");
const Process::Captured unlisted = Self({"init"}, r);
Check("init on a kept config: one whose modes list lenses gets no lens line; one that lists none is told to list them",
listed.returnCode == 0 && listed.out.contains("kept .review-gates/config.json") && !listed.out.contains("trim ") && !listed.out.contains("list the lenses")
&& unlisted.returnCode == 0 && unlisted.out.contains("list the lenses each mode runs in .review-gates/config.json (modes.<mode>.lenses) - every lens is off until a mode lists it"),
listed.out + unlisted.out);
Write(r / ".review-gates" / "config.json", R"({"base": "origin/dev"})");
Write(r / ".gitignore", "node_modules/\n/.review-gates/push\n");
Write(r / ".claude" / "skills" / "prepush" / "SKILL.md", std::format("---\nname: prepush\n---\n{} 0.0.1: old -->\nold text\n", marker));

View file

@ -122,7 +122,7 @@ int main() {
}
using GateList = std::vector<std::pair<std::string, std::vector<std::string>>>;
Check("mode_lenses = the five; --only keeps, --without drops, an emptied gate disappears; Gate 3 with one of its lenses dropped keeps the other",
Set(Plan::ModeLenses()) == lensSet && Gates(Plan::GatesFor(onlyFn)) == GateList{{"Gate 2 — Functional", {Fn}}}
Set(Plan::KindLenses()) == lensSet && Gates(Plan::GatesFor(onlyFn)) == GateList{{"Gate 2 — Functional", {Fn}}}
&& Titles(Plan::GatesFor({}, onlyFn)) == std::vector<std::string>{"Gate 1 — Approach", "Gate 3 — Proof", "Gate 4 — Commit truth"} && Plan::GatesFor(onlyFn, onlyFn).empty()
&& Gates(Plan::GatesFor({}, security))[2] == std::pair<std::string, std::vector<std::string>>{"Gate 3 — Proof", {"repo-bug-prover.md"}}
&& Gates(Plan::GatesFor(security)) == GateList{{"Gate 3 — Proof", {"repo-security.md"}}});
@ -135,7 +135,7 @@ int main() {
&& Gates(Plan::GatesFor(onlyFacts)) == GateList{{"Gate 4 — Commit truth", {Facts}}} && Titles(Plan::GatesFor({}, {}, onlyFacts)).size() == 3);
const std::vector<std::string> allLenses(lensSet.begin(), lensSet.end());
const std::set<std::string> textAndDoc = {"p1:subject", "p1:diffstat", "p2:subject", "p2:msg", "p2:diffstat", "p2:README.md#hdr", "p2:README.md#1"};
const std::set<std::string> nrAll = Set(Plan::NotReviewed(units, lines, Plan::ModeLenses()));
const std::set<std::string> nrAll = Set(Plan::NotReviewed(units, lines, Plan::KindLenses()));
std::vector<std::string> withoutFacts;
for (const Plan::Lens& lens : Plan::Lenses) {
if (lens.kernel) continue;

View file

@ -42,21 +42,21 @@ int main() {
fs::create_directories(td);
auto load = [&] { return Config::Load(td); };
Config::Loaded loaded = load();
auto noLensOn = [](const std::vector<std::string>& problems) { return problems.size() == 1 && problems[0].starts_with("lenses: no lens is on"); };
auto noLensOn = [](const std::vector<std::string>& problems) { return problems.size() == 1 && problems[0].starts_with("modes: no mode lists a lens"); };
Check("no file: the defaults, _path None, and the one problem of a repository that turns no lens on (2026-10-31)", noLensOn(loaded.problems) && loaded.config.at("aiTrailers") == "forbid"
&& loaded.config.at("functional").at("appUrl") == "http://localhost:8080" && loaded.config.at("_path").is_null(), Show(loaded.problems));
fs::create_directories(td / ".review-gates");
const fs::path p = td / ".review-gates" / "config.json";
auto put = [&](const Json& value) { std::ofstream(p, std::ios::binary) << DumpSpaced(value); };
// A config under test turns the bug-prover on unless it says which lenses
// run itself: every lens is off by default, and a config with none on is
// a problem of its own (checked below).
// A config under test lists the bug-prover in the commits mode unless it
// defines modes itself: every lens is off until a mode lists it, and a
// config with none listed is a problem of its own (checked below).
auto bad = [&](Json value) {
if (!value.contains("lenses")) value["lenses"] = {{"repo-bug-prover", {{"run", true}}}};
if (!value.contains("modes")) value["modes"] = {{"commits", {{"lenses", {"repo-bug-prover"}}}}};
put(value);
return load().problems;
};
put({{"base", "origin/dev"}, {"aiTrailers", "allow"}, {"allowDirty", {"supabase/config.toml"}}, {"lenses", {{"repo-bug-prover", {{"run", true}}}}},
put({{"base", "origin/dev"}, {"aiTrailers", "allow"}, {"allowDirty", {"supabase/config.toml"}}, {"modes", {{"commits", {{"lenses", {"repo-bug-prover"}}}}}},
{"functional", {{"exclude", {"**/*.test.ts"}}, {"readyPath", "/health"}, {"extraTools", {"Bash(redis-cli:*)", "mcp__x__y"}}}}});
loaded = load();
Check("the file merges over the defaults, nested too", loaded.problems.empty() && loaded.config.at("base") == "origin/dev" && loaded.config.at("aiTrailers") == "allow"
@ -65,7 +65,7 @@ int main() {
Check("rangeFrom 'base' without a base branch is a problem; 'sideways' is one too", Any(bad({{"rangeFrom", "base"}}), "needs a base")
&& Any(bad({{"rangeFrom", "sideways"}, {"base", "origin/dev"}}), "rangeFrom must be"));
std::vector<std::string> problems = bad({{"bogus", 1}, {"functional", {{"nope", 2}, {"appUrl", "ftp://x"}}}, {"aiTrailers", "maybe"}, {"authors", {"nomail"}},
{"lenses", {{"repo-functional", {{"run", true}}}}}});
{"modes", {{"commits", {{"lenses", {"repo-functional"}}}}}}});
Check("unknown keys and bad values are each a problem", problems.size() == 5 && Any(problems, "bogus") && Any(problems, "functional.nope") && Any(problems, "appUrl")
&& Any(problems, "aiTrailers") && Any(problems, "authors"), Show(problems));
problems = bad({{"functional", {{"extraTools", {"rm -rf /"}}, {"mcp", {{"args", "x"}}}}}});
@ -126,15 +126,14 @@ int main() {
Check("--config to a missing file is a problem", Any(Config::Load(td, td / "none.json").problems, "no such file"));
put(Json::object());
loaded = load();
bool noneRun = true;
for (const Json& entry : loaded.config.at("lenses")) noneRun = noneRun && entry.at("run") == false;
std::vector<std::string> everyLens;
for (const Plan::Lens& lens : Plan::Lenses) everyLens.emplace_back(lens.file);
bool defaultsMatch = true;
std::set<std::string> names;
for (const Plan::Lens& lens : Plan::Lenses) {
names.insert(Plan::LensName(lens.file));
defaultsMatch = defaultsMatch && Config::Defaults().at("lenses").at(Plan::LensName(lens.file)).at("environment") == lens.env;
const Json entry = Config::Defaults().at("lenses").at(Plan::LensName(lens.file));
defaultsMatch = defaultsMatch && entry.at("environment") == lens.env && !entry.contains("run");
}
const Json defaults = Config::Defaults();
std::set<std::string> defaultNames;
@ -144,31 +143,59 @@ int main() {
for (const std::string_view lf : {"better-solution.md", "code-quality.md", "minimalism.md", "bug-prover.md", "facts.md", "consistency.md", "process-docs.md", "etiquette.md", "conciseness.md", "maintainer-persona.md"}) {
environments[std::string(lf)] = "none";
}
Check("lenses: every lens is off by default (2026-10-31, opt-in only) with plan.LENSES' env - none/browser/none/none/none - and a config that turns none on has the one problem that says so; the table names every lens of plan.LENSES",
noLensOn(loaded.problems) && noneRun && Config::LensesOff(loaded.config) == everyLens && Dyn::Equal(Config::Environments(loaded.config), environments) && defaultNames == names && defaultsMatch,
Check("modes: the defaults are the three kinds as modes of their own name with no lens and no effort of their own; every lens is off until a mode lists it (2026-10-31, opt-in only) and the one problem names every lens; lenses hold the environment alone - none/browser/none/none/none",
noLensOn(loaded.problems) && loaded.problems[0].contains("repo-facts") && loaded.problems[0].contains("maintainer-persona") && Config::LensesOff(loaded.config) == everyLens
&& Dyn::Equal(Config::Environments(loaded.config), environments) && defaultNames == names && defaultsMatch && defaults.at("modes").size() == 3
&& Dyn::Equal(defaults.at("modes").at("reply"), {{"kind", "reply"}, {"lenses", Json::array()}, {"effort", nullptr}})
&& Config::ModeNames(loaded.config) == std::vector<std::string>{"reply", "series", "commits"} && Config::ModeOf(loaded.config, "commits")->lensFiles.empty()
&& Config::ModeOf(loaded.config, "commits")->kind == "commits" && !Config::ModeOf(loaded.config, "nope"),
Show(loaded.problems));
put({{"lenses", {{"repo-functional", {{"run", false}}}, {"repo-bug-prover", {{"run", true}, {"environment", "browser"}}}}}});
put({{"modes", {{"commits", {{"lenses", {"repo-bug-prover"}}}}}}, {"lenses", {{"repo-bug-prover", {{"environment", "browser"}}}}}});
loaded = load();
std::vector<std::string> allButProver;
for (const Plan::Lens& lens : Plan::Lenses) {
if (lens.file != "repo-bug-prover.md") allButProver.emplace_back(lens.file);
}
Check("lenses: run=true merges and every lens not turned on is listed off in gate order; a static lens may take the browser", loaded.problems.empty()
&& Config::LensesOff(loaded.config) == allButProver && Config::Environments(loaded.config).at("repo-bug-prover.md") == "browser"
&& loaded.config.at("lenses").at("repo-functional").at("environment") == "browser", Show(loaded.problems));
problems = bad({{"lenses", {{"nope", {{"run", true}}}, {"repo-better-solution", {{"run", "yes"}, {"environment", "http"}}}, {"repo-bug-prover", {{"run", true}}}}}});
Check("lenses: an unknown lens is an unknown key; run must be a bool; environment must be one of none, browser, supabase, browser+supabase", problems.size() == 3
&& Any(problems, "lenses.nope") && Any(problems, "run must be") && Any(problems, "one of none, browser, supabase, browser+supabase"), Show(problems));
problems = bad({{"lenses", {{"repo-functional", {{"run", true}, {"environment", "none"}}}}}});
Check("the functional lens running with no application under test is a problem; off with none is fine; an unknown environment is reported once",
Check("modes: a kind's mode lists its lenses; the lenses no mode lists, and the kind's lenses the mode leaves out, come in table order; a static lens may take the browser", loaded.problems.empty()
&& Config::LensesOff(loaded.config) == allButProver
&& Config::LensesOff(loaded.config, "commits") == std::vector<std::string>{"repo-better-solution.md", "repo-functional.md", "repo-security.md", "repo-facts.md"}
&& Config::ModeOf(loaded.config, "commits")->lensFiles == std::vector<std::string>{"repo-bug-prover.md"} && !Config::ModeOf(loaded.config, "commits")->effort
&& Config::Environments(loaded.config).at("repo-bug-prover.md") == "browser" && loaded.config.at("lenses").at("repo-functional").at("environment") == "browser", Show(loaded.problems));
problems = bad({{"modes", {{"quick", {{"lenses", {"repo-bug-prover"}}}}}}});
Check("modes: a mode of the repository's own name needs its kind (its lenses cannot be checked without one, so nothing is listed)", problems.size() == 2
&& Any(problems, "modes.quick.kind must name the artifact kind") && Any(problems, "no mode lists a lens"), Show(problems));
put({{"modes", {{"quick", {{"kind", "commits"}, {"lenses", {"repo-facts", "repo-bug-prover"}}, {"effort", "high"}}}}}});
loaded = load();
const std::optional<Config::Mode> quick = Config::ModeOf(loaded.config, "quick");
Check("modes: a named mode is a kind, the lenses it lists in the table's order and its effort; the kinds' own modes stay beside it with no lens", loaded.problems.empty() && quick
&& quick->kind == "commits" && quick->lensFiles == std::vector<std::string>{"repo-bug-prover.md", "repo-facts.md"} && quick->effort == "high"
&& Config::ModeNames(loaded.config) == std::vector<std::string>{"reply", "series", "commits", "quick"}
&& Config::LensesOff(loaded.config, "quick") == std::vector<std::string>{"repo-better-solution.md", "repo-functional.md", "repo-security.md"}
&& Config::ModeOf(loaded.config, "commits")->lensFiles.empty() && Config::LensesOff(loaded.config, "nope").empty(), Show(loaded.problems));
problems = bad({{"modes", {{"commits", {{"kind", "series"}, {"lenses", {"repo-bug-prover", "facts", "nope", "repo-bug-prover"}}, {"effort", "ultra"}}}}}});
Check("modes: a kind's own mode is that kind; a lens of another kind, a name that is no lens and a lens listed twice are each a problem; the effort is one of the five", problems.size() == 5
&& Any(problems, "modes.commits.kind must be commits") && Any(problems, "facts is no lens of a commits") && Any(problems, "nope is no lens of a commits")
&& Any(problems, "repo-bug-prover is listed twice") && Any(problems, "modes.commits.effort must be one of low, medium, high, xhigh, max"), Show(problems));
Check("modes: not an object, a mode that is no object, lenses that are no list", Any(bad({{"modes", 5}}), "modes must be an object")
&& Any(bad({{"modes", {{"commits", 5}}}}), "modes.commits must be an object") && Any(bad({{"modes", {{"commits", {{"lenses", "repo-bug-prover"}}}}}}), "modes.commits.lenses must be a list of lens names"));
put({{"lenses", {{"repo-bug-prover", {{"run", true}}}}}});
loaded = load();
Check("lenses.<name>.run is retired: a warning that says a mode lists what runs, and nothing runs until one does", loaded.config.at("_warnings").size() == 1
&& Dyn::Str(loaded.config.at("_warnings").at(0)).contains("lenses.repo-bug-prover.run is no longer read") && noLensOn(loaded.problems), Show(loaded.problems));
problems = bad({{"lenses", {{"nope", {{"environment", "none"}}}, {"repo-better-solution", {{"environment", "http"}}}}}});
Check("lenses: an unknown lens is an unknown key; environment must be one of none, browser, supabase, browser+supabase", problems.size() == 2 && Any(problems, "lenses.nope")
&& Any(problems, "one of none, browser, supabase, browser+supabase"), Show(problems));
problems = bad({{"modes", {{"commits", {{"lenses", {"repo-functional"}}}}}}, {"lenses", {{"repo-functional", {{"environment", "none"}}}}}});
Check("the functional lens listed by a mode with no application under test is a problem; unlisted with none is fine; an unknown environment is reported once",
problems.size() == 1 && problems[0].contains("empirical")
&& bad({{"lenses", {{"repo-functional", {{"run", false}, {"environment", "none"}}}, {"repo-bug-prover", {{"run", true}}}}}}).empty()
&& bad({{"lenses", {{"repo-functional", {{"run", true}, {"environment", "http"}}}}}}).size() == 1, Show(problems));
problems = bad({{"lenses", {{"repo-functional", {{"run", true}, {"environment", "supabase"}}}}}});
&& bad({{"modes", {{"commits", {{"lenses", {"repo-bug-prover"}}}}}}, {"lenses", {{"repo-functional", {{"environment", "none"}}}}}}).empty()
&& bad({{"modes", {{"commits", {{"lenses", {"repo-functional"}}}}}}, {"lenses", {{"repo-functional", {{"environment", "http"}}}}}}).size() == 1, Show(problems));
problems = bad({{"modes", {{"commits", {{"lenses", {"repo-functional"}}}}}}, {"lenses", {{"repo-functional", {{"environment", "supabase"}}}}}});
Check("the database alone is no application under test for the functional lens; browser+supabase is; a static lens may take either, or the database alone",
problems.size() == 1 && problems[0].contains("empirical") && problems[0].contains("browser, browser+supabase")
&& bad({{"lenses", {{"repo-functional", {{"run", true}, {"environment", "browser+supabase"}}}, {"repo-bug-prover", {{"run", true}, {"environment", "supabase"}}},
{"repo-better-solution", {{"run", true}, {"environment", "browser+supabase"}}}}}}).empty(), Show(problems));
&& bad({{"modes", {{"commits", {{"lenses", {"repo-functional", "repo-bug-prover", "repo-better-solution"}}}}}},
{"lenses", {{"repo-functional", {{"environment", "browser+supabase"}}}, {"repo-bug-prover", {{"environment", "supabase"}}}, {"repo-better-solution", {{"environment", "browser+supabase"}}}}}}).empty(),
Show(problems));
loaded = load();
Check("supabase: the defaults are the local stack's Postgres (54322), no start/stop, no notes",
Dyn::Equal(loaded.config.at("supabase"), {{"dbUrl", "postgresql://postgres:postgres@127.0.0.1:54322/postgres"}, {"start", nullptr}, {"stop", nullptr}, {"notes", ""}}));
@ -179,11 +206,11 @@ int main() {
Any(bad({{"supabase", {{"dbUrl", "http://127.0.0.1:54321"}}}}), "postgres://") && bad({{"supabase", {{"dbUrl", "postgres://u:p@localhost:6543/x"}}}}).empty()
&& bad({{"supabase", {{"dbUrl", "postgresql://u@[::1]:5432/x"}}}}).empty() && Any(bad({{"supabase", {{"start", {"bash", "x"}}}}}), "supabase.start")
&& Any(bad({{"supabase", {{"nope", 1}}}}), "supabase.nope"));
Check("functional.appUrl is checked only while a lens that runs drives the application (plan.ENVIRONMENTS item 4)",
Any(bad({{"functional", {{"appUrl", "ftp://x"}}}, {"lenses", {{"repo-functional", {{"run", true}}}}}}), "appUrl")
Check("functional.appUrl is checked only while a listed lens drives the application (plan.ENVIRONMENTS item 4)",
Any(bad({{"functional", {{"appUrl", "ftp://x"}}}, {"modes", {{"commits", {{"lenses", {"repo-functional"}}}}}}}), "appUrl")
&& !Any(bad({{"functional", {{"appUrl", "ftp://x"}}}}), "appUrl")
&& bad({{"functional", {{"appUrl", "ftp://x"}}}, {"lenses", {{"repo-functional", {{"run", false}}}, {"repo-bug-prover", {{"run", true}, {"environment", "supabase"}}}}}}).empty()
&& Any(bad({{"functional", {{"appUrl", "ftp://x"}}}, {"lenses", {{"repo-functional", {{"run", false}}}, {"repo-bug-prover", {{"run", true}, {"environment", "browser+supabase"}}}}}}), "appUrl"));
&& bad({{"functional", {{"appUrl", "ftp://x"}}}, {"modes", {{"commits", {{"lenses", {"repo-bug-prover"}}}}}}, {"lenses", {{"repo-bug-prover", {{"environment", "supabase"}}}}}}).empty()
&& Any(bad({{"functional", {{"appUrl", "ftp://x"}}}, {"modes", {{"commits", {{"lenses", {"repo-bug-prover"}}}}}}, {"lenses", {{"repo-bug-prover", {{"environment", "browser+supabase"}}}}}}), "appUrl"));
problems = bad({{"functional", {{"extraTools", {"Bash(psql:*)", "Bash(psql -h 127.0.0.1:*)", "Bash(psqlx:*)", "Bash(npm run e2e:*)", "mcp__x__y"}}}}});
Check("functional.extraTools: an entry pool.DENY overrides is a problem (psql commits to the shared database; npm is a package manager); a mere prefix twin is not",
problems.size() == 1 && problems[0].contains("Bash(psql:*)") && problems[0].contains("Bash(psql -h 127.0.0.1:*)") && problems[0].contains("Bash(npm run e2e:*)")
@ -193,17 +220,19 @@ int main() {
const Json rc = Config::Redacted(withPassword);
Check("redacted: the dump's copy of the config masks the database password and drops _path", rc.at("supabase").at("dbUrl") == "postgresql://postgres:***@127.0.0.1:54322/postgres"
&& !DumpSpaced(rc).contains("hunter2") && !rc.contains("_path"));
problems = bad({{"lenses", {{"repo-functional", false}, {"repo-bug-prover", {{"run", true}}}}}});
problems = bad({{"lenses", {{"repo-functional", false}}}});
Check("a bare false under lenses is a shape problem, never an exception; lenses_off and environments fall back to the defaults (off, browser)", problems.size() == 1
&& problems[0].contains("must be an object") && Config::LensesOff(load().config) == allButProver && Config::Environments(load().config).at(fn) == "browser", Show(problems));
Json allOff = Json::object();
for (auto it = defaults.at("lenses").begin(); it != defaults.at("lenses").end(); ++it) allOff[it.key()] = {{"run", false}};
problems = bad({{"lenses", allOff}});
Check("every lens off is the problem that names them", problems.size() == 1 && problems[0].contains("no lens is on") && problems[0].contains("repo-facts"), Show(problems));
Json noLens = Json::object();
for (const std::string_view kind : Plan::Kinds) noLens[std::string(kind)] = {{"lenses", Json::array()}};
problems = bad({{"modes", noLens}});
Check("no mode listing a lens is the problem that names them", problems.size() == 1 && problems[0].contains("no mode lists a lens") && problems[0].contains("repo-facts"), Show(problems));
const Json ex = Config::Example();
Check("the example is what process/templates/config.json holds, every lens off, and its only problem says to turn one on", noLensOn(Config::Validate(ex))
Check("the example is what process/templates/config.json holds: the three kinds as modes with every lens of their kind at the kind's effort, no problem", Config::Validate(ex).empty()
&& Dyn::Equal(Dyn::LoadJsonFile(fs::current_path() / "process" / "templates" / "config.json"), ex)
&& std::ranges::all_of(ex.at("lenses"), [](const Json& entry) { return entry.at("run") == false; }), Show(Config::Validate(ex)));
&& ex.at("modes").at("commits").at("lenses").size() == 5 && ex.at("modes").at("series").at("lenses").size() == 10 && ex.at("modes").at("reply").at("lenses").size() == 5
&& ex.at("modes").at("commits").at("effort") == "xhigh" && ex.at("modes").at("series").at("effort") == "max" && ex.at("modes").at("reply").at("effort") == "max"
&& std::ranges::none_of(ex.at("lenses"), [](const Json& entry) { return entry.contains("run"); }), Show(Config::Validate(ex)));
const fs::path repo = td / "r";
fs::create_directories(repo);
Process::Run({"git", "-C", repo.string(), "init", "-q"});

View file

@ -27,6 +27,8 @@ TABLE = [('repo-better-solution.md', 'none'), ('repo-functional.md', 'browser'),
('process-docs.md', 'none'), ('etiquette.md', 'none'), ('conciseness.md', 'none'), ('maintainer-persona.md', 'none')]
# The push gates' lenses: the plans and estimates of the series goldens are theirs.
REPO_TABLE = TABLE[:5]
# The lenses of each artifact kind (Plan::KindLenses): a mode lists lenses of its kind.
KIND_LENSES = {'commits': [f for f, _ in REPO_TABLE], 'series': [f for f, _ in TABLE[5:]], 'reply': ['facts.md', 'etiquette.md', 'consistency.md', 'conciseness.md', 'maintainer-persona.md']}
GATES = [('Gate 1 — Approach', ['repo-better-solution.md']), ('Gate 2 — Functional', ['repo-functional.md']),
('Gate 3 — Proof', ['repo-bug-prover.md', 'repo-security.md']), ('Gate 4 — Commit truth', ['repo-facts.md'])]
name_of = lambda f: f.replace('.md', '')
@ -69,7 +71,14 @@ for case in c['cases']:
case['config'] = json.loads(spelled(json.dumps(shown), repo))
case['path'] = None if path is None else spelled(path, repo)
lenses = shown.get('lenses')
case['lensesOff'] = [f for f, _ in TABLE if entry_of(lenses, name_of(f)).get('run') is False]
# The lenses no mode lists (Config::LensesOff with no mode): a mode's list counts for the lenses of its kind.
listed = set()
for mname, mode in (shown.get('modes') or {}).items():
if not isinstance(mode, dict): continue
kind = mname if mname in KIND_LENSES else mode.get('kind')
if kind in KIND_LENSES and isinstance(mode.get('lenses'), list):
listed |= {n + '.md' for n in mode['lenses'] if isinstance(n, str) and n + '.md' in KIND_LENSES[kind]}
case['lensesOff'] = [f for f, _ in TABLE if f not in listed]
case['environments'] = {f: entry_of(lenses, name_of(f)).get('environment', 'none') for f, _ in TABLE}
if old_problems != case['problems']: changed_problems[case['name']] = (old_problems, case['problems'])
missing = scratch / 'missing'; missing.mkdir()

File diff suppressed because it is too large Load diff

View file

@ -200,8 +200,8 @@ echo "{\"type\":\"result\",\"subtype\":\"success\",\"is_error\":false,\"result\"
&& 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"
std::ranges::contains(Plan::KindLenses("series"), std::string(Plan::PersonaLens)) && std::ranges::contains(Plan::KindLenses("reply"), std::string(Plan::PersonaLens))
&& !std::ranges::contains(Plan::KindLenses("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);
@ -595,13 +595,18 @@ echo "{\"type\":\"result\",\"subtype\":\"success\",\"is_error\":false,\"result\"
{
const fs::path bench = base / "bench";
fs::create_directories(bench / ".review-gates");
Json lenses = Json::object();
// The bench's modes: a series runs every kernel lens, a reply its
// kind's five.
Json modes = {{"series", {{"lenses", Json::array()}}}, {"reply", {{"lenses", Json::array()}}}};
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}};
for (const std::string_view lf : on) modes["series"]["lenses"].push_back(Plan::LensName(lf));
const std::array<std::string_view, 5> replyOn = {"facts.md", "etiquette.md", "consistency.md", "conciseness.md", Plan::PersonaLens};
for (const std::string_view lf : replyOn) modes["reply"]["lenses"].push_back(Plan::LensName(lf));
// A mode of the bench's own name: a reply with the facts lens alone, at effort high.
modes["quick"] = {{"kind", "reply"}, {"lenses", Json::array({"facts"})}, {"effort", "high"}};
// The bench's own section for the facts lens (lenses.facts.section).
Write(bench / "process" / "lens-sections" / "facts.md", "## The bench's own section\n\n- 2026-10-31: a section of the bench repository, appended to the facts charter.\n");
lenses["facts"]["section"] = "process/lens-sections/facts.md";
Write(bench / ".review-gates" / "config.json", DumpFile(Json({{"lenses", lenses}})));
Write(bench / ".review-gates" / "config.json", DumpFile(Json({{"modes", modes}, {"lenses", {{"facts", {{"section", "process/lens-sections/facts.md"}}}}}})));
const fs::path fakeDir = base / "fake";
const fs::path fake = fakeDir / "claude";
Write(fake, FakeClaude);
@ -697,6 +702,25 @@ echo "{\"type\":\"result\",\"subtype\":\"success\",\"is_error\":false,\"result\"
&& !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));
// A mode of the repository's own name (2026-11-01): `quick`, a
// reply with the facts lens alone at effort high. The run takes
// its kind, lenses and effort from the config and the dump says
// mode and kind; the run lock, the run number and the dump name
// are the kind's, so this is the artifact's third reply dump. An
// unknown mode is refused with the configured names.
const Process::Captured quick = run({"--mode", "quick", "--subsystem", "x", "--artifact", "journal/x/draft.eml", "--no-reuse", "--no-salvage"});
Json quickDump;
try {
quickDump = Dyn::LoadJsonFile(bench / "journal" / "x" / "gate-state" / std::format("reply-{}-run-3.json", replyHash.substr(0, 8)));
} catch (const std::exception&) {
}
const Process::Captured nope = run({"--mode", "nope", "--subsystem", "x", "--artifact", "journal/x/draft.eml", "--dry-run"});
const Json quickLenses = quickDump.is_object() ? quickDump.at("coverage").at("lenses") : Json::array();
Check("a named mode: its kind, lenses and effort from the config, mode and kind in the dump, the dump numbered and named by the kind; an unknown mode is refused naming the configured ones",
quick.returnCode == 2 && quickDump.is_object() && quickDump.at("mode") == "quick" && quickDump.at("kind") == "reply" && quickDump.at("args").at("effort") == "high"
&& quickLenses == Json({"facts"}) && !quickDump.at("lensesOff").empty() && quick.out.contains("quick (a reply)") && nope.returnCode == 3
&& nope.err.contains("--mode nope: no such mode in the config (modes: ") && nope.err.contains("quick"),
std::format("exit {} / {}; {}{}", quick.returnCode, nope.returnCode, Dyn::Slice(quick.out, -600), nope.err));
}
fs::remove_all(base);
return Failures ? 1 : 0;

View file

@ -366,8 +366,8 @@ namespace {
fs::create_directories(td / ".review-gates");
fs::copy_file(fs::current_path() / "process" / "templates" / "config.json", td / ".review-gates" / "config.json");
const Config::Loaded loaded = Config::Load(td);
Check("template: process/templates/config.json, copied as the README says, loads with no warning and no problem but the one that says to turn a lens on - unknown keys included (finding 2; every lens off since 2026-10-31)",
loaded.problems.size() == 1 && loaded.problems[0].starts_with("lenses: no lens is on") && loaded.config.at("_warnings").empty(), Show(loaded.problems));
Check("template: process/templates/config.json, copied as the README says, loads with no warning and no problem - unknown keys included (finding 2; the three kinds as modes with every lens of their kind, 2026-11-01)",
loaded.problems.empty() && loaded.config.at("_warnings").empty(), Show(loaded.problems));
}
fs::remove_all(base);
return Failures ? 1 : 0;

View file

@ -205,7 +205,7 @@ echo "{\"type\":\"result\",\"subtype\":\"success\",\"is_error\":false,\"result\"
Git(repo, {"config", "user.email", "me@example.com"});
Git(repo, {"config", "user.name", "Me"});
Write(repo / ".gitignore", ".review-gates/push/\n.review-gates/trees/\n");
Write(repo / ".review-gates" / "config.json", R"({"lenses": {"repo-better-solution": {"run": true}, "repo-bug-prover": {"run": true}, "repo-security": {"run": true}}})");
Write(repo / ".review-gates" / "config.json", R"({"modes": {"commits": {"lenses": ["repo-better-solution", "repo-bug-prover", "repo-security"]}}})");
Write(repo / "README.md", "a greeter\n");
Git(repo, {"add", "-A"});
Git(repo, {"commit", "-q", "-m", "base"});
@ -429,7 +429,7 @@ echo "{\"type\":\"result\",\"subtype\":\"success\",\"is_error\":false,\"result\"
// too) - push-ready, with the code units, the per-commit slots and
// the functional lens's slot listed as not reviewed.
{
Write(base / "facts.json", R"({"lenses": {"repo-facts": {"run": true}}})");
Write(base / "facts.json", R"({"modes": {"commits": {"lenses": ["repo-facts"]}}})");
const std::int64_t before = calls();
std::vector<std::string> truthArgs = runArgs({"--no-reuse", "--no-salvage"});
truthArgs.insert(truthArgs.begin() + 1, {"--config", (base / "facts.json").string()});
@ -457,7 +457,7 @@ echo "{\"type\":\"result\",\"subtype\":\"success\",\"is_error\":false,\"result\"
{
const fs::path bench = base / "bench";
fs::create_directories(bench / ".review-gates");
Write(bench / ".review-gates" / "config.json", R"({"lenses": {"repo-bug-prover": {"run": true}}})");
Write(bench / ".review-gates" / "config.json", R"({"modes": {"commits": {"lenses": ["repo-bug-prover"]}}})");
const fs::path out = bench / "journal" / "push" / "y";
const Process::Captured benchFreeze = Process::Run({Platform::SelfExe().string(), "freeze", "--repo", repo.string(), "--out", out.string(), "--worktree", "--base", baseSha},
bench, std::chrono::seconds(60), {}, Env);

View file

@ -53,14 +53,14 @@ int main() {
};
// Every lens is off by default: a config under test turns the
// bug-prover on, or the one problem it would have is that none runs.
auto sl = [](const Json& kw) { return Json {{"slots", kw}, {"lenses", {{"repo-bug-prover", {{"run", true}}}}}}; };
auto sl = [](const Json& kw) { return Json {{"slots", kw}, {"modes", {{"commits", {{"lenses", {"repo-bug-prover"}}}}}}}; };
Check("config: slots default to none; a dir inside the repository, start/stop with {n}, and the measured memMb with it; anything else is a problem",
Dyn::Equal(Config::Defaults().at("slots"), {{"dir", nullptr}, {"start", nullptr}, {"stop", nullptr}, {"memMb", nullptr}, {"startMb", nullptr}})
&& AnyContains(bad(sl({{"dir", "s"}, {"memMb", 1}, {"startMb", 0}})), "slots.startMb")
&& bad(sl({{"dir", ".local-stack/slots"}, {"start", "bash s.sh full --slot {n}"}, {"stop", "bash s.sh stop --slot {n}"}, {"memMb", 1500}})).empty()
&& AnyContains(bad(sl({{"dir", "/abs"}, {"memMb", 1}})), "inside the repository") && AnyContains(bad(sl({{"dir", "s"}, {"start", "bash s.sh"}, {"memMb", 1}})), "{n}")
&& AnyContains(bad(sl({{"dir", "s"}})), "slots.memMb") && AnyContains(bad(sl({{"dir", "s"}, {"memMb", true}})), "slots.memMb") && bad(sl({{"nope", 1}})).size() == 1);
Write(p, R"({"functional": {"concurrency": 4}, "lenses": {"repo-bug-prover": {"run": true}}})");
Write(p, R"({"functional": {"concurrency": 4}, "modes": {"commits": {"lenses": ["repo-bug-prover"]}}})");
const Config::Loaded cr = Config::Load(td);
const Config::Loaded cr2 = Config::Load("/", p);
const Json& warnings = cr.config.at("_warnings");
@ -343,7 +343,7 @@ int main() {
Write(td / ".local-stack" / "slots" / "1" / "slot.json", R"({"appUrl": "http://localhost:20180", "apiUrls": [], "dbUrl": null, "login": null})");
Json cfg = Config::Load(td).config;
cfg["slots"] = {{"dir", ".local-stack/slots"}, {"start", "up --slot {n}"}, {"stop", nullptr}, {"memMb", 1500}, {"startMb", 6000}};
cfg["lenses"]["repo-functional"]["run"] = true;
cfg["modes"]["commits"]["lenses"].push_back("repo-functional");
cfg["lenses"]["repo-functional"]["environment"] = "browser+supabase";
std::vector<std::string> out;
const Run::Machine machine = {.available = [] { return std::optional<std::int64_t>(20'000); }, .reserve = [] { return std::int64_t {4000}; }};