/** * The canonical top-level layout of `.peaks/_runtime/`. * * WHY THIS MODULE EXISTS * * `.peaks/_runtime/` holds two populations whose names look identical: * * 1. SESSION DIRS — `-session-`, one per peaks session. * Anything else that *looks* like a session id but is not one is a * defect (a test that passed `--session-id x`, a typo, an old * `unknown-sid` bucket). * 2. SYSTEM ENTRIES — dirs and files the code itself writes there on * purpose: `callers/`, `change/`, `benchmarks/`, `session.json`, … * * The doctor check `L3:l3-orphan-sessions` must flag (1) and tolerate (2), * and the only thing that can tell them apart is a list of the system * entries. That list used to live inside the check as a hand-maintained * `new Set(['change'])` — and it had already drifted: `callers/` is a * designed location (`caller-binding-service.ts` stores * `.peaks/_runtime/callers/.json`), was never added, and made * `peaks doctor` exit non-zero on a clean workspace *permanently* * (`4 orphan session(s) …: callers, cli, unknown-sid, x`). The check's own * doc comment asked future maintainers to keep a *second* list — the prose * `RUNTIME_SYSTEM_SUBDIRS_DOC` — in sync by hand. Prose cannot enforce * itself, so the two lists diverged exactly as you would expect. * * WHY DERIVATION WAS REJECTED AND A REGISTRY IS THE SOURCE OF TRUTH * * A derivation would have to classify a bare name as "system" or "bogus" * without a list. There is no signal to derive from: `callers` (system) and * `x` (bogus) are the same shape — a lowercase word. The session-id regex * separates them from `2026-09-16-session-5bcf09`, not from each other. So * the alternatives were (a) keep a literal in the check, or (b) put the * literal *and its consumers* in one place. This module is (b): the check * imports the set instead of re-declaring it, and * `tests/unit/workspace/runtime-layout-drift-guard.test.ts` parses `src/**` * and fails when the code writes a `_runtime` child that is not registered * here. The registry can no longer drift silently — it can only drift loudly, * as a red test. * * The guard parses ASTs rather than grepping, because this repository * documents the layout in prose constantly: `migrate-1-4-1-service.ts` has an * array whose two adjacent string literals are `'_runtime', '_sub_agents'` * (they are two *separate* SKIP entries), and `evidence-generator.ts` * describes a past escape as `` `.peaks/_runtime/pwned.md` `` inside a * comment. Both are false positives for a text scan and invisible to an AST * walk. See `tests/unit/runtime/no-runtime-input-guard.test.ts` for the same * decision made the same way. */ /** * `dir` entries are scanned by `L3:l3-orphan-sessions` and must not be * flagged. `file` entries are never flagged by that check — it only reads * directories — and are registered so the drift guard can classify them and * so this module stays an honest census of the tree rather than half of one. */ export type RuntimeEntryKind = 'dir' | 'file'; export interface RuntimeSystemEntry { /** The exact top-level name under `.peaks/_runtime/`. */ readonly name: string; readonly kind: RuntimeEntryKind; /** What writes it and why it is legitimate. */ readonly purpose: string; } /** * Reserved top-level dir for session-less command output. * * `peaks baseline audit` runs with no bound session (it is the only scorer * that can run inside the secretless OIDC publish gate), so it has no * `` to write under. It used to pass the literal `'cli'` as its * sessionId, which made `.peaks/_runtime/cli/capability-audit/*.json` look * exactly like a session dir that failed validation — `peaks doctor` has * been red on this repository ever since. The name is underscore-prefixed to * match the tree's existing convention for "machinery, not a session" * (`_runtime`, `_sub_agents`) and to make it impossible to mistake for a * session id. */ export declare const RUNTIME_SESSIONLESS_SCOPE = "_audit"; /** * Every legitimate top-level entry under `.peaks/_runtime/`. * * This is the list `L3:l3-orphan-sessions` tolerates. Adding an entry that * nothing writes is a red test (the drift guard's reverse direction); writing * an entry that is not here is also a red test. Both directions are pinned in * `tests/unit/workspace/runtime-layout-drift-guard.test.ts`. */ export declare const RUNTIME_SYSTEM_ENTRIES: readonly RuntimeSystemEntry[]; /** * The set `L3:l3-orphan-sessions` filters with. Derived from the registry * above so the check and the census cannot disagree about the same name. */ export declare const RUNTIME_SYSTEM_SUBDIRS: ReadonlySet; /** * Every registered top-level name — dirs AND files. * * Derived from the registry above so the census and the registry cannot * disagree about what "registered" means. */ export declare const RUNTIME_SYSTEM_ENTRY_NAMES: ReadonlySet; /** One top-level entry under the runtime root, as the census sees it. */ export interface RuntimeRootEntry { /** The exact top-level name. */ readonly name: string; /** `dir` for a directory, `file` for everything else. */ readonly kind: RuntimeEntryKind; } /** One top-level entry that is neither a session dir nor a registered system entry. */ export interface RuntimeRootViolation { readonly name: string; readonly kind: RuntimeEntryKind; } /** * What a read of the runtime root produced. * * `unreadable` is a first-class outcome, not an exception: a sweep that cannot * look must not report "clean" — the one answer it is not entitled to give * (see `services/audit/enforcers/root-scratch-artifacts.ts`). */ export type RuntimeRootRead = { readonly status: 'read'; readonly entries: readonly RuntimeRootEntry[]; } | { readonly status: 'unreadable'; readonly reason: string; }; /** * The census verdict. `clean` is reachable ONLY for a tree that was read. */ export type RuntimeRootCensus = { readonly status: 'clean'; readonly entryCount: number; } | { readonly status: 'violations'; readonly violations: readonly RuntimeRootViolation[]; } | { readonly status: 'undetermined'; readonly reason: string; }; /** * The census: every top-level entry of the runtime root that is neither a * session dir nor a registered system entry. * * Three classes, in this order: * * 1. a DIRECTORY whose name is a valid session id — legal. A FILE wearing a * session-shaped name is NOT legal: only a directory can be a session * home, so such a file is a stray. * 2. a name in `RUNTIME_SYSTEM_ENTRIES` — legal, whatever its kind. The * registry is the authority on which names may sit at the root. * 3. everything else — a violation, reported with its name AND its kind. */ export declare function censusRuntimeRoot(input: { readonly read: RuntimeRootRead; /** Defaults to the canonical validator; the doctor injects its own. */ readonly isValidSessionId?: (sid: string) => boolean; }): RuntimeRootCensus;