/** * The state-home PREFLIGHT (wicked-core#411 / crew#497; acceptance findings F-RC1-011, F-RC2-020, * F-032/F-033): say at BOOT — and keep saying, on every `GET /diagnostics`, `GET /health` and * `POST /runs` — that the daemon's state home holds an entry the worker Read fence cannot classify, * instead of letting every governed run discover it at its first worker. * * # What happened * * Core's fence over the state home is an EXPLICIT registry (`tests/fixtures/state-home-subtrees.json`, * design v3.1 §1): an entry it does not classify refuses the launch by name, fail closed. Right — * but it fired at the run's FIRST WORKER, after a planning council and the intake gate, as a failed * unit the failure-triage judge labelled "triage judge errored" and escalated with an "Approve to * retry" that could only fail again. The daemon booted GREEN: `/health` ok, `/diagnostics` clean, * `recentErrors: []`. Twice in one day that cost every governed run on a host — the acceptance rig's * `WICKED_WORKFLOWS_DIR=/workflows` (crew seeds the interactive-* drop-in defs there at * boot), and a `skills.fixture-debris-…` directory left in the operator's live state home. * * # What this module does * * 1. {@link assertWickedRootsOutsideStateHome} — the BOOT refusal: a `WICKED_*` root variable * pointed INSIDE the state home is a configuration error `serve` refuses with (the rig's * shape). The defaults all live outside; the variables here are the ones whose target crew * writes into (or spawns a bridge that does). Two classes (crew#569): a FENCED variable's * target is also a registered fixture entry, so a pre-existing placement is denied rather * than refusing every launch; a REFUSE-ONLY variable places a file crew itself never seeds * (`WICKED_CREW_SYSTEM_SETTINGS`) — an existing placement is the configuration error the * boot names, there is nothing to fence, so it has no registry row. Paths are compared by * their REAL spelling (the deepest existing ancestor realpath'd — W1 of crew#555): core's * fence canonicalises, so a symlinked state home must not pass here and refuse at intake. * 2. {@link StateHomeWatch} — the SURVEY: which state home the fence classifies and every entry it * cannot classify there. Asked of the engine (`Core.preflightStateHome`, the same code the * fence and core's intake refusal run) when the installed addon carries it; on an older addon * crew classifies with its own copy of the registry's names (`state-home-registry.ts`, held * equal to the fixture by test). Re-run on demand — a `readdir` of two directories — so an * entry that appears AFTER boot (the live daemon's debris did) is reported without a restart. * 3. {@link stateHomeBlockerBody} — the `POST /runs` 409: the typed error body, naming every entry * and the remedy, until the entries are gone. * * The daemon does NOT refuse to serve over an unregistered entry (studio must still load and show * the blocker); it refuses to LAUNCH. Every message is in operator terms — the entry, the state * home, the variable, what to do — and none cites a repository fixture path (F-033). */ /** The remedy every message ends with — spelled once, operator terms, no repository paths. */ export declare const STATE_HOME_REMEDY: string; /** The finding kind `GET /diagnostics.stateHome.findings[]` and `GET /health.warnings[]` carry. */ export declare const STATE_HOME_FINDING_KIND = "state-home.unregistered"; /** The machine-readable code on the `POST /runs` 409 body. */ export declare const STATE_HOME_BLOCKER_CODE = "state_home_unregistered"; /** * The `WICKED_*` root variables whose target crew WRITES INTO, with what lands there — the ones an * operator can point inside the state home by accident (the rig did). Each default lives outside * the state home; the fixture registers the top-level name each would create so a pre-existing * placement is fenced, and this module refuses to boot into one. * * `WICKED_INTERACTIVE_ROOT` is deliberately NOT listed since crew 0.7.35 (D-L7-1 / BC-49): the * interactive default now lives INSIDE the state home (`interactive/`, a registered join entry the * fence classifies), so the variable may name any path — inside the state home or out. */ export declare const STATE_HOME_ROOT_ENVS: ReadonlyArray<{ variable: string; creates: string; fenced: boolean; }>; /** * A `WICKED_*` root variable pointed inside the state home (crew#497). A CONFIGURATION error * `serve` refuses with — the same posture as an unshareable bus db or governance store. */ export declare class StateHomePlacementError extends Error { readonly variable: string; readonly value: string; readonly stateHome: string; /** The top-level entry the variable's target would create under the state home. */ readonly entry: string; constructor(variable: string, value: string, stateHome: string, entry: string, creates: string); } /** * The REAL spelling of `p` (W1 residual of crew#555): `resolve` it, realpath the deepest EXISTING * ancestor and re-join the rest. `realpathSync` alone throws for a path that does not exist yet — * and crew creates `WICKED_WORKFLOWS_DIR` AFTER this preflight (`registerWorkflow` seeds it at * boot) — so a naive realpath-else-resolve compares spelled paths again and a symlinked state home * boots green, then core's fence (which canonicalises) refuses at intake. Crew's rule ⊇ core's: * the one divergence — a symlinked ancestor of a not-yet-existing target — crew refuses at boot * where core would refuse at intake once the dir exists; same outcome, earlier. No existing * ancestor at all (a bare relative spelling nobody created) ⇒ the resolved spelling. */ export declare function canonicalize(p: string): string; /** * Refuse a `WICKED_*` root pointed inside `stateHome` (the rig's F-RC1-011 shape). Pure over `env` * so the boot and the tests spell the rule once; throws {@link StateHomePlacementError} naming the * variable, its value, the entry it would create and the remedy. An unset or empty variable is * fine (the default lives outside the state home). A FILE value's entry is its first segment * below the state home (the file itself when placed at the top level — crew#569). */ export declare function assertWickedRootsOutsideStateHome(env: NodeJS.ProcessEnv, stateHome: string): void; /** One entry the registry cannot classify (mirrors core's `UnregisteredEntry`). */ export interface UnregisteredStateHomeEntry { name: string; path: string; /** `state-home` — a top-level entry; `skills-root` — a child of `/skills` that is * neither the read slot nor a registered denied child. */ level: 'state-home' | 'skills-root'; } export interface StateHomeFinding { kind: typeof STATE_HOME_FINDING_KIND; severity: 'error'; message: string; } /** * What `GET /diagnostics` reports as `stateHome` (additive; the next `wicked-crew-api-types` cut * declares it). Honest throughout: a field the daemon cannot answer is `null`, never invented. */ export interface StateHomeHealth { /** The state home surveyed (the fence's, derived from the handed snapshot; else the db parent). */ stateHome: string | null; /** `snapshot` — derived from the handed `WICKED_SKILLS_SNAPSHOT` (the directory the fence * classifies); `db` — the core db's parent, surveyed for information when no snapshot is handed. */ derivedFrom: 'snapshot' | 'db' | null; /** Who classified: the engine (`Core.preflightStateHome`), crew's own registry copy on an older * addon, or nobody (no db path and no snapshot — a library boot). */ source: 'engine' | 'crew' | 'unavailable'; unregistered: UnregisteredStateHomeEntry[]; /** `true` exactly when a HANDED snapshot derives a state home with unregistered entries — the * condition core's intake fence refuses every launch on, and what `POST /runs` answers 409 for. */ refusesLaunches: boolean; findings: StateHomeFinding[]; remedy: string; /** Why the survey could not run, or `null`. */ error: string | null; /** When this survey was taken (unix ms). */ checkedAt: number; } /** The engine's `Core.preflightStateHome` static, as the adapter exposes it (or `null` on an older addon). */ export type EnginePreflight = (snapshotPath: string | null, dbPath: string) => Promise; /** * Crew's own survey of `stateHome` — the fallback classification for an addon without * `Core.preflightStateHome`: every top-level entry `state-home-registry.ts` does not classify, and * every child of the skills root that is neither the read slot nor a registered name * (`skills/root-names.ts`). Sorted as listed. Throws when a directory cannot be listed. */ export declare function surveyStateHome(stateHome: string): UnregisteredStateHomeEntry[]; /** crew#1021: health/diagnostics keep the fallback survey's directory reads off the main thread. */ export declare function surveyStateHomeAsync(stateHome: string): Promise; /** * The state home a handed snapshot derives (core's `state_home::of_snapshot`, by SHAPE alone): * `/skills/snapshots/` — three components up when the parent is the read slot, * the grandparent the skills root and the last component a generation (decimal digits). `null` * for any other spelling. */ export declare function stateHomeOfSnapshot(snapshotPath: string): string | null; export interface StateHomeWatchOptions { /** The core db the engine was spawned over (`CoreAdapter.dbPath`), or `null` (a stub route set). */ dbPath: string | null; /** What `WICKED_SKILLS_SNAPSHOT` is exported as right now — read at each refresh so a later * publish is judged against the generation it hands. */ snapshotPath: () => string | null; /** The engine binding, or `null` on an addon without it (crew classifies then). */ engine: EnginePreflight | null; /** Crew's fallback survey — injectable for tests. */ survey?: (stateHome: string) => UnregisteredStateHomeEntry[]; now?: () => number; } /** * The daemon's live state-home classification: {@link refresh} re-runs the survey (engine first, * crew's copy on an older addon) and {@link last} is the most recent answer — what the routes * report and gate on. Never throws: a survey that cannot run is an answer with `error` set. */ export declare class StateHomeWatch { private readonly opts; private lastHealth; private inFlight; constructor(opts: StateHomeWatchOptions); /** The most recent survey (the boot's until {@link refresh} runs again). */ get last(): StateHomeHealth; private cacheKey; /** crew#1021: coalesce only the current generation; an older survey cannot replace its answer. */ refresh(): Promise; private fromEngine; private fromCrew; } /** The `POST /runs` 409 body while the state home refuses launches: the typed error, as fields. */ export declare function stateHomeBlockerBody(health: StateHomeHealth): { error: string; code: typeof STATE_HOME_BLOCKER_CODE; stateHome: string | null; unregistered: UnregisteredStateHomeEntry[]; remedy: string; }; /** Does an engine launch rejection carry core's own intake refusal (`StateHomeConfigError`)? */ export declare function isEngineStateHomeRefusal(message: string): boolean; //# sourceMappingURL=state-home-preflight.d.ts.map