A lens has one charter, the one this pipeline ships; what a repository knows that is not general to the lens - its platform and layers, the record it keeps its decisions in, what its users already have, the calibration entries that are its own - goes in a file the config names for the lens (lenses.<name>.section), and Charter appends it after one blank line. The charter sha in every reuse key covers the combined text. The config refuses a path outside the repository - absolute, or climbing out with `..` - by the check slots.dir had, now one both settings share; a file missing or one the run could not use (a dangling link, unreadable, not UTF-8) is a problem too, checked through the reader Charter uses (Prompt::ReadSection), so a section is never dropped in silence, and a name that is no lens is the unknown key it always was. Nothing lists a directory, so what lies beside the files is no concern of the config's: a directory listed would have to tolerate or refuse every foreign entry. The run logs which charters got one. The better-solution charter gains a consumer's general entry (2026-10-31: a mechanism reworked a second time in one submission is the signal to derive the fix at every layer the data crosses). The procedure names the section wherever it names the charter: among the config's keys, in the lens inputs, in the reuse rule (an edit to either re-runs the lens) and in the feedback loop, which routes a repository's own lesson to its section and a general one to the shipped charter.
218 lines
19 KiB
C++
218 lines
19 KiB
C++
// SPDX-License-Identifier: AGPL-3.0-only
|
|
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
|
|
|
// The consumer config: the defaults, the merge, unknown keys, validation,
|
|
// the lenses (run, environment), authors, the login fixture, the example.
|
|
|
|
#include <stdlib.h>
|
|
import std;
|
|
import Crafter.Review;
|
|
namespace fs = std::filesystem;
|
|
using namespace Crafter::Review;
|
|
|
|
namespace {
|
|
std::int32_t Failures = 0;
|
|
|
|
void Check(std::string_view name, bool cond, std::string_view detail = {}) {
|
|
std::println("{}{}{}", cond ? " ok " : " FAIL ", name, !cond && !detail.empty() ? std::format(" ({})", detail) : std::string());
|
|
if (!cond) ++Failures;
|
|
}
|
|
|
|
bool Any(const std::vector<std::string>& problems, std::string_view part) {
|
|
return std::ranges::any_of(problems, [&](const std::string& p) { return p.contains(part); });
|
|
}
|
|
|
|
std::string Show(const std::vector<std::string>& problems) {
|
|
Json out = Json::array();
|
|
for (const std::string& p : problems) out.push_back(p);
|
|
return Dyn::ReprValue(out);
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
std::println("config: defaults, merge, unknown keys, validation, lenses (run, environment), authors, login fixture, the example");
|
|
// A git call of a test reads no global config: this machine's signs
|
|
// every commit with the owner's key.
|
|
setenv("GIT_CONFIG_GLOBAL", "/dev/null", 1);
|
|
setenv("GIT_CONFIG_SYSTEM", "/dev/null", 1);
|
|
SetHome(fs::current_path());
|
|
const std::string fn(Plan::FunctionalLens);
|
|
const fs::path td = fs::temp_directory_path() / std::format("crafter-review-config-{}", Platform::PublishedPid());
|
|
fs::remove_all(td);
|
|
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"); };
|
|
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).
|
|
auto bad = [&](Json value) {
|
|
if (!value.contains("lenses")) value["lenses"] = {{"repo-bug-prover", {{"run", true}}}};
|
|
put(value);
|
|
return load().problems;
|
|
};
|
|
put({{"base", "origin/dev"}, {"aiTrailers", "allow"}, {"allowDirty", {"supabase/config.toml"}}, {"lenses", {{"repo-bug-prover", {{"run", true}}}}},
|
|
{"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"
|
|
&& loaded.config.at("functional").at("readyPath") == "/health" && loaded.config.at("functional").at("appUrl") == "http://localhost:8080" && loaded.config.at("_path") == p.string(),
|
|
Show(loaded.problems));
|
|
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}}}}}});
|
|
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"}}}}}});
|
|
Check("extraTools must be Bash(<prefix>:*) or mcp__...; mcp.args a list", problems.size() == 2, Show(problems));
|
|
// A path found on the host is not there inside the Flatpak.
|
|
const std::vector<std::string> missingCommand = bad({{"functional", {{"mcp", {{"command", "/nonexistent/bin/npx"}}}}}});
|
|
Check("functional.mcp.command: an absolute path that is no program here is a problem; a name, or a program that is there, is not",
|
|
missingCommand.size() == 1 && missingCommand[0].starts_with("functional.mcp.command '/nonexistent/bin/npx' is no program here")
|
|
&& bad({{"functional", {{"mcp", {{"command", "/bin/sh"}}}}}}).empty() && bad({{"functional", {{"mcp", {{"command", "npx"}}}}}}).empty(),
|
|
Show(missingCommand));
|
|
// A lens's own section (lenses.<name>.section, 2026-11-01, Jorijn: a path
|
|
// per lens rather than a directory listed): a file of the repository's,
|
|
// appended to the shipped charter. Only the section problems and the
|
|
// unknown keys count here: a config under test may turn no lens on.
|
|
auto sectionProblems = [&](const Json& lenses) {
|
|
std::vector<std::string> out;
|
|
for (const std::string& problem : bad({{"lenses", lenses}})) {
|
|
if (problem.contains("section") || problem.contains("unknown key lenses.")) out.push_back(problem);
|
|
}
|
|
return out;
|
|
};
|
|
const std::string outside = "lenses.repo-facts.section must be null or a file inside the repository (a relative path)";
|
|
Check("lenses.<name>.section: not a string, a blank one, an absolute path and one climbing out with .. are problems (slots.dir's rule) and resolve to nothing",
|
|
Any(sectionProblems({{"repo-facts", {{"section", 5}}}}), outside) && Any(sectionProblems({{"repo-facts", {{"section", " "}}}}), outside)
|
|
&& Any(sectionProblems({{"repo-facts", {{"section", "/elsewhere/facts.md"}}}}), outside) && Any(sectionProblems({{"repo-facts", {{"section", "../facts.md"}}}}), outside)
|
|
&& Config::LensSectionsOf({{"lenses", {{"repo-facts", {{"section", "/elsewhere/facts.md"}}}}}}, td).empty()
|
|
&& Config::LensSectionsOf({{"lenses", {{"repo-facts", {{"section", "a/../../facts.md"}}}}}}, td).empty());
|
|
fs::create_directories(td / "sections" / "adir");
|
|
std::ofstream(td / "sections" / "facts.md", std::ios::binary) << "## Ours\n";
|
|
std::ofstream(td / "sections" / "README.md", std::ios::binary) << "beside the sections, no concern of the config's\n";
|
|
std::ofstream(td / "sections" / "bad.md", std::ios::binary) << std::string("\xff\n", 2);
|
|
fs::create_symlink("missing-target.md", td / "sections" / "dangling.md");
|
|
const std::vector<std::string> named = sectionProblems({{"repo-facts", {{"section", "sections/facts.md"}}}, {"repo-bug-prover", {{"section", "sections/bad.md"}}},
|
|
{"repo-security", {{"section", "sections/dangling.md"}}}, {"repo-functional", {{"section", "sections/adir"}}},
|
|
{"facts", {{"section", "sections/nope.md"}}}});
|
|
Check("lenses.<name>.section: a file that is there is fine, whatever lies beside it; one not UTF-8, a dangling link, a directory or a path to nothing is a problem naming the lens and the path",
|
|
named.size() == 4 && Any(named, "lenses.repo-bug-prover.section: sections/bad.md is not UTF-8") && Any(named, "lenses.repo-security.section: sections/dangling.md cannot be read")
|
|
&& Any(named, "lenses.repo-functional.section: sections/adir cannot be read") && Any(named, "lenses.facts.section: sections/nope.md does not exist"),
|
|
Show(named));
|
|
const std::vector<std::string> unknown = sectionProblems({{"repo-fact", {{"section", "sections/facts.md"}}}, {"_common", {{"section", "sections/facts.md"}}},
|
|
{"repo-facts", {{"sectoin", "sections/facts.md"}}}});
|
|
Check("lenses.<name>.section: a name that is no lens - the common text's too - and a misspelt key are unknown keys",
|
|
Any(unknown, "unknown key lenses.repo-fact") && Any(unknown, "unknown key lenses._common") && Any(unknown, "unknown key lenses.repo-facts.sectoin"), Show(unknown));
|
|
// A file that cannot be read - where permissions bind, as they do not for
|
|
// root.
|
|
std::ofstream(td / "sections" / "locked.md", std::ios::binary) << "## Ours\n";
|
|
fs::permissions(td / "sections" / "locked.md", fs::perms::none);
|
|
const bool unreadable = !Platform::ReadFile(td / "sections" / "locked.md");
|
|
const std::vector<std::string> locked = sectionProblems({{"repo-facts", {{"section", "sections/locked.md"}}}});
|
|
fs::permissions(td / "sections" / "locked.md", fs::perms::owner_all);
|
|
Check("lenses.<name>.section: a file that cannot be read is a problem naming it", !unreadable || Any(locked, "lenses.repo-facts.section: sections/locked.md cannot be read"), Show(locked));
|
|
const std::map<std::string, fs::path> resolved = Config::LensSectionsOf({{"lenses", {{"repo-facts", {{"section", "sections/facts.md"}}}}}}, td);
|
|
Check("lenses.<name>.section: a file that is there has no problem and resolves against the repository, keyed by the lens's file; null is none",
|
|
sectionProblems({{"repo-facts", {{"section", "sections/facts.md"}}}}).empty() && resolved.size() == 1 && resolved.at("repo-facts.md") == td / "sections" / "facts.md"
|
|
&& Config::LensSectionsOf({{"lenses", {{"repo-facts", {{"section", nullptr}}}}}}, td).empty());
|
|
std::ofstream(p, std::ios::binary) << "{not json";
|
|
Check("not JSON -> one problem", load().problems.size() == 1);
|
|
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 defaults = Config::Defaults();
|
|
std::set<std::string> defaultNames;
|
|
for (auto it = defaults.at("lenses").begin(); it != defaults.at("lenses").end(); ++it) defaultNames.insert(it.key());
|
|
Json environments = {{"repo-better-solution.md", "none"}, {fn, "browser"}, {"repo-bug-prover.md", "none"}, {"repo-security.md", "none"}, {Plan::FactsLens, "none"}};
|
|
// The kernel modes' lenses (2026-10-31), every one without an environment.
|
|
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,
|
|
Show(loaded.problems));
|
|
put({{"lenses", {{"repo-functional", {{"run", false}}}, {"repo-bug-prover", {{"run", true}, {"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",
|
|
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"}}}}}});
|
|
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));
|
|
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", ""}}));
|
|
problems = bad({{"supabase", {{"dbUrl", "postgresql://postgres:secret@db.abcdefgh.supabase.co:5432/postgres"}}}});
|
|
Check("supabase.dbUrl: a hosted database is refused (this machine only), and the message does not echo the password", problems.size() == 1
|
|
&& problems[0].contains("not this machine") && !problems[0].contains("secret"), Show(problems));
|
|
Check("supabase.dbUrl: http:// is no database URL; localhost and ::1 are this machine; start/stop must be command lines; an unknown key is reported",
|
|
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")
|
|
&& !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"));
|
|
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:*)")
|
|
&& !problems[0].contains("psqlx") && problems[0].contains("supabase environment"), Show(problems));
|
|
Json withPassword = load().config;
|
|
withPassword["supabase"] = {{"dbUrl", "postgresql://postgres:hunter2@127.0.0.1:54322/postgres"}, {"start", nullptr}, {"stop", nullptr}, {"notes", ""}};
|
|
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}}}}}});
|
|
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));
|
|
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))
|
|
&& 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)));
|
|
const fs::path repo = td / "r";
|
|
fs::create_directories(repo);
|
|
Process::Run({"git", "-C", repo.string(), "init", "-q"});
|
|
Process::Run({"git", "-C", repo.string(), "config", "user.email", "me@example.com"});
|
|
Check("authors: the configured list, else git config user.email", Config::Authors({{"authors", {"a@b.c"}}}, repo) == std::vector<std::string>{"a@b.c"}
|
|
&& Config::Authors({{"authors", nullptr}}, repo) == std::vector<std::string>{"me@example.com"});
|
|
std::ofstream(td / "u.json", std::ios::binary) << "{}";
|
|
Check("login_fixture: the first candidate that exists", Config::LoginFixture({{"functional", {{"login", {{"fixtures", {"nope.json", "u.json"}}}}}}}, td) == "u.json"
|
|
&& !Config::LoginFixture({{"functional", {{"login", {{"fixtures", Json::array()}}}}}}, td));
|
|
fs::remove_all(td);
|
|
return Failures ? 1 : 0;
|
|
}
|