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
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue