/**
 * Upper bound on entries the namespace may hold that **nobody owns**.
 *
 * A run allocates one root per process — the main process plus one per worker —
 * and several runs share a workstation, so a healthy namespace legitimately
 * holds entries while work is in flight. This ceiling is not a tuning knob for
 * that; it is the point past which the namespace is provably becoming the very
 * thing it replaced.
 *
 * ## Why it counts unowned entries rather than all of them
 *
 * It compared against the total, and a total is a sum over every concurrent run
 * on the box. Measured (CodySwannGT/lisa#3032), six snapshots of the shared
 * namespace between 519 and 3,730 entries: in five of them EVERY root had a
 * live owner that started before it. 24.3% of sampled instants sat above this
 * ceiling, in stretches up to 127 seconds, and a run starting inside one was
 * refused for its siblings' work under a message reading "accumulating rather
 * than being reclaimed" — about a namespace in which nothing was accumulating.
 * Ten full-suite runs at one commit produced 2 PASS and 8 REFUSED that way.
 *
 * Live-owned entries cannot accumulate: they are released when their owner
 * exits, measured as 3,729 becoming reclaimable within 22 seconds when a run's
 * workers ended together. Orphaned and unrecognised entries are the only ones
 * that persist without bound, and they are exactly what the corrected
 * arithmetic still counts — so no leak detection is lost.
 *
 * The number itself is unchanged and is deliberately not re-tuned here. It is a
 * leak detector, not a performance backstop: the directory cost this campaign
 * began with was measured at hundreds of thousands of entries, three orders of
 * magnitude above this line, so a second absolute cap would need calibrating
 * against measured harm and belongs in its own ticket.
 */
export declare const MAX_NAMESPACE_ENTRIES = 512;
/** What an inspection of the namespace found. */
export interface NamespaceResidue {
    /** Entries whose owning process is gone but which were not removed */
    readonly orphaned: readonly string[];
    /** Entries that do not follow the run-root naming scheme at all */
    readonly unrecognised: readonly string[];
    /** Total entries present */
    readonly total: number;
}
/**
 * Classifies what is left in the namespace.
 *
 * `unrecognised` is the interesting bucket. The sweep identifies residue by
 * parsing the owning pid out of a directory's name, so a change to that naming
 * scheme does not break the sweep loudly — it makes the sweep silently match
 * nothing while continuing to report success. Naming that case separately turns
 * that regression into a failure instead of a slow leak.
 * @param dir - Namespace directory to inspect
 * @param alive - Liveness probe, overridable for tests
 * @returns The classified residue.
 */
export declare const inspectNamespace: (dir?: string, alive?: (pid: number) => boolean) => NamespaceResidue;
/**
 * Builds the failure message for a namespace that is accumulating.
 *
 * Every branch names the directory and the offending entries, because the
 * failure this replaces was a 60-second timeout that named nothing.
 *
 * `unrecognised` is deliberately NOT a failure. It was, and it wedged: one
 * foreign directory in the namespace failed every subsequent run, forever, with
 * a message a downstream user could not act on. Those entries are reclaimed on
 * age by the sweep instead. What remains here are the two conditions that mean
 * this fix itself has stopped working — residue the sweep should have taken and
 * did not, and a namespace growing without bound.
 * @param dir - Namespace directory inspected
 * @param residue - What the inspection found
 * @returns The message, or `undefined` when the namespace is healthy.
 */
export declare const describeResidueFailure: (dir: string, residue: NamespaceResidue) => string | undefined;
/**
 * Renders the banner printed at the moment of refusal.
 *
 * A throw from `setup` exits 1, which is the half of the contract that works.
 * The other half did not: Vitest reports the throw as an unhandled error AFTER
 * the run summary, so what an operator actually saw, in order, was
 *
 * ```
 * line   5:  No test files found, exiting with code 1
 * lines 11-390: a full coverage report, every file at 0%
 * line 397:  Error: Test scratch namespace ... past the ceiling of 512
 * ```
 *
 * measured on vitest 4.1.9 over a 416-line output. The guard bit correctly and
 * was still PRESENTED as a coverage failure — "no tests" and an all-zero
 * coverage table are the first and largest things on the page, and the reason
 * is 392 lines below the verdict. Anything reading the top of the output, or
 * reading the coverage numbers, gets the wrong story; that is a silent
 * degradation inside a guard written to abolish silent degradation.
 *
 * So the reason is printed HERE, before the throw, from a hook that runs before
 * collection — which puts it above every line of that output instead of below
 * it. The throw is kept exactly as it was: this changes where the reason is
 * read, not whether the run fails.
 *
 * The banner says what the zeroes below it mean, because the operator standing
 * at this gate is not necessarily an engineer and "0% coverage" reads as a
 * verdict on the code rather than as the absence of a measurement.
 * @param failure - The residue failure being reported
 * @returns Operator-readable banner text.
 */
export declare const renderRefusalNotice: (failure: string) => string;
/**
 * Renders the ONE line a refused run ends on.
 *
 * The banner {@link renderRefusalNotice} produces is read by an operator who
 * starts at the top. Nobody does that with a long transcript, and the tail was
 * measured to be a stack trace through vitest's `_initializeGlobalSetup` — no
 * line anywhere said what the run had concluded. Every other run puts its
 * verdict at the end, so the end is where a reader looks, and a refused run
 * gave them a frame in somebody else's internals instead.
 *
 * That absence has a name in this campaign: NO-RESULT, one of the three
 * distinct outcomes fourteen runs of one unchanged commit produced
 * (CodySwannGT/lisa#3032). "Every run emits a summary line" is the clause it
 * violates, and it violates it by emitting nothing at all rather than by
 * emitting the wrong thing.
 *
 * One line, deliberately. A second banner at the foot of the page would be a
 * wall a reader skips exactly as they skipped the first one.
 * @param failure - The residue failure being reported
 * @returns A single newline-terminated summary line.
 */
export declare const renderRefusalSummary: (failure: string) => string;
/**
 * Vitest's marker for a pool worker. Absent in the process that runs
 * `globalSetup`, present in every process that runs a test file — measured on
 * vitest 4.1.9 rather than assumed, and pinned by a test so a rename cannot
 * silently disarm {@link announceRefusal}.
 */
export declare const POOL_WORKER_ENV = "VITEST_POOL_ID";
/**
 * Arms the summary line to be written when the process finally exits.
 *
 * At exit rather than inline, because inline is where the banner already is and
 * the whole point is to reach the reader who starts at the other end. Vitest
 * prints its unhandled-error report after `setup` throws, so anything written
 * before the throw lands above it; an exit hook lands below.
 *
 * `writeSync` on the descriptor, never `process.stderr.write`. On a POSIX pipe
 * Node's stderr is asynchronous, and an asynchronous write issued from an exit
 * handler can be discarded when the process tears down — which would leave this
 * fix passing its own tests while changing no transcript anywhere. The writer
 * is a parameter so a test can observe the line without redefining an ESM
 * module namespace, which is not permitted.
 *
 * Unguarded on purpose: {@link announceRefusal} is the only caller and holds
 * the single guard. Two copies of one condition is one condition that can go
 * inert without any test noticing.
 * @param failure - The residue failure being reported
 * @param write - Where the line goes; defaults to a synchronous stderr write
 */
export declare const armRefusalSummary: (failure: string, write?: (text: string) => void) => void;
/**
 * Says a run was refused — at the top of the transcript and again at the foot —
 * and says nothing at all from a process that cannot refuse a run.
 *
 * ## Why a worker announces nothing
 *
 * This project has tests that invoke the real {@link setup} against a
 * deliberately overfull namespace, and both halves of an announcement outlive
 * the call that made it: the banner is already on the run's stderr, and the
 * summary is a handler that fires whenever that process exits. Announced
 * unconditionally, each such test contributed to a transcript that was not its
 * own.
 *
 * Measured on ten consecutive runs of 64 files and 893 tests, every one green
 * and exiting 0: **two** "TEST RUN REFUSED TO START" banners in each, naming
 * the tests' own fixture directories, and — until this guard — two matching
 * "❌ NO VERDICT" lines. A green run that says it was refused is the same class
 * of lie as a killed gate that says FAILED, in the same transcript, and it is
 * the one the "repeated runs agree" scenario trips over: two readers of the
 * same passing run can reasonably disagree about what it concluded.
 *
 * The banner half predates the summary half and shipped with #3027; the summary
 * half was introduced by #3032's own first attempt at this fix. Both are cured
 * here by one condition rather than two.
 *
 * The guard is structural rather than a rule each test must remember: only the
 * process vitest runs `globalSetup` in can refuse a run, and that process is
 * the one WITHOUT {@link POOL_WORKER_ENV}. A test calling `setup` runs in a
 * worker and therefore announces nothing, whatever it does to the namespace.
 * @param failure - The residue failure being reported
 * @param writeNotice - Where the banner goes; defaults to the run's stderr
 */
export declare const announceRefusal: (failure: string, writeNotice?: (text: string) => void) => void;
/**
 * Reclaims residue from previous runs, then refuses to start into a namespace
 * that is accumulating.
 *
 * A refusal speaks twice — a banner before collection and a summary line at
 * exit — and the two are not redundant. They are the top and the bottom of a
 * transcript nobody reads in full.
 * @throws When residue is present that the sweep cannot or did not reclaim.
 */
export declare const setup: () => void;
/**
 * Reclaims what this run allocated.
 *
 * This hook cannot fail a run — see the module header: Vitest swallows a throw
 * from here. So it does the one thing it can do reliably, which is the cleanup;
 * the judgement is made by {@link setup} on the next run, where a throw is
 * actually honoured.
 *
 * The namespace directory itself is deliberately left in place. Creating a name
 * inside a saturated shared temp root is the expensive operation this whole
 * module exists to stop paying repeatedly; keeping one directory forever means
 * the cost is paid once on a machine rather than once per run, and it is what
 * makes "one entry in the shared temp root, ever" literally true rather than
 * approximately true.
 */
export declare const teardown: () => void;
//# sourceMappingURL=scratch-global-setup.d.ts.map