/** * `GET /diagnostics` → `governance`: is the engine's governance evidence actually LANDING? * (crew#495, acceptance finding F-022.) * * The engine's emit seam either writes an event onto the shared store (`WICKED_ESTATE_DB`) or * spools it to the dead-letter outbox (`WICKED_APPS_EMIT_DEADLETTER`) with a loud stderr line * nobody reads. Until this module existed, neither outcome reached `/diagnostics`, so a daemon * that had dead-lettered every governance event for a week reported `recentErrors: []` while the * home board said "Governed 100%". This is the daemon's read-only answer, assembled from records * it already owns, under the surface's honesty rule: a number it cannot derive is `null`, never 0. * * - `store` — the resolved store and which rule chose it (`core/governance-store.ts`); * `null` when this process never resolved one (a library boot) — then every emit * dead-letters, and a `governance.store` finding says so. * - `records` — EVENT nodes on the store now, and how many landed since this daemon booted. * Counted through the engine binding (`Core.eventStoreCount`, wicked-core-ts ≥ the * release carrying crew#495's companion); on an older addon both are `null`. * - `deadletters` — a streamed fold of the outbox: count, per-type and per-reason buckets, the * timestamp range where entries carry the engine's `ts` (older engines wrote none — * those are counted as `untimestamped`, never given an invented time), plus the * legacy HOME outbox a pre-fix engine wrote, if one exists. * - `findings` — `governance.deadletter` (error) whenever the outbox holds an entry; * `governance.store` (error) when no store is resolved; `governance.legacy-outbox` * when the pre-fix HOME outbox exists — a WARNING naming the replay command when it * is this daemon's own prior outbox (default state home under that HOME), an INFO * with only the read-only `--dry-run` inspect recipe when the state home is isolated * and the file cannot be attributed to this daemon (F-2R2-006: a fresh rig must not * be told to import another daemon's dead letters). */ import { type GovernanceStoreLocation, type GovernanceStoreSource, type LegacyOutboxScope } from '../core/governance-store.js'; export interface GovernanceStoreInfo { path: string; source: GovernanceStoreSource; } export interface GovernanceRecords { total: number | null; /** Records landed since this daemon's API came up: the baseline is taken when the routes * register, AFTER the engine has booted, so the engine's own boot-time emits are in the baseline. */ sinceBoot: number | null; } export interface GovernanceDeadletters { path: string | null; count: number; byType: Record; byReason: Record; timestamped: number; untimestamped: number; oldestTs: number | null; newestTs: number | null; truncated: boolean; /** The pre-fix HOME outbox, when one exists — with WHOSE it is (`scope`, F-2R2-006): `own` when * this daemon runs in that HOME's default state home (its earlier versions spooled it), `host` * when this daemon's state home is isolated and the file is other daemons' dead letters. */ legacyOutbox: LegacyOutbox | null; } export interface LegacyOutbox { path: string; bytes: number; scope: LegacyOutboxScope; } export type GovernanceFindingKind = 'governance.store' | 'governance.deadletter' | 'governance.legacy-outbox'; export interface GovernanceFinding { kind: GovernanceFindingKind; /** `info` is the one non-actionable rung: a HOME outbox that is NOT this daemon's (F-2R2-006). */ severity: 'info' | 'warning' | 'error'; message: string; } export interface GovernanceHealth { store: GovernanceStoreInfo | null; records: GovernanceRecords; deadletters: GovernanceDeadletters; findings: GovernanceFinding[]; } /** Bounds on the fold's work: it runs inside a request handler, and a runaway outbox (227 MB of * test junk was once observed under HOME) must not turn a diagnostics GET into a file crawl. Past * the byte cap the fold stops and says so (`truncated`) — the count is then a floor, honestly * labelled, never a guess at the rest. */ export declare const DEADLETTER_FOLD_MAX_BYTES: number; /** One folded outbox — the `deadletters` block minus the legacy HOME pointer the caller adds. */ export type DeadletterFold = Omit; export declare function emptyDeadletterFold(path: string | null): DeadletterFold; /** The bucket a reason folds into: its text before the first `:` — the engine's three shapes are * `no shared store (…)`, `open shared store failed: ` and `store write failed: * `, and only the prefix is stable across entries. */ export declare function reasonBucket(reason: string): string; /** A usable `ts`: a finite, non-negative epoch-millisecond number inside `Date`'s range. Anything * else on a spool line (a torn `1e20`, a negative, a string) is counted as untimestamped — never * a `RangeError` that turns `/diagnostics` into a 500. */ export declare function isEpochMs(value: unknown): value is number; /** * Stream-fold one outbox file. A missing file is an EMPTY fold (no dead letters — the good case), * an unreadable one folds what it could; a malformed line still COUNTS (it is an entry the engine * appended; a torn tail from a crashed daemon is exactly what a replay must not lose sight of) but * contributes no type or timestamp. */ export declare function foldDeadletters(path: string): Promise; /** * The fold, re-run only when the outbox CHANGES (size or mtime) — one `stat` per request, never a * re-read for a dashboard that polls, and never a stale answer once the engine appends. */ export declare class DeadletterFoldCache { private value; private inFlight; get(path: string): Promise; } /** A reader over the store's EVENT nodes: `null` = the installed engine cannot count (no * `eventStoreCount` binding). A missing store file counts as 0 — nothing has landed yet. */ export type EventCounter = ((dbPath: string) => Promise) | null; /** * Baseline-at-boot plus a short-TTL live count. `sinceBoot` is `total − baseline`; either side * unknown (no binding, or a read that failed) folds to `null` rather than a fabricated delta. */ export declare class GovernanceRecordCounter { private readonly dbPath; private readonly counter; private readonly ttlMs; private baseline; private live; private inFlight; private readonly baselinePromise; constructor(dbPath: string | null, counter: EventCounter, ttlMs?: number); /** Test seam: the boot baseline has been taken. */ ready(): Promise; private read; private total; records(): Promise; } /** Quote one argument for the operator's shell: bare when it needs no quoting, otherwise * single-quoted on POSIX and double-quoted on Windows (JSON quoting is not shell quoting there). */ export declare function shellQuote(arg: string): string; /** * The replay recipe every finding points at — one spelling, so the console and the log agree — and * TARGET-SPECIFIC: a bare recipe followed on a daemon booted with a custom `--db` or * `--governance-db` would replay into a different store than the one that dead-lettered. The target * is ALWAYS an explicit, DURABLE `--governance-db`: the flag outranks the env rungs of * `replayTarget()`'s ladder (a `WICKED_CREW_GOVERNANCE_DB` / `WICKED_ESTATE_DB` exported in the * operator's shell would otherwise win over `--db`), and a `:memory:` daemon's dead letters are * pointed at its durable sidecar store rather than at a target a real replay refuses. `null` target * = no daemon store known: the bare recipe, for a `--dry-run` inspection. */ export declare function replayCommand(outboxPath: string, target: GovernanceStoreLocation | null): string; export interface GovernanceHealthInputs { location: GovernanceStoreLocation | null; records: GovernanceRecords; fold: DeadletterFold; /** The pre-fix HOME outbox when it exists and is non-empty — path, size and whose it is — else `null`. */ legacyOutbox: LegacyOutbox | null; } export declare function governanceHealth(input: GovernanceHealthInputs): GovernanceHealth; /** * `{ path, bytes, scope }` when the legacy HOME outbox exists and is non-empty (a `stat`, never a * read), else `null`. `scope` is {@link legacyOutboxScope} against `coreDbPath` — the daemon's core * db, `null` when no store resolved (then `host`: nothing to attribute the file to). */ export declare function probeLegacyOutbox(path: string | null, coreDbPath?: string | null): Promise; /** * The per-daemon diagnostics assembly the route calls: one fold cache, one record counter, one * legacy probe. `legacyOutboxPath` is injectable so a test never has to `stat` the developer's real * HOME to reason about the block. */ export declare class GovernanceDiagnostics { private readonly location; private readonly legacyOutboxPath; private readonly folds; private readonly counter; constructor(location: GovernanceStoreLocation | null, eventCounter: EventCounter, legacyOutboxPath: string | null); /** Test seam: the boot baseline has been taken. */ ready(): Promise; health(): Promise; } //# sourceMappingURL=governance-health.d.ts.map