Crafter.Review/implementations/Crafter.Review-Seats.cpp
Jorijn van der Graaf ceb87f70ef
All checks were successful
Flatpak / check (push) Has been skipped
Flatpak / publish (push) Successful in 5m30s
feat(seats): an API seat's cycle starts on its own grant day
The ledger reset every API seat's spend on one
REVIEW_GATES_API_CYCLE_DAY and cleared every credit-out, an OpenRouter
key's too, at that reset. Each API seat's cycle starts on its
ANTHROPIC_API_KEY<n>_GRANT_DAY now (1..31, a day past a month's end its
last), else the cycle day, also 1..31. A credit-out carries across the
rollover, and the run's probe tries a key held out on credit again, as
`seats` does an API key: an ok lifts the mark - an API key's is new
credits, its cycle restarting from the grant day, or from today when
that day is over a week back; an OpenRouter key keeps its cycle's spend,
and a key that answers with its cycle's budget spent stays out on that
budget. A probe that finds a key out of credit records it, unless the
ledger already has it out. Both are decided in one locked update on the
file's entry, a mark another driver made since the probe started
standing, and the ledger's stamps carry their UTC offset so marks are
ordered by instant.

`seats` shows the probe's reading beside the driver's verdict (the
`probe` and `driver` columns replace `state` and `ledger: out`), each
seat's grant day and when its next credits are due, and notes on what
the probe changed in the ledger or why the driver's verdict stands; the
run logs the days.
2026-11-02 18:44:02 +01:00

1077 lines
57 KiB
C++

// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
module;
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
module Crafter.Review:Seats_impl;
import :Seats;
import :Json;
import :Text;
import :Stream;
import :Platform;
import :Process;
import :Crypto;
import std;
namespace fs = std::filesystem;
namespace Crafter::Review::Seats {
namespace {
constexpr std::array<std::string_view, 7> Weekdays = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
// en-GB, as Intl.DateTimeFormat abbreviates them (September is "Sept").
constexpr std::array<std::string_view, 12> Months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"};
constexpr std::array<std::string_view, 4> ModelFamilies = {"fable", "opus", "sonnet", "haiku"};
const std::string& EnvOf(const std::map<std::string, std::string>& env, const std::string& key) {
static const std::string none;
const auto found = env.find(key);
return found == env.end() ? none : found->second;
}
double AsDouble(const Json& value) {
if (value.is_number_float()) return value.get<double>();
if (value.is_boolean()) return value.get<bool>() ? 1.0 : 0.0;
if (value.is_number_unsigned()) return static_cast<double>(value.get<std::uint64_t>());
if (value.is_number_integer()) return static_cast<double>(value.get<std::int64_t>());
return 0.0;
}
// The value as a float, or nullopt for one that is none.
std::optional<double> FloatOf(const Json& value) {
if (value.is_number() || value.is_boolean()) return AsDouble(value);
if (value.is_string()) return Dyn::ParseFloat(value.get_ref<const std::string&>());
return std::nullopt;
}
// An integer when n is integral, else n.
Json IntIfWhole(double n) {
if (std::isfinite(n) && n == std::floor(n) && std::fabs(n) < 9.0e18) return Json(static_cast<std::int64_t>(n));
return Json(n);
}
// A number field as a float, 0 when it is falsy.
double NumberOr0(const Json& value) {
return Dyn::Truthy(value) ? AsDouble(value) : 0.0;
}
bool Less(const Json& a, const Json& b) {
return AsDouble(a) < AsDouble(b);
}
// An ASCII literal, case-insensitive as re.I reads it, at `at`.
bool MatchCi(std::u32string_view text, std::size_t at, std::string_view word) {
if (at + word.size() > text.size()) return false;
for (std::size_t i = 0; i < word.size(); ++i) {
const char p = word[i];
if (p >= 'a' && p <= 'z') {
if (Dyn::IgnoreCaseLetter(text[at + i]) != p) return false;
} else if (text[at + i] != static_cast<char32_t>(static_cast<std::uint8_t>(p))) {
return false;
}
}
return true;
}
bool ContainsCi(std::string_view text, std::string_view word) {
const std::u32string decoded = Dyn::Decode(text);
for (std::size_t at = 0; at + word.size() <= decoded.size(); ++at) {
if (MatchCi(decoded, at, word)) return true;
}
return false;
}
// seats.CREDIT_RE: the organization's credit balance is gone, or a
// spend limit was reached.
bool CreditText(std::string_view text) {
constexpr std::array<std::string_view, 9> Phrases = {
"credit balance too low", "credit balance is too low", "add funds", "reached your api usage limits", "reached your specified api usage limits",
"reached your workspace api usage limits", "reached your specified workspace api usage limits", "crossed its monthly api usage threshold",
"enforced_spend_limit_reached",
};
return std::ranges::any_of(Phrases, [&](std::string_view phrase) { return ContainsCi(text, phrase); });
}
std::string ResultText(const Json& result) {
return Dyn::StrOr(Dyn::Get(Dyn::Or(result, Dyn::EmptyDict()), "result"), "");
}
bool TokenChar(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '-';
}
std::size_t RunOf(std::string_view s, std::size_t at, const std::function<bool(char)>& in) {
std::size_t end = at;
while (end < s.size() && in(s[end])) ++end;
return end - at;
}
// re.sub(pattern, replacement, text) for a pattern given as a matcher:
// the length of the match at a position, or 0.
std::string SubAll(std::string_view text, const std::function<std::size_t(std::string_view, std::size_t)>& match, std::string_view replacement) {
std::string out;
std::size_t at = 0;
while (at < text.size()) {
const std::size_t length = match(text, at);
if (length > 0) {
out += replacement;
at += length;
} else {
out.push_back(text[at]);
++at;
}
}
return out;
}
// eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}
std::size_t Jwt(std::string_view s, std::size_t at) {
if (!s.substr(at).starts_with("eyJ")) return 0;
std::size_t i = at + 3;
for (std::int32_t part = 0; part < 3; ++part) {
if (part > 0) {
if (i >= s.size() || s[i] != '.') return 0;
++i;
}
const std::size_t run = RunOf(s, i, TokenChar);
if (run < 10) return 0;
i += run;
}
return i - at;
}
// sk-ant-[A-Za-z0-9_-]{20,}
std::size_t AnthropicKey(std::string_view s, std::size_t at) {
if (!s.substr(at).starts_with("sk-ant-")) return 0;
const std::size_t run = RunOf(s, at + 7, TokenChar);
return run >= 20 ? 7 + run : 0;
}
// Bearer\s+[A-Za-z0-9._~+/=-]{16,}
std::size_t Bearer(std::string_view s, std::size_t at) {
if (!s.substr(at).starts_with("Bearer")) return 0;
const std::size_t gap = Dyn::SpaceRun(s, at + 6);
if (gap == 0) return 0;
const std::size_t run = RunOf(s, at + 6 + gap, [](char c) { return TokenChar(c) || std::string_view("._~+/=").contains(c); });
return run >= 16 ? 6 + gap + run : 0;
}
// [A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}, as the regex engine
// backs off: the local part runs up to the @; the domain is the
// longest prefix of its run that ends in a dot and two letters or more.
std::size_t Email(std::string_view s, std::size_t at) {
auto local = [](char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || std::string_view("._%+-").contains(c); };
auto domain = [](char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.' || c == '-'; };
auto letter = [](char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); };
const std::size_t name = RunOf(s, at, local);
if (name == 0 || at + name >= s.size() || s[at + name] != '@') return 0;
const std::size_t start = at + name + 1;
const std::size_t run = RunOf(s, start, domain);
for (std::size_t k = run; k-- > 1;) {
if (s[start + k] != '.') continue;
const std::size_t letters = RunOf(s, start + k + 1, letter);
if (letters >= 2) return name + 1 + k + 1 + letters;
}
return 0;
}
// shutil.which(name, path=...): an executable file on the path.
std::optional<std::string> Which(std::string_view name, std::string_view path) {
for (const std::string& dir : Dyn::Split(path, ":")) {
// An empty PATH entry gives the bare name.
const fs::path candidate = dir.empty() ? fs::path(name) : fs::path(dir) / name;
std::error_code error;
if (fs::is_regular_file(candidate, error) && access(candidate.c_str(), X_OK) == 0) return candidate.string();
}
return std::nullopt;
}
// _version_key: claude-code-<major>.<minor>.<patch>.
std::array<std::int64_t, 3> VersionKey(std::string_view name) {
const std::size_t at = name.find("claude-code-");
if (at == std::string_view::npos) return {0, 0, 0};
for (std::size_t from = at; from != std::string_view::npos; from = name.find("claude-code-", from + 1)) {
std::array<std::int64_t, 3> parts {};
std::size_t i = from + 12;
bool ok = true;
for (std::int32_t p = 0; p < 3 && ok; ++p) {
if (p > 0) {
if (i >= name.size() || name[i] != '.') {
ok = false;
break;
}
++i;
}
const std::size_t digits = Dyn::DecimalRun(name, i);
if (digits == 0) {
ok = false;
break;
}
parts[static_cast<std::size_t>(p)] = Dyn::ParseInt(name.substr(i, digits)).value_or(0);
i += digits;
}
if (ok) return parts;
}
return {0, 0, 0};
}
const Json& Window(const Json& seat, std::string_view key) {
return Dyn::Get(Dyn::Or(Dyn::Get(seat, "windows"), Dyn::EmptyDict()), key);
}
std::string SeatName(const Json& seat) {
const Json& id = Dyn::At(seat, "seat");
return Dyn::Equal(id, Json(0)) ? std::string("local") : std::format("seat {}", Dyn::Str(id));
}
std::string SeatCell(const Json& seat) {
const Json& id = Dyn::At(seat, "seat");
return Dyn::Equal(id, Json(0)) ? std::string("local") : Dyn::Str(id);
}
std::int64_t Inflight(const Json& board, const std::map<std::int32_t, std::string>& keys, const Json& seat) {
const auto key = keys.find(Dyn::At(seat, "seat").get<std::int32_t>());
if (key == keys.end() || !board.is_object()) return 0;
const Json& record = Dyn::Get(Dyn::Or(Dyn::Get(board, "seats"), Dyn::EmptyDict()), key->second);
return static_cast<std::int64_t>(AsDouble(Dyn::Get(Dyn::Or(record, Dyn::EmptyDict()), "inflight")));
}
std::vector<Json> SortedBySeat(const std::vector<Json>& seats) {
std::vector<Json> out = seats;
std::ranges::stable_sort(out, [](const Json& a, const Json& b) { return Less(Dyn::At(a, "seat"), Dyn::At(b, "seat")); });
return out;
}
}
double JsRound(double x) {
return std::floor(x + 0.5);
}
std::string Fmt(const Json& value) {
if ((value.is_number() || value.is_boolean())) {
const double n = AsDouble(value);
if (std::isfinite(n) && n == std::floor(n)) return std::to_string(static_cast<std::int64_t>(n));
}
return Dyn::Str(value);
}
Json DefaultConfig() {
return {{"maxUtil", 85}, {"lensCostPct", 10}, {"waitMaxMin", 15}, {"policy", "spread"}, {"handoverPct", 90}};
}
Json ConfigFromEnv(const std::map<std::string, std::string>& env) {
Json config = DefaultConfig();
const Json defaults = DefaultConfig();
for (const auto& [key, variable] : std::array<std::pair<std::string_view, std::string_view>, 4>{{
{"maxUtil", "REVIEW_GATES_SEAT_MAX_UTIL"}, {"lensCostPct", "REVIEW_GATES_LENS_COST_PCT"},
{"waitMaxMin", "REVIEW_GATES_SEAT_WAIT_MIN"}, {"handoverPct", "REVIEW_GATES_SEAT_HANDOVER_PCT"}}}) {
const auto found = env.find(std::string(variable));
Json value = defaults.at(std::string(key));
if (found != env.end()) {
const std::optional<double> n = Dyn::ParseFloat(found->second);
if (n && !Dyn::Strip(found->second).empty() && std::isfinite(*n) && *n >= 0) value = IntIfWhole(*n);
}
config[std::string(key)] = value;
}
config["policy"] = EnvOf(env, "REVIEW_GATES_SEAT_POLICY") == "pack" ? "pack" : "spread";
return config;
}
std::vector<std::pair<std::int32_t, std::string>> SeatTokensFromEnv(const std::map<std::string, std::string>& env) {
std::vector<std::pair<std::int32_t, std::string>> out;
for (std::int32_t i = 1; i <= MaxSeats; ++i) {
std::string token = Dyn::Strip(EnvOf(env, std::format("CLAUDE_CODE_OAUTH_TOKEN{}", i)));
if (token.empty() && i == 1) token = Dyn::Strip(EnvOf(env, "CLAUDE_CODE_OAUTH_TOKEN"));
if (!token.empty()) out.emplace_back(i, token);
}
return out;
}
std::vector<std::pair<std::int32_t, std::string>> ApiKeysFromEnv(const std::map<std::string, std::string>& env) {
std::vector<std::pair<std::int32_t, std::string>> out;
for (std::int32_t i = 1; i <= MaxSeats; ++i) {
const std::string key = Dyn::Strip(EnvOf(env, std::format("ANTHROPIC_API_KEY{}", i)));
if (!key.empty()) out.emplace_back(i, key);
}
return out;
}
std::map<std::int32_t, std::int32_t> ApiGrantDays(const std::map<std::string, std::string>& env) {
std::map<std::int32_t, std::int32_t> out;
for (std::int32_t i = 1; i <= MaxSeats; ++i) {
// Decimal digits of any script; past 31 the value stays out of
// range.
const std::u32string value = Dyn::Decode(Dyn::Strip(EnvOf(env, std::format("ANTHROPIC_API_KEY{}_GRANT_DAY", i))));
if (value.empty() || !std::ranges::all_of(value, [](char32_t c) { return Dyn::DecimalValue(c) >= 0; })) continue;
std::int32_t day = 0;
for (const char32_t c : value) day = std::min(day * 10 + Dyn::DecimalValue(c), 32);
if (day >= 1 && day <= 31) out[i] = day;
}
return out;
}
std::vector<std::pair<std::int32_t, std::string>> OpenRouterKeysFromEnv(const std::map<std::string, std::string>& env) {
std::vector<std::pair<std::int32_t, std::string>> out;
for (std::int32_t i = 1; i <= MaxSeats; ++i) {
std::string key = Dyn::Strip(EnvOf(env, std::format("OPENROUTER_KEY{}", i)));
if (key.empty() && i == 1) key = Dyn::Strip(EnvOf(env, "OPENROUTER_KEY"));
if (!key.empty()) out.emplace_back(i, key);
}
return out;
}
std::vector<std::pair<std::string, std::string>> DotenvLines(std::string_view text) {
std::vector<std::pair<std::string, std::string>> out;
for (const std::string& raw : Dyn::Split(text, "\n")) {
// ^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$
std::string_view line(raw);
std::size_t at = Dyn::SpaceRun(line, 0);
if (line.substr(at).starts_with("export")) {
const std::size_t gap = Dyn::SpaceRun(line, at + 6);
if (gap > 0) at += 6 + gap;
}
auto nameChar = [](char c, bool first) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' || (!first && c >= '0' && c <= '9');
};
std::size_t end = at;
while (end < line.size() && nameChar(line[end], end == at)) ++end;
if (end == at) continue;
const std::string name(line.substr(at, end - at));
at = end + Dyn::SpaceRun(line, end);
if (at >= line.size() || line[at] != '=') continue;
++at;
at += Dyn::SpaceRun(line, at);
std::string value = Dyn::RStrip(line.substr(at));
if (value.size() >= 2 && (value.front() == '"' || value.front() == '\'') && value.back() == value.front()) value = value.substr(1, value.size() - 2);
out.emplace_back(name, std::move(value));
}
return out;
}
std::map<std::string, std::string> SeatTokensFromEnvFile(std::string_view text) {
std::map<std::string, std::string> env;
for (const auto& [name, value] : DotenvLines(text)) {
for (const std::string_view stem : {std::string_view("CLAUDE_CODE_OAUTH_TOKEN"), std::string_view("ANTHROPIC_API_KEY"), std::string_view("OPENROUTER_KEY")}) {
if (!name.starts_with(stem)) continue;
std::string_view rest = std::string_view(name).substr(stem.size());
// ANTHROPIC_API_KEY<n>_GRANT_DAY: the seat's grant day.
if (stem == "ANTHROPIC_API_KEY" && rest.size() == 11 && rest.ends_with("_GRANT_DAY")) rest = rest.substr(0, 1);
const bool digit = rest.size() == 1 && rest[0] >= '1' && rest[0] <= '6';
if (!rest.empty() && !digit) break;
if (stem == "ANTHROPIC_API_KEY" && !digit) break;
env[name] = value;
break;
}
}
return env;
}
std::map<std::string, std::string> SettingsFromEnvFile(std::string_view text) {
std::map<std::string, std::string> out;
for (const auto& [name, value] : DotenvLines(text)) {
if (name.starts_with("REVIEW_GATES_")) out[name] = value;
}
return out;
}
Json Pct(const Json& utilization) {
const std::optional<double> n = FloatOf(utilization);
if (!n || !std::isfinite(*n) || *n < 0) return 0;
return IntIfWhole(JsRound(*n * 1000) / 10);
}
namespace {
std::int64_t IntOf(const Json& value) {
const std::optional<double> n = FloatOf(value);
if (!n || !std::isfinite(*n)) return 0;
return static_cast<std::int64_t>(*n);
}
}
Json ParseRateLimitInfo(const Json& raw) {
if (!raw.is_object()) return nullptr;
Json windows = Json::object();
const Json& unified = Dyn::Get(raw, "unifiedWindows");
if (unified.is_object()) {
for (auto it = unified.begin(); it != unified.end(); ++it) {
if (!it.value().is_object()) continue;
windows[it.key()] = {{"utilization", Pct(Dyn::Get(it.value(), "utilization"))}, {"resetsAt", IntOf(Dyn::Get(it.value(), "resetsAt"))}};
}
}
const std::int64_t resets = IntOf(Dyn::Get(raw, "resetsAt"));
return {
{"status", Dyn::StrOr(Dyn::Get(raw, "status"), "")},
{"rateLimitType", Dyn::Truthy(Dyn::Get(raw, "rateLimitType")) ? Json(Dyn::Str(Dyn::At(raw, "rateLimitType"))) : Json()},
{"resetsAt", resets ? Json(resets) : Json()},
{"windows", std::move(windows)},
// overageDisabledReason, e.g. out_of_credits: a limit is a hard 429,
// not a charge.
{"overageReason", Dyn::Truthy(Dyn::Get(raw, "overageDisabledReason")) ? Json(Dyn::Str(Dyn::At(raw, "overageDisabledReason"))) : Json()},
};
}
Json ParseStream(std::string_view text) {
Json result;
Json rate;
Json session;
if (Dyn::Strip(text).empty()) return {{"result", nullptr}, {"rateLimit", nullptr}, {"sessionId", nullptr}};
for (const std::string& raw : Dyn::Split(text, "\n")) {
const std::string line = Dyn::Strip(raw);
if (line.empty()) continue;
Json o;
try {
o = ParseLoose(line);
} catch (const std::exception&) {
continue;
}
if (!o.is_object()) continue;
const Json& id = Dyn::Get(o, "session_id");
if (!Dyn::Truthy(session) && id.is_string()) {
const std::string& value = id.get_ref<const std::string&>();
const bool shaped = value.size() == 36 && std::ranges::all_of(value, [](char c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || c == '-'; });
if (shaped) session = id;
}
if (Dyn::Get(o, "type") == "result") {
result = o;
} else if (Dyn::Get(o, "type") == "rate_limit_event") {
const Json parsed = ParseRateLimitInfo(Dyn::Get(o, "rate_limit_info"));
if (Dyn::Truthy(parsed)) rate = parsed;
}
}
return {{"result", result}, {"rateLimit", rate}, {"sessionId", session}};
}
std::vector<std::string> ProbeArgs(std::string_view model) {
return {"--print", "ok", "--system-prompt", "Reply ok.", "--model", std::string(model), "--max-turns", "1", "--tools", "",
"--strict-mcp-config", "--disable-slash-commands", "--output-format", "stream-json", "--verbose"};
}
std::map<std::string, std::string> ProbeEnv(std::string_view token, const std::string& configDir, const std::map<std::string, std::string>& base) {
const std::string& home = EnvOf(base, "HOME");
return {
{"PATH", EnvOf(base, "PATH")},
{"HOME", home.empty() ? fs::temp_directory_path().string() : home},
{"CLAUDE_CONFIG_DIR", configDir},
{"CLAUDE_CODE_OAUTH_TOKEN", std::string(token)},
{"MAX_THINKING_TOKENS", "0"},
{"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC", "1"},
{"DISABLE_AUTOUPDATER", "1"},
};
}
std::map<std::string, std::string> LocalEnv(const std::map<std::string, std::string>& base) {
std::map<std::string, std::string> env = base;
for (const std::string_view key : {"CLAUDE_CODE_OAUTH_TOKEN", "ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN"}) env.erase(std::string(key));
env["MAX_THINKING_TOKENS"] = "0";
env["CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"] = "1";
env["DISABLE_AUTOUPDATER"] = "1";
return env;
}
fs::path WriteApiKeyApproval(const fs::path& configDir, std::string_view key) {
const fs::path path = configDir / ".claude.json";
if (fs::exists(path)) return path;
const std::size_t length = Dyn::Length(key);
const std::string tail = Dyn::Slice(key, static_cast<std::int64_t>(length) - std::min<std::int64_t>(20, static_cast<std::int64_t>(length)));
const Json approval = {{"hasCompletedOnboarding", true}, {"customApiKeyResponses", {{"approved", {tail}}, {"rejected", Json::array()}}}};
// Two passes of a fresh seat write it in the same instant. Written
// whole under another name, then linked into place, which fails when
// the file is there: the one that loses leaves the other's, and with
// it whatever claude has written to it since.
const fs::path temp = std::format("{}.{}.{}.tmp", path.string(), Platform::PublishedPid(), TokenHex(4));
{
std::ofstream out(temp, std::ios::binary);
out << DumpSpaced(approval);
}
fs::permissions(temp, fs::perms::owner_read | fs::perms::owner_write, fs::perm_options::replace);
std::error_code error;
link(temp.c_str(), path.c_str());
fs::remove(temp, error);
return path;
}
std::map<std::string, std::string> ApiEnv(std::string_view key, const std::string& configDir, const std::map<std::string, std::string>& base) {
// Minimal by construction: no CLAUDE_CODE_ENTRYPOINT of a launcher (it
// turns the subscriber credits' answer into "Credit balance is too
// low").
std::map<std::string, std::string> env = ProbeEnv("", configDir, base);
env.erase("CLAUDE_CODE_OAUTH_TOKEN");
env["ANTHROPIC_API_KEY"] = std::string(key);
WriteApiKeyApproval(configDir, key);
return env;
}
bool LooksLikeCreditExhaustion(const Json& result, std::string_view text) {
return CreditText(ResultText(result)) || CreditText(text);
}
bool LooksLikeAuthFailure(const Json& result, std::string_view text) {
const Json& status = Dyn::Get(Dyn::Or(result, Dyn::EmptyDict()), "api_error_status");
if (Dyn::Equal(status, Json(401)) || Dyn::Equal(status, Json(403))) return true;
return Stream::AuthText(ResultText(result)) || Stream::AuthText(text);
}
std::string ClipReason(std::string_view input, std::span<const std::string> secrets) {
std::string s(input);
for (const std::string& secret : secrets) {
if (!secret.empty()) s = Dyn::Replace(s, secret, "[redacted:token]");
}
s = SubAll(s, Jwt, "[redacted:jwt]");
s = SubAll(s, AnthropicKey, "[redacted:token]");
s = SubAll(s, Bearer, "Bearer [redacted]");
s = SubAll(s, Email, "[redacted:email]");
// re.sub(r'\s+', ' ', s).strip()[:160]
std::string collapsed;
for (std::size_t at = 0; at < s.size();) {
const std::size_t gap = Dyn::SpaceRun(s, at);
if (gap > 0) {
collapsed.push_back(' ');
at += gap;
continue;
}
const std::size_t used = Dyn::CodePointAt(s, at).second;
collapsed.append(s, at, used);
at += used;
}
return Dyn::Slice(Dyn::Strip(collapsed), 0, 160);
}
Json ProbeSeat(std::int32_t seat, const std::string& token, const ProbeOptions& options) {
const std::string claude = options.claudeBin.empty() ? FindClaudeBin() : options.claudeBin;
Json base = {{"seat", seat}, {"probedWith", options.model}, {"windows", Json::object()}};
auto with = [&](Json extra) {
Json out = base;
for (auto it = extra.begin(); it != extra.end(); ++it) out[it.key()] = it.value();
return out;
};
const std::vector<std::string> secrets = {token};
std::optional<fs::path> configDir;
if (!options.local) {
for (;;) {
configDir = fs::temp_directory_path() / std::format("rg-seat-{}-{}", seat, TokenHex(4));
if (fs::create_directory(*configDir)) break;
}
fs::permissions(*configDir, fs::perms::owner_all, fs::perm_options::replace);
}
const std::map<std::string, std::string> parent = Platform::Environment();
const std::map<std::string, std::string> env = options.local ? LocalEnv(parent) : (options.api ? ApiEnv(token, configDir->string(), parent) : ProbeEnv(token, configDir->string(), parent));
std::vector<std::string> argv = {claude};
std::ranges::move(ProbeArgs(options.model), std::back_inserter(argv));
if (options.local) {
argv.emplace_back("--setting-sources");
argv.emplace_back("");
argv.emplace_back("--no-session-persistence");
}
auto cleanup = [&] {
std::error_code error;
if (configDir) fs::remove_all(*configDir, error);
};
// A binary that cannot be started is said here, as an OSError, before
// the supervisor would only report it as an exit code.
const std::string path = claude.contains('/') ? claude : Which(claude, EnvOf(env, "PATH")).value_or("");
if (path.empty() || access(path.c_str(), X_OK) != 0) {
cleanup();
return with({{"state", "unknown"}, {"error", ClipReason(std::format("probe spawn failed: {}: {}", Dyn::ErrnoText(2), Dyn::Repr(claude)), secrets)}});
}
Process::Captured run;
try {
run = Process::Run(Process::Supervised(argv), std::nullopt, std::chrono::milliseconds(static_cast<std::int64_t>(options.timeoutS * 1000)), {}, env);
} catch (const std::exception& error) {
cleanup();
return with({{"state", "unknown"}, {"error", ClipReason(std::format("probe spawn failed: {}", error.what()), secrets)}});
}
cleanup();
if (run.timedOut) return with({{"state", "unknown"}, {"error", std::format("probe timeout after {} ms", static_cast<std::int64_t>(options.timeoutS * 1000))}});
const std::string out = Dyn::UniversalNewlines(Dyn::DecodeReplace(run.out));
const std::string err = Dyn::UniversalNewlines(Dyn::DecodeReplace(run.err));
const std::int32_t code = Process::SupervisedReturnCode(run.returnCode);
const Json parsed = ParseStream(out);
const Json& rate = parsed.at("rateLimit");
const Json& result = parsed.at("result");
const Json cost = Dyn::Get(Dyn::Or(result, Dyn::EmptyDict()), "total_cost_usd");
auto said = [&](std::string_view fallback) {
const std::string text = ResultText(result);
return !text.empty() ? text : (!err.empty() ? err : std::string(fallback));
};
if (options.api) {
// No rate windows on a Console key: the probe's result line is the
// whole reading.
if (Dyn::Truthy(result) && !Dyn::Truthy(Dyn::Get(result, "is_error"))) return with({{"state", "ok"}, {"costUsd", cost}});
if (LooksLikeCreditExhaustion(result, err)) return with({{"state", "out"}, {"error", ClipReason(said("credit balance too low"), secrets)}});
if (LooksLikeAuthFailure(result, err)) return with({{"state", "invalid"}, {"error", ClipReason(said("authentication failed"), secrets)}});
return with({{"state", "unknown"}, {"error", ClipReason(std::format("probe failed (exit {}) {}", code, said("")), secrets)}});
}
if (Dyn::Truthy(rate) && Dyn::Truthy(rate.at("windows"))) {
const auto [util, binding] = SeatUtil(rate.at("windows"));
Json resets = Dyn::Get(Dyn::Or(Dyn::Get(rate.at("windows"), "five_hour"), Dyn::EmptyDict()), "resetsAt");
if (!Dyn::Truthy(resets)) resets = Dyn::Get(rate, "resetsAt");
if (rate.at("status") == "rejected") {
// The one-word probe was refused: the seat takes nothing until
// its reset, whatever its windows read.
std::string why = std::format("{} rejected", Dyn::StrOr(Dyn::Get(rate, "rateLimitType"), "window"));
if (Dyn::Truthy(Dyn::Get(rate, "overageReason"))) why += std::format(", overage {}", Dyn::Str(rate.at("overageReason")));
const Json limitedUtil = AsDouble(util) > 100 ? util : Json(100);
return with({{"state", "limited"}, {"windows", rate.at("windows")}, {"util", limitedUtil},
{"binding", Dyn::Truthy(Dyn::Get(rate, "rateLimitType")) ? rate.at("rateLimitType") : Json(binding)}, {"resetsAt", resets}, {"error", why}});
}
return with({{"state", "ok"}, {"windows", rate.at("windows")}, {"util", util}, {"binding", binding}, {"resetsAt", resets}});
}
if (LooksLikeAuthFailure(result, err)) return with({{"state", "invalid"}, {"error", ClipReason(said("authentication failed"), secrets)}});
return with({{"state", "unknown"}, {"error", ClipReason(std::format("no rate_limit_event (exit {}) {}", code, said("")), secrets)}});
}
double Quantize(double util) {
return JsRound(util / RankQuantum) * RankQuantum;
}
std::pair<Json, std::string> BindingWindow(const Json& windows) {
Json util = 0;
std::string binding;
for (auto it = windows.begin(); it != windows.end(); ++it) {
const Json& value = Dyn::At(it.value(), "utilization");
if (Less(util, value) || binding.empty()) {
util = value;
binding = it.key();
}
}
return {util, binding};
}
std::pair<Json, std::string> SeatUtil(const Json& windows) {
const auto [util, binding] = BindingWindow(windows);
return {Json(static_cast<std::int64_t>(Quantize(AsDouble(util)))), binding};
}
std::string ModelFamily(std::string_view model) {
const std::u32string text = Dyn::Decode(model);
for (std::size_t at = 0; at < text.size(); ++at) {
for (const std::string_view family : ModelFamilies) {
if (MatchCi(text, at, family)) return std::string(family);
}
}
return "";
}
bool IsFableWindow(std::string_view key) {
return key == FableWindow || ModelFamily(key) == "fable";
}
const Json* FableWindowOf(const Json& seat) {
const Json& windows = Dyn::Or(Dyn::Get(seat, "windows"), Dyn::EmptyDict());
for (auto it = windows.begin(); it != windows.end(); ++it) {
if (IsFableWindow(it.key())) return &it.value();
}
return nullptr;
}
bool LongWindowsOk(const Json& seat) {
const Json& windows = Dyn::Or(Dyn::Get(seat, "windows"), Dyn::EmptyDict());
for (auto it = windows.begin(); it != windows.end(); ++it) {
if (it.key() == "five_hour" || IsFableWindow(it.key())) continue;
if (AsDouble(Dyn::At(it.value(), "utilization")) >= LongWindowCutoff) return false;
}
return true;
}
bool IsEligible(const Json& seat, const Json& config) {
return Dyn::At(seat, "state") == "ok" && NumberOr0(Dyn::Get(seat, "util")) <= AsDouble(config.at("maxUtil")) && LongWindowsOk(seat);
}
bool FableOpen(const Json& seat, const Json& config) {
const Json& fable = Dyn::Or(Dyn::Get(seat, "fable"), Dyn::EmptyDict());
if (Dyn::Get(fable, "state") == "limited" || (Dyn::At(seat, "state") == "limited" && IsFableWindow(Dyn::StrOr(Dyn::Get(seat, "binding"), "")))) return false;
const Json* w = FableWindowOf(seat);
if (!w) return true;
const double u = AsDouble(Dyn::At(*w, "utilization"));
return u <= AsDouble(config.at("maxUtil")) && u < LongWindowCutoff;
}
Json PickNext(const std::vector<Json>& seats, const Json& config) {
if (seats.empty()) return {{"kind", "stop"}, {"status", "error"}, {"reason", "no seat token configured"}};
std::vector<Json> usable;
for (const Json& s : SortedBySeat(seats)) {
if (Dyn::At(s, "state") != "invalid") usable.push_back(s);
}
if (usable.empty()) {
std::vector<std::string> ids;
for (const Json& s : seats) ids.push_back(Dyn::Str(Dyn::At(s, "seat")));
return {{"kind", "stop"}, {"status", "error"}, {"reason", std::format("no valid seat token (seat{} {} rejected)", seats.size() == 1 ? "" : "s", Dyn::Join(ids, ", "))}};
}
if (std::ranges::all_of(usable, [](const Json& s) { return Dyn::At(s, "state") == "unknown"; })) {
return {{"kind", "seat"}, {"seat", usable[0].at("seat")}, {"source", "round-robin"}};
}
const double maxUtil = AsDouble(config.at("maxUtil"));
const double cost = AsDouble(config.at("lensCostPct"));
auto projected = [&](const Json& s) {
return Dyn::At(s, "state") == "unknown" || Dyn::Get(s, "util").is_null() ? maxUtil : AsDouble(Dyn::At(s, "util"));
};
auto longOk = [&](const Json& s) { return Dyn::At(s, "state") == "unknown" || LongWindowsOk(s); };
std::vector<const Json*> pool;
for (const Json& s : usable) {
if (projected(s) + cost <= maxUtil && longOk(s)) pool.push_back(&s);
}
const Json* chosen = nullptr;
auto least = [&](const std::vector<const Json*>& candidates) {
const Json* best = candidates.front();
for (const Json* s : candidates) {
if (projected(*s) < projected(*best)) best = s;
}
return best;
};
if (!pool.empty()) {
chosen = config.at("policy") == "pack" ? pool.front() : least(pool);
} else {
// Nothing fits under the ceiling: the most headroom among the seats
// whose long windows are open, else any.
std::vector<const Json*> candidates;
for (const Json& s : usable) {
if (longOk(s)) candidates.push_back(&s);
}
if (candidates.empty()) {
for (const Json& s : usable) candidates.push_back(&s);
}
chosen = least(candidates);
}
return {{"kind", "seat"}, {"seat", chosen->at("seat")}, {"source", "probe"}};
}
bool NeedsHaiku(const Json& seat) {
const Json& state = Dyn::At(seat, "state");
return state == "unknown" || state == "invalid" || (state == "limited" && IsFableWindow(Dyn::StrOr(Dyn::Get(seat, "binding"), "")));
}
std::string FableState(const Json& fable) {
if (Dyn::At(fable, "state") == "limited") return IsFableWindow(Dyn::StrOr(Dyn::Get(fable, "binding"), "")) ? "limited" : "ok";
return Dyn::Str(Dyn::At(fable, "state"));
}
Json MergeFableProbe(Json base, const Json& fable) {
if (const Json* w = FableWindowOf(fable)) {
Json windows = Json::object();
const Json& own = Dyn::Or(Dyn::Get(base, "windows"), Dyn::EmptyDict());
for (auto it = own.begin(); it != own.end(); ++it) {
if (!IsFableWindow(it.key())) windows[it.key()] = it.value();
}
windows[std::string(FableWindow)] = *w;
base["windows"] = std::move(windows);
}
if (Dyn::At(base, "state") == "ok") {
// The seat's utilisation is its own windows' — the Fable window
// gates Fable work only (FableOpen): a drained Fable window makes an
// Opus seat, never an idle one.
Json own = Json::object();
for (auto it = base.at("windows").begin(); it != base.at("windows").end(); ++it) {
if (!IsFableWindow(it.key())) own[it.key()] = it.value();
}
const auto [util, binding] = SeatUtil(own);
base["util"] = util;
base["binding"] = binding;
}
base["fable"] = {{"state", FableState(fable)}};
const Json& state = Dyn::At(fable, "state");
if (state != "ok" && state != "limited" && Dyn::Truthy(Dyn::Get(fable, "error"))) base["fable"]["error"] = fable.at("error");
return base;
}
std::string FindClaudeBin(const std::map<std::string, std::string>& env, const std::string& home) {
for (const std::string_view variable : {"CLAUDE_BIN", "REVIEW_GATES_CLAUDE_BIN"}) {
if (const std::string& value = EnvOf(env, std::string(variable)); !value.empty()) return value;
}
if (const std::string& path = EnvOf(env, "PATH"); !path.empty()) {
if (const std::optional<std::string> found = Which("claude", path)) return *found;
}
std::vector<std::string> found;
for (const std::string_view extensions : {".vscode-oss/extensions", ".vscode/extensions", ".vscode-insiders/extensions", ".cursor/extensions"}) {
const fs::path dir = fs::path(home) / extensions;
std::error_code error;
if (!fs::is_directory(dir, error)) continue;
std::vector<std::string> names;
for (const fs::directory_entry& entry : fs::directory_iterator(dir, error)) {
const std::string name = entry.path().filename().string();
if (name.starts_with("anthropic.claude-code-")) names.push_back(name);
}
std::ranges::stable_sort(names, [](const std::string& a, const std::string& b) { return VersionKey(a) > VersionKey(b); });
for (const std::string& name : names) {
const fs::path binary = dir / name / "resources" / "native-binary" / "claude";
if (fs::exists(binary, error)) found.push_back(binary.string());
}
}
for (const fs::path local : {fs::path(home) / ".claude" / "local" / "claude", fs::path(home) / ".local" / "bin" / "claude"}) {
std::error_code error;
if (fs::exists(local, error)) found.push_back(local.string());
}
return found.empty() ? std::string("claude") : found.front();
}
std::string FindClaudeBin() {
return FindClaudeBin(Platform::Environment(), Platform::Home());
}
Zone ZoneNamed(std::string_view name) {
// ZoneInfo(name): a zone the system's zoneinfo holds, else none.
std::error_code error;
const bool known = name == "UTC" || fs::is_regular_file(fs::path("/usr/share/zoneinfo") / name, error);
return {known ? std::string(name) : std::string(), std::string(name)};
}
Zone LocalZone() {
std::string name = Dyn::LStrip(Platform::GetEnv("TZ").value_or(""), ":");
if (name.empty()) {
std::error_code error;
const std::string target = fs::weakly_canonical("/etc/localtime", error).string();
const std::size_t at = target.find("/zoneinfo/");
if (at != std::string::npos) name = target.substr(at + 10);
}
if (!name.empty()) {
Zone zone = ZoneNamed(name);
if (!zone.name.empty()) return zone;
}
return {"", name.empty() ? Platform::LocalTime("%Z") : name};
}
namespace {
// The broken-down time of `t` in the zone: the C library's, with TZ
// set to the zone's name for the call.
std::tm InZone(std::time_t t, const Zone& zone) {
static std::mutex lock;
const std::lock_guard guard(lock);
std::tm out {};
if (zone.name.empty()) {
localtime_r(&t, &out);
return out;
}
const char* before = getenv("TZ");
const std::optional<std::string> saved = before ? std::optional<std::string>(before) : std::nullopt;
setenv("TZ", zone.name.c_str(), 1);
tzset();
localtime_r(&t, &out);
if (saved) {
setenv("TZ", saved->c_str(), 1);
} else {
unsetenv("TZ");
}
tzset();
return out;
}
}
std::string Clock(double epoch, const Zone& zone) {
const std::tm d = InZone(static_cast<std::time_t>(std::floor(epoch)), zone);
return std::format("{:02d}:{:02d}", d.tm_hour, d.tm_min);
}
std::optional<std::string> BurstWindow(const Json& windows) {
for (const std::string_view window : {std::string_view("five_hour"), std::string_view("seven_day")}) {
if (!Dyn::Get(Dyn::Or(Dyn::Get(windows, window), Dyn::EmptyDict()), "utilization").is_null()) return std::string(window);
}
return std::nullopt;
}
std::string WindowCell(const Json& windows, std::string_view window) {
const Json& u = Dyn::Get(Dyn::Or(Dyn::Get(windows, window), Dyn::EmptyDict()), "utilization");
if (!u.is_null()) return std::format("{}%", Fmt(u));
return window == "five_hour" && BurstWindow(windows) == "seven_day" ? std::string("no limit") : std::string("—");
}
std::string FormatWhen(double epoch, double now, const Zone& zone) {
const auto diffMin = static_cast<std::int64_t>(JsRound((epoch - now) / 60));
std::string relative;
if (diffMin <= 0) {
relative = "now";
} else if (diffMin < 60) {
relative = std::format("in {} min", diffMin);
} else if (diffMin < 48 * 60) {
relative = std::format("in {}h {:02d}m", diffMin / 60, diffMin % 60);
} else {
relative = std::format("in {}d {}h", diffMin / 1440, (diffMin % 1440) / 60);
}
const std::tm d = InZone(static_cast<std::time_t>(std::floor(epoch)), zone);
std::string clock = std::format("{:02d}:{:02d}", d.tm_hour, d.tm_min);
if (epoch - now > 24 * 3600) {
// Monday is 0 here; tm_wday counts from Sunday.
const auto index = static_cast<std::size_t>((d.tm_wday + 6) % 7);
clock = std::format("{} {:02d} {}, {}", Weekdays[index], d.tm_mday, Months[static_cast<std::size_t>(d.tm_mon)], clock);
}
return std::format("{} ({})", clock, relative);
}
std::string RenderTable(const std::vector<std::vector<std::string>>& rows) {
std::vector<std::size_t> widths(rows.at(0).size(), 0);
for (const std::vector<std::string>& row : rows) {
for (std::size_t i = 0; i < widths.size(); ++i) widths[i] = std::max(widths[i], Dyn::Length(row.at(i)));
}
auto line = [&](const std::vector<std::string>& row) {
std::vector<std::string> cells;
for (std::size_t i = 0; i < widths.size(); ++i) cells.push_back(Dyn::LJust(row[i], widths[i]));
return std::format("| {} |", Dyn::Join(cells, " | "));
};
std::vector<std::string> out = {line(rows[0])};
std::vector<std::string> dashes;
for (const std::size_t width : widths) dashes.emplace_back(width + 2, '-');
out.push_back(std::format("|{}|", Dyn::Join(dashes, "|")));
for (std::size_t r = 1; r < rows.size(); ++r) out.push_back(line(rows[r]));
return Dyn::Join(out, "\n");
}
std::string RenderStatusReport(const std::vector<Json>& seats, const Json& config, double now, const Zone& zone, const Json& board,
const std::map<std::int32_t, std::string>& keys) {
const std::vector<Json> ordered = SortedBySeat(seats);
const bool anyInflight = std::ranges::any_of(ordered, [&](const Json& s) { return Inflight(board, keys, s) != 0; });
std::vector<std::vector<std::string>> rows;
std::vector<std::string> header = {"seat", "5h", "resets", "7d", "resets", "fable 7d"};
if (anyInflight) header.emplace_back("in flight");
rows.push_back(header);
for (const Json& s : ordered) {
auto w = [&](std::string_view k) { return WindowCell(Dyn::Or(Dyn::Get(s, "windows"), Dyn::EmptyDict()), k); };
auto when = [&](std::string_view k) {
const Json& at = Dyn::Get(Dyn::Or(Window(s, k), Dyn::EmptyDict()), "resetsAt");
return Dyn::Truthy(at) ? FormatWhen(AsDouble(at), now, zone) : std::string("—");
};
const Json* fw = FableWindowOf(s);
const Json& f = Dyn::Get(s, "fable");
std::string fable;
if (Dyn::Truthy(f) && Dyn::Get(f, "state") == "limited") {
fable = std::format("limited{}", fw ? std::format(" ({}%)", Fmt(Dyn::At(*fw, "utilization"))) : std::string());
} else if (fw) {
fable = std::format("{}%", Fmt(Dyn::At(*fw, "utilization")));
} else {
fable = Dyn::Truthy(f) ? Dyn::Str(Dyn::At(f, "state")) : std::string("—");
}
std::vector<std::string> row = {SeatCell(s), w("five_hour"), when("five_hour"), w("seven_day"), when("seven_day"), fable};
if (anyInflight) {
const std::int64_t n = Inflight(board, keys, s);
row.push_back(n ? std::to_string(n) : "—");
}
rows.push_back(std::move(row));
}
std::vector<Json> eligible;
for (const Json& s : ordered) {
if (IsEligible(s, config)) eligible.push_back(s);
}
std::ranges::stable_sort(eligible, [](const Json& a, const Json& b) { return NumberOr0(Dyn::Get(a, "util")) < NumberOr0(Dyn::Get(b, "util")); });
const Json pick = PickNext(seats, config);
const std::string next = pick.at("kind") == "seat" ? SeatName({{"seat", pick.at("seat")}}) : std::format("{}: {}", Dyn::Str(pick.at("status")), Dyn::Str(pick.at("reason")));
std::vector<std::string> limited;
std::vector<std::string> unusable;
std::vector<std::string> fableDead;
for (const Json& s : ordered) {
const Json& state = Dyn::At(s, "state");
if (state == "limited") {
const Json& resets = Dyn::Get(s, "resetsAt");
limited.push_back(std::format("{} until {}", SeatName(s), Dyn::Truthy(resets) ? FormatWhen(AsDouble(resets), now, zone) : std::string("?")));
}
if (state != "ok" && state != "limited") {
unusable.push_back(std::format("{} {}{}", SeatName(s), Dyn::Str(state), Dyn::Truthy(Dyn::Get(s, "error")) ? std::format(" ({})", Dyn::Str(s.at("error"))) : std::string()));
}
const Json& fableOfSeat = Dyn::Or(Dyn::Get(s, "fable"), Dyn::EmptyDict());
const Json& fableStateOfSeat = Dyn::Get(fableOfSeat, "state");
if ((state == "ok" || state == "limited") && !fableStateOfSeat.is_null() && fableStateOfSeat != "ok" && fableStateOfSeat != "limited") {
fableDead.push_back(std::format("{} {}{}", SeatName(s), Dyn::Str(fableStateOfSeat),
Dyn::Truthy(Dyn::Get(fableOfSeat, "error")) ? std::format(" ({})", Dyn::Str(fableOfSeat.at("error"))) : std::string()));
}
}
const bool fableSeen = std::ranges::any_of(seats, [](const Json& s) { return FableWindowOf(s) != nullptr; });
std::vector<Json> fableRoom;
for (const Json& s : ordered) {
if (Dyn::At(s, "state") == "ok" && FableWindowOf(s) && FableOpen(s, config)) fableRoom.push_back(s);
}
std::ranges::stable_sort(fableRoom, [](const Json& a, const Json& b) { return Less(Dyn::At(*FableWindowOf(a), "utilization"), Dyn::At(*FableWindowOf(b), "utilization")); });
const double maxUtil = AsDouble(config.at("maxUtil"));
std::vector<std::string> lines = {RenderTable(rows), ""};
lines.push_back(std::format("policy {} · ceiling {}% · handover at {}% · a lens is placed at +{}% · 7d cutoff {}%", Dyn::Str(config.at("policy")), Fmt(config.at("maxUtil")),
Fmt(config.at("handoverPct")), Fmt(config.at("lensCostPct")), LongWindowCutoff));
std::vector<std::string> eligibleText;
for (const Json& s : eligible) eligibleText.push_back(std::format("{} ({}% headroom)", SeatName(s), Fmt(Json(maxUtil - NumberOr0(Dyn::Get(s, "util"))))));
lines.push_back(std::format("eligible now: {} → next lens: {}", eligible.empty() ? std::string("none") : Dyn::Join(eligibleText, ", "), next));
if (fableSeen) {
std::vector<std::string> room;
for (const Json& s : fableRoom) room.push_back(std::format("{} ({}% fable headroom)", SeatName(s), Fmt(Json(maxUtil - AsDouble(Dyn::At(*FableWindowOf(s), "utilization"))))));
lines.push_back(std::format("fable lenses: {}", fableRoom.empty() ? std::string("no seat with room on its Fable window — Fable lenses run on their fallback model") : Dyn::Join(room, ", ")));
}
if (!limited.empty()) lines.push_back(std::format("limited: {}", Dyn::Join(limited, "; ")));
if (!unusable.empty()) lines.push_back(std::format("not usable: {}", Dyn::Join(unusable, "; ")));
if (!fableDead.empty()) lines.push_back(std::format("fable probe: {}", Dyn::Join(fableDead, "; ")));
return Dyn::Join(lines, "\n");
}
std::string RenderApiReport(const std::vector<Json>& apiSeats, const Json& apiConfig, double now, const Json& board, const std::map<std::int32_t, std::string>& keys) {
const std::string today = Platform::LocalTime("%Y-%m-%d", now);
// "17 Sept"
auto date = [](const std::string& iso) {
return std::format("{} {}", std::stoi(iso.substr(8, 2)), Months[static_cast<std::size_t>(std::stoi(iso.substr(5, 2)) - 1)]);
};
std::vector<std::vector<std::string>> rows = {{"seat", "probe", "driver", "driver spend this cycle", "procs", "cycle from", "grant day", "next credits", "in flight"}};
std::vector<std::string> notes;
for (const Json& s : SortedBySeat(apiSeats)) {
const std::string id = std::format("api{}", Dyn::Str(Dyn::At(s, "seat")));
const Json& ledger = Dyn::Or(Dyn::Get(s, "ledger"), Dyn::EmptyDict());
const double spent = NumberOr0(Dyn::Get(ledger, "spentUsd"));
const std::string state = Dyn::Str(Dyn::At(s, "state"));
const std::string probe = state + (Dyn::Truthy(Dyn::Get(s, "error")) && state != "ok" ? std::format(" ({})", Dyn::Str(s.at("error"))) : std::string());
// The driver's verdict, SeatPool::LedgerOut's.
const Json& ledgerOut = Dyn::Get(s, "ledgerOut");
const bool out = !ledgerOut.is_null();
const std::string driver = !out ? std::string("ok")
: ledgerOut == "credit" ? std::format("out (credit since {})", Dyn::Replace(Dyn::Slice(Dyn::Str(Dyn::Get(ledger, "outSince")), 0, 16), "T", " "))
: std::string("out (budget spent)");
const std::string due = Dyn::StrOr(Dyn::Get(s, "nextCredits"), "");
const std::string dueText = due.size() < 10 ? std::string("—") : due == today ? std::string("today") : due < today ? std::format("overdue, {}", date(due)) : date(due);
const std::int64_t n = Inflight(board, keys, s);
rows.push_back({id, probe, driver, std::format("${:.2f} of ${}", spent, Fmt(apiConfig.at("budgetUsd"))), Dyn::StrOr(Dyn::Get(ledger, "procs"), "0"),
Dyn::StrOr(Dyn::Get(ledger, "cycle"), "—"), Dyn::StrOr(Dyn::Get(s, "grantDay"), "—"), dueText,
n ? std::format("{} of {}", n, Dyn::Str(apiConfig.at("concurrency"))) : std::string("—")});
const Json& event = Dyn::Get(s, "event");
if (event == "new-grant") {
notes.push_back(std::format("{}: the key answers again - new credits; the ledger cycle restarted (from {})", id, Dyn::StrOr(Dyn::Get(ledger, "cycle"), "—")));
} else if (event == "credit-gone") {
notes.push_back(std::format("{}: the credit error, now recorded in the ledger", id));
} else if (state == "ok" && ledgerOut == "credit") {
notes.push_back(std::format("{}: the key answered, but the credit error was recorded since the probe started - the ledger's verdict stands", id));
} else if (state == "ok" && out) {
notes.push_back(std::format("{}: the key answers, but the driver's budget for this cycle is spent (REVIEW_GATES_API_BUDGET_USD)", id));
} else if (state != "ok" && state != "out" && out) {
notes.push_back(std::format("{}: probe {} - the ledger's verdict stands", id, state));
}
}
std::vector<std::string> lines = {RenderTable(rows)};
lines.insert(lines.end(), notes.begin(), notes.end());
lines.push_back(std::format("{} seats run first (REVIEW_GATES_SEAT_ORDER={}) · budget ${} per seat per cycle · a cycle starts on the seat's grant day (ANTHROPIC_API_KEY<n>_GRANT_DAY, else "
"REVIEW_GATES_API_CYCLE_DAY={}) · {} processes per seat · placed at +${} each",
Dyn::Str(apiConfig.at("order")) == "api-first" ? "api" : "oauth", Dyn::Str(apiConfig.at("order")), Fmt(apiConfig.at("budgetUsd")), Dyn::Str(apiConfig.at("cycleDay")),
Dyn::Str(apiConfig.at("concurrency")), Fmt(apiConfig.at("lensCostUsd"))));
lines.emplace_back("the balance itself is in the Console (Settings > Billing > Promotional credits); the ledger counts only what the review drivers spent, and \"overdue\" means the grant day "
"is wrong");
return Dyn::Join(lines, "\n");
}
std::string RenderDrivers(const Json& board, double now, const Zone& zone) {
const Json& drivers = Dyn::Or(Dyn::Get(Dyn::Or(board, Dyn::EmptyDict()), "drivers"), Dyn::EmptyDict());
if (!Dyn::Truthy(drivers)) return std::format("drivers: none running (the board at {})", Dyn::StrOr(Dyn::Get(Dyn::Or(board, Dyn::EmptyDict()), "path"), "?"));
std::vector<std::pair<std::string, const Json*>> sorted;
for (auto it = drivers.begin(); it != drivers.end(); ++it) sorted.emplace_back(it.key(), &it.value());
std::ranges::stable_sort(sorted, [](const auto& a, const auto& b) { return NumberOr0(Dyn::Get(*a.second, "since")) < NumberOr0(Dyn::Get(*b.second, "since")); });
std::vector<std::string> parts;
std::int64_t total = 0;
for (const auto& [key, driver] : sorted) {
const Json& d = *driver;
const Json& settings = Dyn::Or(Dyn::Get(d, "settings"), Dyn::EmptyDict());
std::string held;
if (Dyn::Truthy(Dyn::Get(d, "mb"))) held += std::format(", {:.0f} MB", AsDouble(d.at("mb")));
if (Dyn::Truthy(Dyn::Get(d, "stacks"))) {
std::vector<std::string> stacks;
for (const Json& stack : d.at("stacks")) stacks.push_back(Dyn::Str(stack));
std::ranges::sort(stacks);
held += std::format(", stack(s) {}", Dyn::Join(stacks, ", "));
}
const Json unknown = "?";
const Json zero = 0;
parts.push_back(std::format("pid {} ({}, {}, width {}) since {} - {} in flight{}", Dyn::Split(key, ":")[0], Dyn::Str(Dyn::Has(d, "run") ? d.at("run") : unknown),
Dyn::Str(Dyn::Has(d, "effort") ? d.at("effort") : unknown), Dyn::Str(Dyn::Has(settings, "concurrency") ? settings.at("concurrency") : unknown),
FormatWhen(NumberOr0(Dyn::Get(d, "since")), now, zone), Dyn::Str(Dyn::Has(d, "inflight") ? d.at("inflight") : zero), held));
total += static_cast<std::int64_t>(NumberOr0(Dyn::Get(d, "inflight")));
}
return std::format("drivers ({} in flight on this machine; a width is that driver's cap): {}", total, Dyn::Join(parts, "; "));
}
}