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:
parent
6cf15919db
commit
d5e54ffeb3
25 changed files with 1277 additions and 711 deletions
|
|
@ -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"});
|
||||
|
|
|
|||
Loading…
Reference in a new issue