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>
This commit is contained in:
parent
e8a58f893f
commit
8ffbd6cf5d
9 changed files with 1764 additions and 3 deletions
88
interfaces/Crafter.Review-Memory.cppm
Normal file
88
interfaces/Crafter.Review-Memory.cppm
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
export module Crafter.Review:Memory;
|
||||
import std;
|
||||
import :Json;
|
||||
|
||||
// Memory (2026-10-22, Jorijn: "make the concurrency limit from free memory,
|
||||
// instead of just guessing"): what the machine has free, what a pass costs,
|
||||
// and the budget the pool admits passes against.
|
||||
//
|
||||
// - Free: the kernel's MemAvailable, read at the start of every gate and
|
||||
// before every launch: the whole machine's, which is right for a budget and
|
||||
// wrong for a cost.
|
||||
// - Cost: the peak PSS of a pass's own process tree (claude, and for the
|
||||
// browser environments the Playwright server and Chromium under it), sampled
|
||||
// by the driver on its own passes and kept per repository and environment
|
||||
// (Calibration); a pass is admitted at the highest of its environment's
|
||||
// recent peaks, or at the measured default before any is recorded.
|
||||
// - Reserve: what is left for the desktop and the page cache —
|
||||
// REVIEW_GATES_MEM_RESERVE_MB, by default 10 % of MemTotal, at least 2 GB.
|
||||
// A policy, not a measurement.
|
||||
export namespace Crafter::Review::Memory {
|
||||
// Recent peaks kept per repository and environment.
|
||||
constexpr std::size_t Keep = 30;
|
||||
|
||||
// MemAvailable in MB; nullopt without /proc/meminfo.
|
||||
std::optional<std::int64_t> Available(const std::filesystem::path& path = "/proc/meminfo");
|
||||
// REVIEW_GATES_MEM_RESERVE_MB, else 10 % of MemTotal, at least 2048.
|
||||
// std::invalid_argument for a value that is no number.
|
||||
std::int64_t Reserve(const std::optional<std::string>& configured, const std::filesystem::path& path = "/proc/meminfo");
|
||||
std::int64_t Reserve();
|
||||
|
||||
// The peak tree PSS of every watched pass, on one thread. A sweep makes one
|
||||
// ppid map, then reads smaps_rollup for every process of every watched tree
|
||||
// — the costly read, it walks the page tables. The pause after a sweep is
|
||||
// `interval` or three times the sweep's own length, whichever is longer, so
|
||||
// sampling holds at most a quarter of one core whatever the number of
|
||||
// passes (2026-10-22). A pass shorter than one pause may go unmeasured.
|
||||
class Sampler {
|
||||
public:
|
||||
using Measure = std::function<double(std::int64_t pid, const std::map<std::int64_t, std::vector<std::int64_t>>& kids)>;
|
||||
using Scan = std::function<std::map<std::int64_t, std::vector<std::int64_t>>()>;
|
||||
explicit Sampler(double interval = 3.0, Measure measure = {}, Scan scan = {});
|
||||
~Sampler();
|
||||
Sampler(const Sampler&) = delete;
|
||||
Sampler& operator=(const Sampler&) = delete;
|
||||
void Watch(const std::string& key, std::int64_t pid);
|
||||
// One sweep over every watched pass; its length in seconds.
|
||||
double Sample();
|
||||
// Stop watching; the peak in MB, rounded to 0.1, or nullopt when
|
||||
// nothing was measured.
|
||||
std::optional<double> Done(const std::string& key);
|
||||
static double Pause(double interval, double sweep);
|
||||
|
||||
private:
|
||||
struct State;
|
||||
std::shared_ptr<State> state;
|
||||
};
|
||||
|
||||
// Recent peaks per repository and environment, in one JSON file next to the
|
||||
// seat ledger: {repo: {env: [MB]}}. A record, never a dependency
|
||||
// (2026-10-22): a damaged file reads as empty, a part of the wrong shape as
|
||||
// absent, and a peak that cannot be written is dropped.
|
||||
class Calibration {
|
||||
public:
|
||||
Calibration(std::filesystem::path path, const std::filesystem::path& repo, std::map<std::string, std::int64_t> defaults);
|
||||
std::vector<Json> Peaks(std::string_view environment) const;
|
||||
struct Cost {
|
||||
// MB, an int or a float as the file holds it.
|
||||
Json megabytes;
|
||||
std::string source;
|
||||
};
|
||||
Cost CostOf(std::string_view environment) const;
|
||||
// Keep a pass's peak; whether it was stored. The file is every
|
||||
// driver's on this machine: the re-read and the write are one section
|
||||
// under its lock (2026-10-31), so a peak another driver wrote between
|
||||
// them is kept.
|
||||
bool Record(std::string_view environment, double megabytes);
|
||||
|
||||
private:
|
||||
std::filesystem::path path;
|
||||
std::string repo;
|
||||
std::map<std::string, std::int64_t> defaults;
|
||||
mutable std::mutex lock;
|
||||
Json Load() const;
|
||||
};
|
||||
}
|
||||
Loading…
Reference in a new issue