// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® export module Crafter.Review:Config; import std; import :Json; // The consumer's configuration: `.review-gates/config.json` at the // repository root (`--config` names another file). It holds what the // pipeline must know about THIS repository — who may author its commits, // which branch a range is measured against, which lenses run and the // environment each gets, how its application is started and reached, where // its local database answers, which paths carry no runtime behaviour — and // nothing about any one review. A missing file is the defaults; an unknown // key or a malformed value is a preflight failure, and a retired key a // warning. The config stays the JSON it was read as, keys in their order. export namespace Crafter::Review::Config { constexpr std::string_view ConfigDir = ".review-gates"; constexpr std::string_view ConfigFile = ".review-gates/config.json"; constexpr std::string_view SlotFile = "slot.json"; // DEFAULTS, a fresh copy each call. Json Defaults(); struct Loaded { Json config; std::vector problems; }; // The defaults, the file's values merged over them, and every problem // Validate finds. A retired key is no problem: it is dropped, and // config["_warnings"] says so and what replaced it — a key the pipeline // stopped reading must not stop a consumer's freeze or run (2026-10-22). // `path` empty: /.review-gates/config.json. Loaded Load(const std::filesystem::path& repo, const std::filesystem::path& path = {}); std::vector Validate(const Json& config); // The config for a dump: the database URL with its password masked, no // `_` bookkeeping keys. Json Redacted(const Json& config); // The emails the freeze accepts as author and committer: the configured // list; none (null, or no `authors` key) - the freeze checks neither. std::optional> Authors(const Json& config); // The lens files no mode lists (what runs nowhere: the slots and init). std::vector LensesOff(const Json& config); // {lens file: environment} for every lens of the table, as the config // holds it (validated elsewhere). Json Environments(const Json& config); struct Slots { // pool.Stacks entries {id, appUrl, apiUrls, dbUrl, login, slot: true}, // in slot order. Json stacks = Json::array(); std::vector problems; }; // The slots that are up: slots.dir//slot.json. Slots SlotStacks(const Json& config, const std::filesystem::path& repo); // slots.start or slots.stop for the slot numbers given, {n} filled. std::vector SlotCommands(const Json& config, std::string_view which, std::span slots); // The first login fixture that exists, relative to the repository root. std::optional LoginFixture(const Json& config, const std::filesystem::path& repo); // The repository's own lens sections (`lenses..section`, resolved // against the repository root), keyed by lens file: each a file the // config names inside the repository (a relative path, no `..` part). std::map LensSectionsOf(const Json& config, const std::filesystem::path& repo); // What is wrong with those files: one missing, or one // Prompt::ReadSection cannot use. std::vector SectionProblems(const Json& config, const std::filesystem::path& repo); // A mode as the config defines it (modes., 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 lensFiles; std::optional effort; }; std::optional ModeOf(const Json& config, std::string_view name); // The configured mode names, in the config's order. std::vector ModeNames(const Json& config); // The lens files of the mode's kind that the mode does not list. std::vector LensesOff(const Json& config, std::string_view mode); // The example a consumer starts from (process/templates/config.json is // this, rendered). Json Example(); // `crafter-review [--config F] config`: the merged config, its warnings // and its problems; exit 1 when there is a problem. std::int32_t Main(const std::filesystem::path& repo, const std::filesystem::path& path); }