/**
 * hosting/conformance/types — what a store presents to the battery, and what
 * the battery hands back.
 */
import type { CheckpointEnvelope, SessionLifecycle } from '../types.js';
/**
 * Every case in the battery, by name.
 *
 * A closed union on purpose. A harness DECLARES the cases it cannot satisfy by
 * writing their names down, and a name that is a free-form string is a name
 * that goes stale silently — a declaration for a case that was renamed would
 * keep suppressing nothing at all, which is the same shape as the bug this
 * whole suite exists to catch.
 */
export type SessionLifecycleCaseName = 'absent-session-hydrates-undefined' | 'persist-hydrate-round-trip' | 'answered-pause-round-trips' | 'unreadable-is-not-absent' | 'forget-removes-the-conversation' | 'ownership-is-derived-from-the-envelope' | 'ownership-fills-in-on-a-later-signed-turn' | 'ownership-survives-a-leaner-turn' | 'ownership-is-not-taken-by-a-different-signer' | 'contested-write-leaves-no-split-brain' | 'owner-of-is-undefined-for-missing-and-unowned' | 'list-by-user-pages-with-a-stable-tie-break' | 'list-by-user-is-newest-first' | 'list-by-user-shows-an-answered-pause-as-a-conversation' | 'an-id-the-store-hands-back-is-not-a-second-address' | 'awkward-session-ids-round-trip' | 'retention-says-who-deletes-and-deletes-only-the-old' | 'optional-members-are-feature-detected';
/**
 * Members a case cannot run without.
 *
 * `listByUser`, `ownerOf` and `retention` are OPTIONAL on the port, and
 * `forget` is not on it at all — so a store that lacks one is not failing
 * anything, and a case about it is reported `'not-applicable'` rather than
 * passed or failed. That distinction is the port's own feature-detection rule,
 * applied to its own test battery.
 */
export type SessionStoreMember = 'listByUser' | 'ownerOf' | 'forget' | 'retention';
/**
 * How the battery reaches one store.
 *
 * A factory rather than an instance, because most of the battery needs a store
 * with NOTHING in it — a listing case that saw another case's rows would be
 * asserting on somebody else's fixtures — and because a store that has been
 * closed, or whose file was removed, cannot be reset in place. One store per
 * case, disposed after it, is the only shape that holds for a `Map`, a file, a
 * transaction and a managed service at once.
 */
export interface SessionStoreHarness {
    /** What this store is called in a report. */
    readonly name: string;
    /**
     * A fresh store holding no sessions. Called once per case.
     *
     * May be sync or async: some stores open a file, some await a connection,
     * and a battery that demanded one shape would exclude half the stores it is
     * here to check.
     */
    createStore(): SessionLifecycle | Promise<SessionLifecycle>;
    /**
     * Release what `createStore` acquired. Called after every case, including
     * the ones that failed — a store left open by a failing case is a file
     * handle leak that shows up three cases later as a confusing second failure.
     */
    disposeStore?(store: SessionLifecycle): void | Promise<void>;
    /**
     * Replace one session's stored bytes with something that is NOT a readable
     * envelope, behind the store's back.
     *
     * There is no portable way to do this — it is a poke at the backing table,
     * the file, or the document — so it is the harness's job. A store that
     * cannot be corrupted from outside must DECLARE
     * `'unreadable-is-not-absent'` with the reason.
     */
    corrupt?(store: SessionLifecycle, sessionId: string): void | Promise<void>;
    /**
     * Cases this store cannot satisfy, BY NAME, each with the reason.
     *
     * The reason is required, and it is the point. A store may legitimately be
     * unable to satisfy a case — a service whose owner field is immutable at
     * creation cannot fill one in on turn two — and the honest way to record
     * that is a sentence somebody can disagree with. A silent skip is a pass
     * with the evidence removed.
     *
     * A declared case still RUNS. If it turns out to pass, the report says so,
     * and the declaration is stale: a gate that absolves itself is worth
     * catching, and so is a gate nobody needed.
     */
    readonly declared?: Partial<Record<SessionLifecycleCaseName, string>>;
}
/** The helpers a case is handed, beside the store. */
export interface ConformanceKit {
    /** A session id nothing else in this run uses. */
    id(suffix: string): string;
    /** A stored conversation, signed by `principal` when one is given. */
    envelope(text: string, principal?: string, savedAt?: number): CheckpointEnvelope;
    /** The harness, for the cases that need one of its hooks. */
    readonly harness: SessionStoreHarness;
}
/** One case in the battery. */
export interface SessionLifecycleCase {
    readonly name: SessionLifecycleCaseName;
    /** The law it holds, in one sentence — printed beside a failure. */
    readonly law: string;
    /** Store members without which this case does not apply. */
    readonly members?: readonly SessionStoreMember[];
    /** Harness hooks without which this case cannot run at all. */
    readonly harnessNeeds?: readonly 'corrupt'[];
    run(store: SessionLifecycle, kit: ConformanceKit): Promise<void>;
}
/**
 * How one case came out.
 *
 *  - `'passed'` — the store holds the law.
 *  - `'not-applicable'` — the case is about an OPTIONAL member this store does
 *    not implement. Feature detection, not a gap.
 *  - `'declared'` — the store implements the member and cannot satisfy the
 *    case, and said so by name. `stillFails: false` means the declaration is
 *    stale: it passes now.
 *  - `'failed'` — including "needed a harness hook nobody provided and nobody
 *    declared", because an undeclared skip is exactly what this suite refuses
 *    to let look like a pass.
 */
export type SessionLifecycleOutcome = {
    readonly case: SessionLifecycleCaseName;
    readonly law: string;
} & ({
    readonly status: 'passed';
} | {
    readonly status: 'not-applicable';
    readonly missing: SessionStoreMember;
} | {
    readonly status: 'declared';
    readonly reason: string;
    readonly stillFails: boolean;
} | {
    readonly status: 'failed';
    readonly error: Error;
});
/** What one store's whole run came to. */
export interface SessionLifecycleReport {
    /** The harness name. */
    readonly store: string;
    readonly outcomes: readonly SessionLifecycleOutcome[];
    readonly passed: number;
    readonly notApplicable: number;
    readonly declared: number;
    readonly failed: number;
    /** True when nothing failed. Declarations do not make a store non-conformant
     *  — they make it conformant WITH STATED LIMITS, which is a different claim
     *  and the report prints both. */
    readonly ok: boolean;
}
