feat(seats): memory, the Playwright MCP config, and the seats library
:Memory: MemAvailable and the reserve policy, the sampler of a pass's peak
tree PSS (one sweep, a pause of three sweeps at least, stopped with the
driver), and the calibration file every driver on the machine shares,
re-read and written under its lock.
:Mcp: the pinned @playwright/mcp 0.0.83 arguments and the --mcp-config of
one pass, compact and in a fixed key order.
:Seats: the dotenv loader (token lines only, TOKEN7 inert), the one-word
probe (run under the supervisor, a missing binary reported with its errno
text), the window maths, the ranking, the Fable merge, the editor-bundle
lookup of the claude binary, the redaction of anything token-shaped, and
the report. This libc++ has no tzdb, so a zone by name converts through
the C library with TZ set, reading the system's zoneinfo.
tests/Seats: 92 checks against reference vectors, numbered in order, all
green.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 07:32:14 +01:00
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
|
|
|
|
|
|
|
|
|
export module Crafter.Review:Seats;
|
|
|
|
|
import std;
|
|
|
|
|
import :Json;
|
|
|
|
|
|
|
|
|
|
// How much usage each Claude seat has left: the dotenv loader, the one-word
|
|
|
|
|
// probe, the window maths, the ranking and the report. The seat pool
|
|
|
|
|
// (:SeatPool) places on these; `crafter-review seats` prints the report (its
|
|
|
|
|
// glue, which needs the ledger and the board, is in :Driver).
|
|
|
|
|
//
|
|
|
|
|
// Seats are CLAUDE_CODE_OAUTH_TOKEN1..6 by fixed name (the legacy
|
|
|
|
|
// CLAUDE_CODE_OAUTH_TOKEN is seat 1 when TOKEN1 is empty), API-credit seats
|
|
|
|
|
// ANTHROPIC_API_KEY1..6, OpenRouter seats OPENROUTER_KEY[1..6]; never printed:
|
|
|
|
|
// the table and the JSON carry indices and percentages only. A probe result
|
|
|
|
|
// and a seat are JSON: the dump and --json show them.
|
|
|
|
|
export namespace Crafter::Review::Seats {
|
|
|
|
|
constexpr std::int32_t MaxSeats = 6;
|
|
|
|
|
constexpr std::string_view ProbeModel = "haiku";
|
|
|
|
|
// The Fable weekly limit is a window of its own, reported ONLY in the
|
|
|
|
|
// rate_limit_event of a Fable request (verified 2026-09-08). The CLI has
|
|
|
|
|
// named it seven_day_overage_included and seven_day_fable; either lands
|
|
|
|
|
// under the canonical key.
|
|
|
|
|
constexpr std::string_view FableProbeModel = "claude-fable-5-1";
|
|
|
|
|
constexpr std::string_view FableWindow = "seven_day_overage_included";
|
|
|
|
|
constexpr double ProbeTimeoutS = 30;
|
|
|
|
|
// Fixed rules, not knobs: utilisation is rounded to RankQuantum before
|
|
|
|
|
// ranking, and a seat whose 7-day window is at or above LongWindowCutoff is
|
|
|
|
|
// out regardless — its reset is days away. The Fable window is exempt here.
|
2026-10-31 21:13:55 +01:00
|
|
|
// 99 since 2026-10-15 (95 before): the weekly window is the budget the
|
|
|
|
|
// lenses run out of, and the pool projects its running passes against the
|
|
|
|
|
// cutoff (SeatPool::LensCost7dPct), so the last percent is spent a pass at
|
|
|
|
|
// a time, not in a burst.
|
feat(seats): memory, the Playwright MCP config, and the seats library
:Memory: MemAvailable and the reserve policy, the sampler of a pass's peak
tree PSS (one sweep, a pause of three sweeps at least, stopped with the
driver), and the calibration file every driver on the machine shares,
re-read and written under its lock.
:Mcp: the pinned @playwright/mcp 0.0.83 arguments and the --mcp-config of
one pass, compact and in a fixed key order.
:Seats: the dotenv loader (token lines only, TOKEN7 inert), the one-word
probe (run under the supervisor, a missing binary reported with its errno
text), the window maths, the ranking, the Fable merge, the editor-bundle
lookup of the claude binary, the redaction of anything token-shaped, and
the report. This libc++ has no tzdb, so a zone by name converts through
the C library with TZ set, reading the system's zoneinfo.
tests/Seats: 92 checks against reference vectors, numbered in order, all
green.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 07:32:14 +01:00
|
|
|
constexpr std::int32_t RankQuantum = 5;
|
2026-10-31 21:13:55 +01:00
|
|
|
constexpr std::int32_t LongWindowCutoff = 99;
|
feat(seats): memory, the Playwright MCP config, and the seats library
:Memory: MemAvailable and the reserve policy, the sampler of a pass's peak
tree PSS (one sweep, a pause of three sweeps at least, stopped with the
driver), and the calibration file every driver on the machine shares,
re-read and written under its lock.
:Mcp: the pinned @playwright/mcp 0.0.83 arguments and the --mcp-config of
one pass, compact and in a fixed key order.
:Seats: the dotenv loader (token lines only, TOKEN7 inert), the one-word
probe (run under the supervisor, a missing binary reported with its errno
text), the window maths, the ranking, the Fable merge, the editor-bundle
lookup of the claude binary, the redaction of anything token-shaped, and
the report. This libc++ has no tzdb, so a zone by name converts through
the C library with TZ set, reading the system's zoneinfo.
tests/Seats: 92 checks against reference vectors, numbered in order, all
green.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 07:32:14 +01:00
|
|
|
|
|
|
|
|
// JavaScript's Math.round: halves go up, never to even.
|
|
|
|
|
double JsRound(double x);
|
|
|
|
|
// A number the way a JS template prints it: 5 not 5.0, 62.5 as is.
|
|
|
|
|
std::string Fmt(const Json& value);
|
|
|
|
|
|
|
|
|
|
// {maxUtil, lensCostPct, waitMaxMin, policy, handoverPct}: the placement
|
|
|
|
|
// knobs; garbage leaves the default.
|
|
|
|
|
Json DefaultConfig();
|
|
|
|
|
Json ConfigFromEnv(const std::map<std::string, std::string>& env);
|
|
|
|
|
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>> ApiKeysFromEnv(const std::map<std::string, std::string>& env);
|
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 13:58:29 +01:00
|
|
|
// {n: day}: ANTHROPIC_API_KEY<n>_GRANT_DAY, the day of the month seat n's
|
|
|
|
|
// organization receives its plan's credits (the Console's
|
|
|
|
|
// Promotional-credits expiry minus three days), 1..31; anything else is
|
|
|
|
|
// ignored.
|
|
|
|
|
std::map<std::int32_t, std::int32_t> ApiGrantDays(const std::map<std::string, std::string>& env);
|
feat(seats): memory, the Playwright MCP config, and the seats library
:Memory: MemAvailable and the reserve policy, the sampler of a pass's peak
tree PSS (one sweep, a pause of three sweeps at least, stopped with the
driver), and the calibration file every driver on the machine shares,
re-read and written under its lock.
:Mcp: the pinned @playwright/mcp 0.0.83 arguments and the --mcp-config of
one pass, compact and in a fixed key order.
:Seats: the dotenv loader (token lines only, TOKEN7 inert), the one-word
probe (run under the supervisor, a missing binary reported with its errno
text), the window maths, the ranking, the Fable merge, the editor-bundle
lookup of the claude binary, the redaction of anything token-shaped, and
the report. This libc++ has no tzdb, so a zone by name converts through
the C library with TZ set, reading the system's zoneinfo.
tests/Seats: 92 checks against reference vectors, numbered in order, all
green.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 07:32:14 +01:00
|
|
|
std::vector<std::pair<std::int32_t, std::string>> OpenRouterKeysFromEnv(const std::map<std::string, std::string>& env);
|
feat(seats): the env file's settings count, and a machine's own seats file
ApiConfig and PoolConfig read the seat order, the budgets and the
concurrency from the process environment alone, so a `.env` that said
REVIEW_GATES_SEAT_ORDER=oauth-first ordered nothing: the Python driver
reads those lines from the file under the process environment
(config_env, 2026-10-17: a setting lives beside the seats it orders).
SeatPool::SettingsOf does the same - the file's placement settings
under the process environment, in a map handed to the placement readers
alone (ApiConfig, PoolConfig, Seats::ConfigFromEnv) - and leaves the
process environment untouched, so no other reader sees a line of the
file: a path's or a binary's line reaches nothing, and two drivers
reading one .env keep one board and one ledger. The placement settings
are one list, SeatPool::PlacementSettings - the fifteen names those
readers read - for the map, the run's log and what makes a working
directory's .env a seats file; a REVIEW_GATES_* line of any other name
stays out of the map and is named, by the run's log and on `seats`'s
stderr. One dotenv grammar serves the tokens and the settings
(Seats::DotenvLines).
`run` and `seats` resolve the file from their own --env-file
(SeatPool::ResolveSeatsFile): the --env-file, else $SEATS_ENV_FILE, else
the working directory's .env - the operator's, as the Python driver and
`seats` read it, not the repository under review's - when it is a file
holding a seat or a placement setting, each with a value, or one that
cannot be read as UTF-8 text (its error is then the command's) - a
readable application .env, its Vite or Supabase lines, one of blank
placeholder lines or one that is no file is passed over, and the run
says so - else the machine's own
$XDG_CONFIG_HOME/crafter-review/env, else ~/.config/crafter-review/env:
one file for every repository on a machine, a repository's seats .env
winning over it. Each file is read once and the settings and the tokens
are taken from that read (SeatsFile::Text), so a file that can be read
only once - a pipe, `--env-file <(gpg -d seats.env.gpg)`, a FIFO - gives
its settings and its seats alike, and a failed read raises its own error
where the tokens are laid over the environment: SeatPool::WithFileTokens,
for `run` and `seats` alike, and LoadSeats takes the merged environment
and no longer reads a file by path. An empty `seats --env-file` means no file
now, as it did for `run`, and the `seats` report names the side that
runs first (it said "api seats run first" whatever the order).
Runs, Commands and KernelModes, the tests that run `run` and `seats`,
point XDG_CONFIG_HOME, and HOST_XDG_CONFIG_HOME for the Flatpak, at a
path of their own that nothing creates, so a global file stays out of
them; the grammar, the settings and their precedence, the lookup and the
one read have their tests, a run with `--env` (a prefix) and with a
repeated --env-file, and a `seats` with `--env`, take tokens and
settings from the one file, an application's .env and a venv are passed
over, a FIFO is read once, no line of the file reaches `stop`, every
listed placement setting moves a reader, another REVIEW_GATES_* line is
named and a .env of blank lines passed over, and the
README, the environment table and the procedure say the new rule.
2026-11-01 01:51:48 +01:00
|
|
|
// Every `[export ]NAME=value` line of a dotenv text, in order: blanks
|
|
|
|
|
// around the `=` and the value dropped, one pair of matching quotes
|
|
|
|
|
// stripped; anything else skipped.
|
|
|
|
|
std::vector<std::pair<std::string, std::string>> DotenvLines(std::string_view text);
|
|
|
|
|
// Only the seat lines of those: the OAuth tokens, the numbered API keys
|
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 13:58:29 +01:00
|
|
|
// with their grant days and the OpenRouter keys; everything else ignored.
|
feat(seats): memory, the Playwright MCP config, and the seats library
:Memory: MemAvailable and the reserve policy, the sampler of a pass's peak
tree PSS (one sweep, a pause of three sweeps at least, stopped with the
driver), and the calibration file every driver on the machine shares,
re-read and written under its lock.
:Mcp: the pinned @playwright/mcp 0.0.83 arguments and the --mcp-config of
one pass, compact and in a fixed key order.
:Seats: the dotenv loader (token lines only, TOKEN7 inert), the one-word
probe (run under the supervisor, a missing binary reported with its errno
text), the window maths, the ranking, the Fable merge, the editor-bundle
lookup of the claude binary, the redaction of anything token-shaped, and
the report. This libc++ has no tzdb, so a zone by name converts through
the C library with TZ set, reading the system's zoneinfo.
tests/Seats: 92 checks against reference vectors, numbered in order, all
green.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 07:32:14 +01:00
|
|
|
std::map<std::string, std::string> SeatTokensFromEnvFile(std::string_view text);
|
feat(seats): the env file's settings count, and a machine's own seats file
ApiConfig and PoolConfig read the seat order, the budgets and the
concurrency from the process environment alone, so a `.env` that said
REVIEW_GATES_SEAT_ORDER=oauth-first ordered nothing: the Python driver
reads those lines from the file under the process environment
(config_env, 2026-10-17: a setting lives beside the seats it orders).
SeatPool::SettingsOf does the same - the file's placement settings
under the process environment, in a map handed to the placement readers
alone (ApiConfig, PoolConfig, Seats::ConfigFromEnv) - and leaves the
process environment untouched, so no other reader sees a line of the
file: a path's or a binary's line reaches nothing, and two drivers
reading one .env keep one board and one ledger. The placement settings
are one list, SeatPool::PlacementSettings - the fifteen names those
readers read - for the map, the run's log and what makes a working
directory's .env a seats file; a REVIEW_GATES_* line of any other name
stays out of the map and is named, by the run's log and on `seats`'s
stderr. One dotenv grammar serves the tokens and the settings
(Seats::DotenvLines).
`run` and `seats` resolve the file from their own --env-file
(SeatPool::ResolveSeatsFile): the --env-file, else $SEATS_ENV_FILE, else
the working directory's .env - the operator's, as the Python driver and
`seats` read it, not the repository under review's - when it is a file
holding a seat or a placement setting, each with a value, or one that
cannot be read as UTF-8 text (its error is then the command's) - a
readable application .env, its Vite or Supabase lines, one of blank
placeholder lines or one that is no file is passed over, and the run
says so - else the machine's own
$XDG_CONFIG_HOME/crafter-review/env, else ~/.config/crafter-review/env:
one file for every repository on a machine, a repository's seats .env
winning over it. Each file is read once and the settings and the tokens
are taken from that read (SeatsFile::Text), so a file that can be read
only once - a pipe, `--env-file <(gpg -d seats.env.gpg)`, a FIFO - gives
its settings and its seats alike, and a failed read raises its own error
where the tokens are laid over the environment: SeatPool::WithFileTokens,
for `run` and `seats` alike, and LoadSeats takes the merged environment
and no longer reads a file by path. An empty `seats --env-file` means no file
now, as it did for `run`, and the `seats` report names the side that
runs first (it said "api seats run first" whatever the order).
Runs, Commands and KernelModes, the tests that run `run` and `seats`,
point XDG_CONFIG_HOME, and HOST_XDG_CONFIG_HOME for the Flatpak, at a
path of their own that nothing creates, so a global file stays out of
them; the grammar, the settings and their precedence, the lookup and the
one read have their tests, a run with `--env` (a prefix) and with a
repeated --env-file, and a `seats` with `--env`, take tokens and
settings from the one file, an application's .env and a venv are passed
over, a FIFO is read once, no line of the file reaches `stop`, every
listed placement setting moves a reader, another REVIEW_GATES_* line is
named and a .env of blank lines passed over, and the
README, the environment table and the procedure say the new rule.
2026-11-01 01:51:48 +01:00
|
|
|
// The REVIEW_GATES_* lines of a dotenv file (quotes and `export`
|
|
|
|
|
// stripped). SeatPool::SettingsOf lays the placement settings among them
|
|
|
|
|
// (SeatPool::PlacementSettings) under the process environment in the map
|
|
|
|
|
// the placement readers take, where no other reader sees them, and names
|
|
|
|
|
// the rest.
|
|
|
|
|
std::map<std::string, std::string> SettingsFromEnvFile(std::string_view text);
|
feat(seats): memory, the Playwright MCP config, and the seats library
:Memory: MemAvailable and the reserve policy, the sampler of a pass's peak
tree PSS (one sweep, a pause of three sweeps at least, stopped with the
driver), and the calibration file every driver on the machine shares,
re-read and written under its lock.
:Mcp: the pinned @playwright/mcp 0.0.83 arguments and the --mcp-config of
one pass, compact and in a fixed key order.
:Seats: the dotenv loader (token lines only, TOKEN7 inert), the one-word
probe (run under the supervisor, a missing binary reported with its errno
text), the window maths, the ranking, the Fable merge, the editor-bundle
lookup of the claude binary, the redaction of anything token-shaped, and
the report. This libc++ has no tzdb, so a zone by name converts through
the C library with TZ set, reading the system's zoneinfo.
tests/Seats: 92 checks against reference vectors, numbered in order, all
green.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 07:32:14 +01:00
|
|
|
|
|
|
|
|
// The CLI's fraction as a percent, past 100 for a seat over its limit.
|
|
|
|
|
Json Pct(const Json& utilization);
|
|
|
|
|
// {status, rateLimitType, resetsAt, windows, overageReason}, or null.
|
|
|
|
|
Json ParseRateLimitInfo(const Json& raw);
|
|
|
|
|
// The last result line, the last rate_limit_event and the session id.
|
|
|
|
|
Json ParseStream(std::string_view text);
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> ProbeArgs(std::string_view model = ProbeModel);
|
|
|
|
|
// The probe child's whole environment: one token under the name the CLI
|
|
|
|
|
// reads, an isolated config dir, thinking off, no nonessential traffic, no
|
|
|
|
|
// updater, and none of the other pool tokens.
|
|
|
|
|
std::map<std::string, std::string> ProbeEnv(std::string_view token, const std::string& configDir, const std::map<std::string, std::string>& base);
|
|
|
|
|
// The CLI's own login: no token or API key from the environment.
|
|
|
|
|
std::map<std::string, std::string> LocalEnv(const std::map<std::string, std::string>& base);
|
|
|
|
|
// The key's approval in the config dir's .claude.json, once.
|
|
|
|
|
std::filesystem::path WriteApiKeyApproval(const std::filesystem::path& configDir, std::string_view key);
|
|
|
|
|
std::map<std::string, std::string> ApiEnv(std::string_view key, const std::string& configDir, const std::map<std::string, std::string>& base);
|
|
|
|
|
bool LooksLikeCreditExhaustion(const Json& result, std::string_view text);
|
|
|
|
|
bool LooksLikeAuthFailure(const Json& result, std::string_view text);
|
|
|
|
|
// One line, at most 160 characters, the seat's own secrets and anything
|
|
|
|
|
// token-shaped redacted.
|
|
|
|
|
std::string ClipReason(std::string_view text, std::span<const std::string> secrets = {});
|
|
|
|
|
|
|
|
|
|
struct ProbeOptions {
|
|
|
|
|
std::string model = std::string(ProbeModel);
|
|
|
|
|
double timeoutS = ProbeTimeoutS;
|
|
|
|
|
std::string claudeBin;
|
|
|
|
|
bool local = false;
|
|
|
|
|
bool api = false;
|
|
|
|
|
};
|
|
|
|
|
// One probe of one seat: {seat, probedWith, windows, state: ok | limited |
|
|
|
|
|
// unknown | invalid | out, util, binding, resetsAt, error, costUsd}.
|
|
|
|
|
Json ProbeSeat(std::int32_t seat, const std::string& token, const ProbeOptions& options);
|
|
|
|
|
|
|
|
|
|
double Quantize(double util);
|
|
|
|
|
// The most-used window and its utilisation, raw.
|
|
|
|
|
std::pair<Json, std::string> BindingWindow(const Json& windows);
|
|
|
|
|
// Quantised for ranking.
|
|
|
|
|
std::pair<Json, std::string> SeatUtil(const Json& windows);
|
feat(seats): accounts without a 5-hour limit
Anthropic is removing the 5-hour limit in stages (from 2026-10-31), and
an account without it reports the weekly window alone. Until now the pool
took such an account for an unprobed seat: always eligible, ranked after
every other, never held to the weekly cutoff, with no cap on its passes.
- The burst window is the 5-hour one, else the weekly one
(Seats::BurstWindow). An account whose burst window is the weekly one is
ranked by it and admitted under the 95 % cutoff with the passes in
flight projected on it, at the measured share per pass (0.04 at xhigh,
0.125 otherwise; REVIEW_GATES_LENS_COST_7D_PCT), and waits for the weekly
reset when over it.
- Every OAuth account and the local login takes at most 128 passes at once,
every driver's counted (REVIEW_GATES_OAUTH_CONCURRENCY): every run
measured with 149 or more in flight on one account drew usage-limit
rejections, none of the 119 with 139 or fewer.
- A 'concurrents' rejection pauses the seat for 20 s or its retry-after,
not until a window's reset (a seat sat two hours at 10 % on one).
- Board entries, the folded foreign record and the board's view carry
cost7d, so the drivers see each other's weekly load.
- `seats` and the log table say `no limit` in such an account's 5-hour
column; the board's others-line names its weekly projection.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 17:07:17 +01:00
|
|
|
// The window a burst of passes moves: the 5-hour one, else the weekly one.
|
|
|
|
|
// Anthropic removes the 5-hour limit in stages (from 2026-10-31), and an
|
|
|
|
|
// account without it reports no 5-hour window. nullopt for a seat with no
|
|
|
|
|
// reading.
|
|
|
|
|
std::optional<std::string> BurstWindow(const Json& windows);
|
|
|
|
|
// A window's utilisation as a report cell: `no limit` in the 5-hour column
|
|
|
|
|
// of an account without that limit.
|
|
|
|
|
std::string WindowCell(const Json& windows, std::string_view window);
|
feat(seats): memory, the Playwright MCP config, and the seats library
:Memory: MemAvailable and the reserve policy, the sampler of a pass's peak
tree PSS (one sweep, a pause of three sweeps at least, stopped with the
driver), and the calibration file every driver on the machine shares,
re-read and written under its lock.
:Mcp: the pinned @playwright/mcp 0.0.83 arguments and the --mcp-config of
one pass, compact and in a fixed key order.
:Seats: the dotenv loader (token lines only, TOKEN7 inert), the one-word
probe (run under the supervisor, a missing binary reported with its errno
text), the window maths, the ranking, the Fable merge, the editor-bundle
lookup of the claude binary, the redaction of anything token-shaped, and
the report. This libc++ has no tzdb, so a zone by name converts through
the C library with TZ set, reading the system's zoneinfo.
tests/Seats: 92 checks against reference vectors, numbered in order, all
green.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 07:32:14 +01:00
|
|
|
std::string ModelFamily(std::string_view model);
|
|
|
|
|
bool IsFableWindow(std::string_view key);
|
|
|
|
|
const Json* FableWindowOf(const Json& seat);
|
|
|
|
|
bool LongWindowsOk(const Json& seat);
|
|
|
|
|
bool IsEligible(const Json& seat, const Json& config);
|
|
|
|
|
bool FableOpen(const Json& seat, const Json& config);
|
|
|
|
|
Json PickNext(const std::vector<Json>& seats, const Json& config);
|
|
|
|
|
bool NeedsHaiku(const Json& seat);
|
|
|
|
|
std::string FableState(const Json& fable);
|
|
|
|
|
Json MergeFableProbe(Json base, const Json& fable);
|
|
|
|
|
|
|
|
|
|
// Where the CLI is: CLAUDE_BIN / REVIEW_GATES_CLAUDE_BIN, then PATH, then
|
|
|
|
|
// the editor extensions' bundled binary (newest first), then the
|
|
|
|
|
// standalone installs. The bare name when nothing is found.
|
|
|
|
|
std::string FindClaudeBin(const std::map<std::string, std::string>& env, const std::string& home);
|
|
|
|
|
std::string FindClaudeBin();
|
|
|
|
|
|
|
|
|
|
// The report's zone: $TZ, else /etc/localtime. A zone by name, as
|
|
|
|
|
// zoneinfo reads it (this libc++ has no tzdb, so the C library converts,
|
|
|
|
|
// with TZ set to the name); an empty name is the process's own zone.
|
|
|
|
|
struct Zone {
|
|
|
|
|
std::string name;
|
|
|
|
|
std::string label;
|
|
|
|
|
};
|
|
|
|
|
Zone LocalZone();
|
|
|
|
|
Zone ZoneNamed(std::string_view name);
|
feat(cli): seats, init, doc, browser install and stop
- `seats` reports every seat probed (Fable first, Haiku for a seat that
needs it), the API seats beside the driver's ledger, the drivers on the
board, or all of it as JSON. In the sandbox a last line names `stop`,
since the pids above cannot be signalled from there.
- `init` sets a repository up: the config from the template, the artifact
and worktree dirs ignored, the prepush skill with a marker that lets a
later init refresh it. The old link into the review-gates submodule is
replaced by a real directory, never written through. A foreign link, a
subdirectory, the home directory and a dir outside git are refused before
anything is written.
- `doc [--path] prepush|manifest|config` prints a shipped text, or where
the host sees it (the deployment's `active` link in the Flatpak).
- `browser install` runs the installer of the Playwright the pinned MCP
server depends on, and checks the Chromium revision landed.
- `stop <scratch>` and `stop --hash <hash8>` write the stop file a running
driver checks every 2 s, for live drivers only; the board is read, never
written.
The option parser takes positionals, in the usage line, the help and the
errors. The Commands test runs all of them through this binary, seats on a
claude stand-in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 15:45:36 +01:00
|
|
|
// "02:40": the clock in the zone.
|
|
|
|
|
std::string Clock(double epoch, const Zone& zone);
|
feat(seats): memory, the Playwright MCP config, and the seats library
:Memory: MemAvailable and the reserve policy, the sampler of a pass's peak
tree PSS (one sweep, a pause of three sweeps at least, stopped with the
driver), and the calibration file every driver on the machine shares,
re-read and written under its lock.
:Mcp: the pinned @playwright/mcp 0.0.83 arguments and the --mcp-config of
one pass, compact and in a fixed key order.
:Seats: the dotenv loader (token lines only, TOKEN7 inert), the one-word
probe (run under the supervisor, a missing binary reported with its errno
text), the window maths, the ranking, the Fable merge, the editor-bundle
lookup of the claude binary, the redaction of anything token-shaped, and
the report. This libc++ has no tzdb, so a zone by name converts through
the C library with TZ set, reading the system's zoneinfo.
tests/Seats: 92 checks against reference vectors, numbered in order, all
green.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 07:32:14 +01:00
|
|
|
// "02:40 (in 1h 50m)"; more than a day out with its date: "Wed 16 Sept,
|
|
|
|
|
// 11:00 (in 6d 10h)".
|
|
|
|
|
std::string FormatWhen(double epoch, double now, const Zone& zone);
|
|
|
|
|
std::string RenderTable(const std::vector<std::vector<std::string>>& rows);
|
|
|
|
|
std::string RenderStatusReport(const std::vector<Json>& seats, const Json& config, double now, const Zone& zone, const Json& board = nullptr,
|
|
|
|
|
const std::map<std::int32_t, std::string>& keys = {});
|
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 13:58:29 +01:00
|
|
|
// API-credit seats: the probe's verdict beside the driver's (its ledger
|
|
|
|
|
// against the budget), the cycle, the grant day and when the next
|
|
|
|
|
// credits are due - each seat's ledger, ledgerOut, grantDay, nextCredits
|
|
|
|
|
// and the probe's ledger event as the seats command set them.
|
|
|
|
|
std::string RenderApiReport(const std::vector<Json>& apiSeats, const Json& apiConfig, double now, const Json& board = nullptr,
|
feat(seats): memory, the Playwright MCP config, and the seats library
:Memory: MemAvailable and the reserve policy, the sampler of a pass's peak
tree PSS (one sweep, a pause of three sweeps at least, stopped with the
driver), and the calibration file every driver on the machine shares,
re-read and written under its lock.
:Mcp: the pinned @playwright/mcp 0.0.83 arguments and the --mcp-config of
one pass, compact and in a fixed key order.
:Seats: the dotenv loader (token lines only, TOKEN7 inert), the one-word
probe (run under the supervisor, a missing binary reported with its errno
text), the window maths, the ranking, the Fable merge, the editor-bundle
lookup of the claude binary, the redaction of anything token-shaped, and
the report. This libc++ has no tzdb, so a zone by name converts through
the C library with TZ set, reading the system's zoneinfo.
tests/Seats: 92 checks against reference vectors, numbered in order, all
green.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 07:32:14 +01:00
|
|
|
const std::map<std::int32_t, std::string>& keys = {});
|
|
|
|
|
std::string RenderDrivers(const Json& board, double now, const Zone& zone);
|
|
|
|
|
}
|