/** * The memory gate: heavy work is refused, not merely narrowed, while the * machine is short of memory (T13127). * * Budgets alone can only narrow: the governor's heavy classes floored at one * slot, so under any pressure one more test suite or build was always * admitted, however full swap was. This gate refuses heavy work (everything * the admission ledger admits, `admission-ledger.ts`) while memory pressure is * above {@link MEMORY_GATE_REFUSE_ABOVE}. Callers that wait (`cleo run * --wait`, evidence runs) report "waiting: memory pressure" with the readings * and start when it falls. * * ## One signal on every platform * * The gate reads the memory `some`/`full avg10` of a {@link ResourceSample}: * PSI on Linux, the PSI-equivalent the darwin backend derives from the * kernel pressure level, RAM squeeze and swap on macOS. CPU saturation is * never a reason to refuse: a busy CPU slows work down, an exhausted memory * takes the machine down. * * ## Hysteresis, machine-wide * * Once refusing, the gate admits again only at or below * {@link MEMORY_GATE_RESUME_AT_OR_BELOW}, so admission does not flap at the * threshold (the T12995 rule: distinct enter and exit thresholds). The * "refusing" state is a small latch file under the CLEO home shared by every * process, so a newcomer cannot slip in while waiters are held: everyone sees * one gate. A latch nobody has refreshed for {@link MEMORY_GATE_LATCH_TTL_MS} * expires. * * ## Failing open * * A sample without a memory signal (no PSI, a failed `sysctl`, a sampling * error) never refuses and never touches the latch. An unreadable or * unwritable latch degrades to point evaluation. A real, lasting pressure * blocks only until the caller's own timeout, which then reports the * readings. * * @module resources/pressure-gate * @task T13127 * @epic T13121 */ import type { MemoryPressureReading } from '@cleocode/contracts'; import type { ResourceSample } from './backend.js'; /** Heavy work is refused while memory `some avg10` is above this (the governor's floor threshold). */ export declare const MEMORY_GATE_REFUSE_ABOVE = 25; /** A refusing gate admits again once memory `some avg10` is at or below this. */ export declare const MEMORY_GATE_RESUME_AT_OR_BELOW = 15; /** Heavy work is also refused while memory `full avg10` is above this. */ export declare const MEMORY_GATE_FULL_REFUSE_ABOVE = 10; /** A refusing gate needs memory `full avg10` at or below this as well. */ export declare const MEMORY_GATE_FULL_RESUME_AT_OR_BELOW = 5; /** A refusing latch nobody refreshed for this long expires. */ export declare const MEMORY_GATE_LATCH_TTL_MS = 60000; /** Back-off hint on a memory-pressure deferral: pressure does not clear in seconds. */ export declare const MEMORY_GATE_RETRY_AFTER_MS = 10000; /** A waiter repeats its "waiting: memory pressure" notice at most this often. */ export declare const MEMORY_GATE_NOTICE_INTERVAL_MS = 60000; /** The gate's decision for one sample. */ export interface MemoryGateVerdict { /** `true` when heavy work must not start now. */ readonly refuse: boolean; /** The readings, or `null` when the sample carried no memory signal. */ readonly reading: MemoryPressureReading | null; } /** * The memory signals of a sample in one line: kernel level, squeeze, swap and * compressor on macOS; PSI and MemAvailable elsewhere. * * @param sample - a backend sample. * * @example * ```ts * describeMemoryPressure(darwinSample); * // → 'kernel level warning; 59% of RAM wired or compressed; swap 11.4 GiB used of 13.0 GiB (24% of RAM)' * ``` */ export declare function describeMemoryPressure(sample: ResourceSample): string; /** * The gate's decision for one sample, given whether it is already refusing. * Pure. A sample without a memory signal never refuses. * * @param sample - a backend sample. * @param latched - `true` when the gate is already refusing: the resume * thresholds apply instead of the refuse thresholds. * * @example * ```ts * evaluateMemoryGate(someAt(20), false).refuse; // false: not above 25 * evaluateMemoryGate(someAt(20), true).refuse; // true: not yet down to 15 * ``` */ export declare function evaluateMemoryGate(sample: ResourceSample, latched: boolean): MemoryGateVerdict; /** * Path of the machine-wide gate latch, beside the governor's slot locks. * * @param cleoHome - the CLEO home. @defaultValue {@link getCleoHome} */ export declare function memoryGatePath(cleoHome?: string): string; /** Options for {@link checkMemoryGate}. */ export interface CheckMemoryGateOptions { /** Clock. @defaultValue Date.now */ readonly now?: () => number; /** Latch file. @defaultValue {@link memoryGatePath} */ readonly path?: string; } /** * The gate's decision for one sample, with machine-wide hysteresis: reads * the shared latch, evaluates, and records a change (or refreshes a refusing * latch). Never throws. A sample without a memory signal neither refuses nor * touches the latch. * * @param sample - a backend sample. * @param opts - clock and latch path (tests). */ export declare function checkMemoryGate(sample: ResourceSample, opts?: CheckMemoryGateOptions): MemoryGateVerdict; /** Remove the latch (tests). @internal */ export declare function _resetMemoryGateForTest(path?: string): void; /** Reports a wait on the gate. */ export interface MemoryGateReporter { /** * The gate refused: report it at once, then at most every * {@link MEMORY_GATE_NOTICE_INTERVAL_MS} with fresh readings. */ waiting(reading: MemoryPressureReading, waitedMs: number, remainingMs?: number): void; /** Admitted: say so when a wait was reported. `score` is the latest reading, when known. */ admitted(waitedMs: number, score: number | null): void; } /** * One "waiting: memory pressure" line. * * @param reading - the gate's readings. * @param subject - what is waiting, e.g. `test run`. * @param waitedMs - how long it has waited so far. * @param remainingMs - how long until the wait gives up, when it has a deadline (T13965). */ export declare function memoryGateWaitingLine(reading: MemoryPressureReading, subject: string, waitedMs: number, remainingMs?: number): string; /** * A {@link MemoryGateReporter} that emits one-line notices. * * @param emit - where a line goes (stderr in the CLI, a `cleo run` notice). * @param subject - what is waiting, e.g. `test run`. * @param opts - clock and repeat interval (tests). */ export declare function memoryGateReporter(emit: (line: string) => void, subject: string, opts?: { readonly now?: () => number; readonly intervalMs?: number; }): MemoryGateReporter; //# sourceMappingURL=pressure-gate.d.ts.map