/** * ProjectManager — open/close a workspace, and the gate the ingress paths read. * * This is the Thread archival model moved to the level that survives. Thread * carried `status` and `TopicManager.requireOpen`; Project — the thing a * tenant actually owns, configures, and closes — carried nothing, so archiving * a workspace meant nothing to the code. See the hierarchy plan. * * Two deliberate differences from the Thread version: * * - Status moves **both ways**. A thread was archived forever; a workspace is * long-lived and a mistaken close should be recoverable. * - The gate is a **function over a store**, not a method on an injected * manager. The three ingress paths already hold a `SessionStore`, so * nothing has to be threaded through a constructor for the invariant to be * enforced — a gate that requires new wiring is a gate somebody forgets to * wire. */ import type { TenantId } from '../../types/ids/index.js'; import type { Project } from '../../types/project/entity.js'; import type { ProjectId } from '../../types/session/ids.js'; import type { SessionStore } from '../../types/session/store.js'; /** * Load a Project and assert it accepts new work. * * Throws on absence — a missing project is a hard error, not an assumed-open * one — and {@link ProjectClosedError} on an archived project. Returns the * loaded Project so the caller can skip a second read; every current caller * needs `config` immediately afterwards. * * `op` names the operation in the error, because "archived" is not by itself * an explanation of what the caller was refused. */ export declare function requireOpenProject(store: Pick, projectId: ProjectId, tenantId: TenantId, op: string): Promise; export interface ProjectManagerDeps { store: SessionStore; } export declare class ProjectManager { private readonly deps; constructor(deps: ProjectManagerDeps); /** See {@link requireOpenProject}. */ requireOpen(projectId: ProjectId, tenantId: TenantId, op?: string): Promise; /** * Close a workspace. * * Refuses while any attached session is non-terminal * ({@link ARCHIVAL_BLOCKING_STATUSES}), throwing {@link ProjectNotEmptyError} * with a sample of what is blocking. The presence check runs **before** the * already-archived short-circuit, so a workspace that is archived and still * harbouring a live session reports the live session rather than reporting * success — the second call is where an operator finds out. * * Idempotent: re-archiving an empty archived project is a no-op that * returns the stored record without a write, so it does not burn a version * and cannot lose a race it is not in. */ archive(projectId: ProjectId, tenantId: TenantId): Promise; /** * Reopen a workspace. Idempotent in the same way, and unconditional * otherwise: there is nothing a live session could make unsafe about * accepting work again. */ reopen(projectId: ProjectId, tenantId: TenantId): Promise; private write; } //# sourceMappingURL=lifecycle.d.ts.map