/**
 * SlackScenarioHarness — Layer-A of the "test-as-self for Slack" demonstration
 * (Pillar 4, §8.3). A deterministic, credential-free scenario suite that drives the
 * SlackPermissionGate with a fixed cast of test users and asserts the decision for
 * each (principal, request) pair. It runs in CI on every build (the regression wall)
 * and is reusable by a future live-workspace demo command.
 *
 * Design: docs/specs/SLACK-ORG-INTEGRATION-SPEC.md §8 (Pillar 4) + §9 (worked examples).
 */
import type { Principal, PermissionDecision, PermissionVerdict, AuthorityGrant } from '../types.js';
import { SlackPermissionGate, type GrantStore } from '../SlackPermissionGate.js';
import { type UserLookup, type ResolvedUserRecord } from '../SlackPrincipalResolver.js';
import { SlackPermissionObserver } from '../SlackPermissionObserver.js';
import { type PermissionLedgerEntry } from '../PermissionDecisionLedger.js';
/**
 * The fixed cast of test users (§8.2). Each is a real registered (or deliberately
 * UNregistered) principal with a verified slackUserId, a role, and a relationship
 * baseline (where a scenario needs anomaly). `grantedGrace` is the member who has
 * been issued a time-boxed floor grant (the A4.3 "Maya now deploys" deterministic
 * mirror — proves the `floor-granted` path without a PIN-gated live mandate).
 */
export declare const CAST: Record<string, Principal>;
/**
 * Which slackUserId currently holds an active floor grant, and for what scope.
 * Consumed by {@link StaticGrantStore} (Layer-A deterministic mirror of the
 * MandateBackedGrantStore) so the granted-member-floor row reaches `floor-granted`.
 * Grace (a member) holds a prod-deploy grant authorized by Olivia (requester ≠
 * authorizer is preserved). No other principal has a grant — deny-by-default.
 */
export declare const ACTIVE_GRANTS: AuthorityGrant[];
/**
 * Layer-A deterministic grant store — the credential-free mirror of
 * MandateBackedGrantStore. The live A4 path issues a PIN-gated, signed,
 * expiring Coordination Mandate grant; that is an operator action and cannot
 * run in CI. This static store returns the SAME shape of grant for the same
 * (slackUserId, scope), so the gate's `floor-granted` branch is exercised
 * identically — only the grant's PROVENANCE differs (a fixture vs a signed
 * mandate), which is exactly the deterministic-vs-live seam (§8.3, §8.5).
 */
export declare class StaticGrantStore implements GrantStore {
    private readonly grants;
    constructor(grants?: AuthorityGrant[]);
    activeGrant(slackUserId: string, scope: string, now: number): AuthorityGrant | undefined;
}
export interface Scenario {
    id: string;
    principal: Principal;
    text: string;
    directed: boolean;
    expectedDecision: PermissionDecision;
    expectedBasis: string;
    proves: string;
}
/**
 * The assertion rows (§8.4 + §9 A–F, plus the two deterministic-subset rows the
 * live runbook adds: a granted-member floor → allow (A4.3) and an unregistered
 * outsider → refuse (A1.3)). Scenario 5 models the CEO's own account behaving
 * anomalously (account-compromise / social-engineering), so it reuses the verified
 * owner principal with an out-of-character money-movement request.
 */
export declare const SCENARIOS: Scenario[];
/**
 * Build the Slice-0 gate wired with the deterministic (heuristic) classifier +
 * anomaly scorer + the Layer-A static grant store. The grant store returns a grant
 * ONLY for Grace's prod-deploy (ACTIVE_GRANTS) — every other principal is
 * deny-by-default, so none of the existing rows' verdicts change; it solely enables
 * the `floor-granted` path for the granted-member-floor row.
 */
export declare function buildSliceZeroGate(): SlackPermissionGate;
export interface ScenarioResult {
    scenario: Scenario;
    verdict: PermissionVerdict;
    pass: boolean;
    mismatch?: string;
}
/** Run all scenarios through a gate and report per-row pass/fail. */
export declare function runScenarioSuite(gate?: SlackPermissionGate): Promise<ScenarioResult[]>;
/**
 * A registry-backed {@link UserLookup} built from the fixed cast, so the pipeline
 * resolves a verified slackUserId → Principal exactly as production does (an
 * unregistered id — Omar — resolves to an unregistered guest). Identity comes ONLY
 * from the authenticated slackUserId, never from message content.
 */
export declare class CastUserLookup implements UserLookup {
    private readonly bySlackId;
    constructor(cast?: Record<string, Principal>);
    resolveFromSlackUserId(slackUserId: string): ResolvedUserRecord | null;
}
/** Build the production-shaped observer (resolver → Slice-0 gate → ledger) for the harness. */
export declare function buildScenarioObserver(stateDir: string): SlackPermissionObserver;
export interface AuditedScenarioResult {
    scenario: Scenario;
    verdict: PermissionVerdict | null;
    /** The decision-ledger entry that landed for this row (the audit proof). */
    ledgerEntry?: PermissionLedgerEntry;
    /** Did the verdict match the expected decision+basis? */
    verdictOk: boolean;
    /** Did a ledger/audit entry with the expected decision+basis actually land? */
    auditOk: boolean;
    /** Both must hold ("verified, not narrated"). */
    pass: boolean;
    mismatch?: string;
}
export interface AuditedSuiteReport {
    summary: {
        total: number;
        passed: number;
        failed: number;
    };
    /** Where the decision ledger was written (the durable audit trail). */
    ledgerPath: string;
    rows: AuditedScenarioResult[];
}
/**
 * Run every scenario through the real observer (resolver → gate → ledger) and
 * assert BOTH the verdict and the matching audit entry per row. Writes the ledger
 * into a fresh temp state dir (or `stateDir` if provided) so the run is hermetic.
 *
 * NOTE on the resolver: the observer resolves the principal from the
 * authenticated slackUserId via {@link CastUserLookup}; the scenario's
 * `principal` field is only used to supply that slackUserId/displayName and to
 * document the intended cast member. The role/registration the gate actually sees
 * comes from the registry resolution — so this exercises the real
 * identity-binding path, not a hand-built Principal.
 */
export declare function runAuditedScenarioSuite(stateDir?: string): Promise<AuditedSuiteReport>;
//# sourceMappingURL=SlackScenarioHarness.d.ts.map