import type { GateStore, GateSignal, GateSignalStep } from "./gate-store.js";
import type { PreferencesStore } from "./preferences-store.js";
import type { RoleStore } from "./role-store.js";
import type { WorkflowGate, VerifyStep } from "./workflow-store.js";
import type { ProjectConfigStore, Component } from "./project-config-store.js";
import type { ToolManager } from "./tool-manager.js";
import type { McpManager } from "../mcp/mcp-manager.js";
import type { GrantPolicy } from "./role-store.js";
import { type GroupPolicyProvider } from "./tool-activation.js";
import type { ProjectContextManager } from "./project-context-manager.js";
export interface VerificationToolActivationDeps {
    toolManager?: ToolManager;
    groupPolicyStore?: GroupPolicyProvider;
    mcpManager?: McpManager | null;
}
export interface VerificationToolActivationResult {
    args: string[];
    env: Record<string, string>;
    toolManager?: ToolManager;
    allowedTools?: string[];
}
/**
 * Build Pi CLI flags for legacy direct verification sub-sessions using the
 * same post-Pi-0.70 contract as normal sessions: no unified `--tools`, file
 * builtins re-registered via `_builtins/extension.ts`, Bobbit extensions kept
 * active, and policy enforcement delegated to the guard extension.
 */
export declare function buildVerificationToolActivation(subSessionId: string, cwd: string, role: {
    toolPolicies?: Record<string, string | GrantPolicy>;
} | undefined, deps?: VerificationToolActivationDeps): VerificationToolActivationResult;
/**
 * Returns the **un-offset** branch container path for a goal — the directory
 * that `resolveStep()` / `componentRoot()` expect as their `branchContainer`
 * argument. Component step resolution layers `repo + relativePath` on top of
 * this value; if you pass `goal.cwd` (which may already include the project's
 * `rootPath` offset relative to the git repo root), the offset is applied
 * twice and verification fails with ENOENT on a doubled path segment.
 *
 * Pinned by `tests/verify-step-resolution.test.ts`.
 */
export declare function goalBranchContainer(goal: {
    worktreePath?: string;
    cwd: string;
}): string;
/**
 * Structural step resolution — see docs/design/multi-repo-components.md §3.3.
 *
 * Given a workflow step, the project's components[] (from project.yaml), and
 * the per-branch container root, return:
 *   - `cwd`: where the step should run
 *   - `runString`: the literal shell command, or `undefined` for non-command
 *     step types (callers handle those separately).
 *
 * Three command shapes are supported:
 *   { component, command }  → lookup `components[name].commands[name]`
 *   { component, run }      → literal `run`, cwd at component root
 *   { run }                 → literal `run`, cwd at branchContainer
 *
 * Throws `WorkflowResolveError` on unknown component / unknown command pairs
 * — the validator catches these at load-time, but runtime resolution still
 * defends in case the workflow snapshot was created before component edits.
 */
export declare function resolveStep(step: VerifyStep, components: Component[], branchContainer: string, ctx?: {
    workflow?: string;
    gate?: string;
    stepIndex?: number;
}): {
    cwd: string;
    runString?: string;
};
export interface VerificationPushSafetyVars {
    branch?: string;
    baseBranch?: string;
    master?: string;
}
export type VerificationPushSafetyResult = {
    ok: true;
} | {
    ok: false;
    reason: string;
};
export declare function validateVerificationPushSafety(command: string, vars: VerificationPushSafetyVars): VerificationPushSafetyResult;
/** Structured result delivered by the verification_result tool. */
export interface VerificationResult {
    verdict: boolean;
    summary: string;
    reportHtml?: string;
}
/**
 * Outcome of a `human-signoff` step. The verification harness parks an
 * awaiter in `pendingSignoffs` until either the REST handler resolves it
 * with a decision (pass/fail + optional feedback) or `cancelStaleVerifications`
 * drains it with `{ cancelled: true }`.
 */
export type SignoffOutcome = {
    decision: "pass" | "fail";
    feedback?: string;
} | {
    cancelled: true;
};
/** Reminder prompt sent when an agent goes idle without calling verification_result. */
export declare const VERIFICATION_RESULT_REMINDER: string;
/**
 * The `verification_result` tool is now a standard goal tool registered in
 * `.bobbit/config/tools/tasks/extension.ts` — no generated extension needed.
 * It calls POST /api/internal/verification-result using the same api() helper
 * as gate_signal, task_update, etc.
 */
export { isTransientReviewError, isTransientQaError, detectJsonValidationError } from "./verification-logic.js";
/**
 * In-flight verification state for REST bootstrapping.
 *
 * Per-step fields used for the "command subprocess survives a gateway
 * restart" scheme (Layer 1) and the duplicate-detection backstop (Layer 2):
 *
 * - `pid` / `startTimeMs` — child process id and spawn wall-clock time.
 *   Together with `bootEpoch` they identify a process we ourselves started.
 * - `outFile` / `errFile` / `exitFile` — files written by a small bash
 *   wrapper around the real command. `exitFile` is renamed into place
 *   atomically when the real command finishes, so even SIGKILL of the
 *   gateway leaves either no exit file (still running) or a complete one
 *   (finished). On resume we tail `outFile`/`errFile` and read `exitFile`
 *   to finalize the step. Only non-container command steps use this path;
 *   docker-exec steps stay on the simpler attached-pipe path because the
 *   exit file would have to live inside the container.
 * - `bootEpoch` — random UUID generated once per VerificationHarness
 *   instance (i.e. per server process). A step whose bootEpoch matches the
 *   current harness was started by THIS process and so its `pid`/file
 *   layout are addressable here. A step whose bootEpoch differs (or is
 *   absent) was started by a previous server lifetime and is treated as
 *   dead unless `pid` + `process.kill(pid, 0)` proves otherwise.
 */
export interface ActiveVerification {
    goalId: string;
    gateId: string;
    signalId: string;
    steps: Array<{
        name: string;
        type: string;
        status: "running" | "passed" | "failed" | "skipped" | "waiting";
        phase?: number;
        durationMs?: number;
        output?: string;
        startedAt: number;
        sessionId?: string;
        /** True while a `human-signoff` step is parked waiting on the user. */
        awaitingHuman?: boolean;
        /** Already-substituted markdown prompt shown to the user (human-signoff). */
        humanPrompt?: string;
        /** Human-readable label rendered on the sign-off card (human-signoff). */
        humanLabel?: string;
        /** OS process id of the spawned command (Layer 1). */
        pid?: number;
        /** Date.now() at spawn — tie-breaker against pid reuse. */
        startTimeMs?: number;
        /** Absolute path to detached child's stdout file (Layer 1). */
        outFile?: string;
        /** Absolute path to detached child's stderr file (Layer 1). */
        errFile?: string;
        /** Absolute path to detached child's exit-code file (Layer 1). */
        exitFile?: string;
        /** bootEpoch of the harness that started this step (Layer 2). */
        bootEpoch?: string;
        /** Step timeout in seconds — propagated for resume budget computation. */
        timeoutSec?: number;
        /** Whether the step expects a non-zero exit (for matchExpectFailure). */
        expectFailure?: boolean;
        /** Optional error-pattern regex for expectFailure matching. */
        errorPattern?: string;
    }>;
    currentPhase?: number;
    overallStatus: "running" | "passed" | "failed" | "cancelled";
    startedAt: number;
    cancelled?: boolean;
}
/**
 * Build the combined system prompt for a review step.
 *
 * Exported at module scope so unit tests can import it directly without
 * instantiating a harness. See docs/goals-workflows-tasks.md — "Gate
 * verification baselines".
 *
 * Branches on `isPreImplementationGate(gate)`:
 * - Pre-implementation (content gate with no upstream): no git diff/log
 *   instructions; `Baseline: none (design gate — no implementation expected)`.
 * - Implementation and later: `git diff origin/<primary>...HEAD` forms; the
 *   `Baseline` line records the resolved origin SHA so failures are trivial
 *   to diagnose.
 */
export declare function buildReviewPrompt(role: {
    promptTemplate: string;
    name?: string;
}, step: {
    name: string;
    prompt?: string;
}, cwd: string, builtinVars: Record<string, string>, signalContent?: string, signalMetadata?: Record<string, string>, goalSpec?: string, allGateStates?: Map<string, {
    metadata?: Record<string, string>;
    content?: string;
    status?: string;
    injectDownstream?: boolean;
}>, gate?: {
    content?: boolean;
    depends_on?: string[];
    dependsOn?: string[];
}): Promise<string>;
export declare class VerificationHarness {
    /** @deprecated Resolve per-goal via projectContextManager instead. */
    private gateStore;
    private _rawBroadcastFn;
    private roleStore;
    private preferencesStore?;
    private sessionManager?;
    private teamManager?;
    private projectConfigStore?;
    private notifyTeamLeadFn?;
    private activeVerifications;
    private readonly _persistPath;
    private projectContextManager;
    /**
     * Per-process random UUID stamped on every step that this harness
     * instance starts (Layer 2 — see `ActiveVerification` jsdoc). On boot,
     * any persisted step whose `bootEpoch` does not match was started by a
     * previous server lifetime and its `pid`/file paths are not addressable
     * by this process. Used by `areVerificationSessionsAlive` and the
     * resume path to distinguish "we own this child" from "this is a
     * zombie persisted from before restart".
     */
    private readonly bootEpoch;
    /** Flag for one-time cmd.exe detached-mode degradation warning. */
    private static _warnedCmdExeDetached;
    /** Limits concurrent command steps (type-check, tests) across all goals. */
    private commandSemaphore;
    /** Pending verification_result resolvers keyed by sessionId. */
    pendingResults: Map<string, (result: VerificationResult) => void>;
    /**
     * Pending human-signoff resolvers keyed by `${signalId}::${stepName}`.
     * Populated when a `human-signoff` step parks and `await`s the user;
     * drained by `resolveSignoff()` (user decision) or `cancelStaleVerifications()`
     * (gate re-signaled / goal completed).
     */
    pendingSignoffs: Map<string, (outcome: SignoffOutcome) => void>;
    /**
     * Resolve a pending human-signoff. Returns `true` if the resolver was
     * found and invoked, `false` if the step is no longer parked (idempotent
     * for callers that race with cancellation or a prior resolve).
     *
     * The verification harness's own `verifyGateSignal` branch builds the
     * step result + artifact from the outcome — callers do not write to the
     * gate store directly.
     */
    resolveSignoff(signalId: string, stepName: string, outcome: SignoffOutcome): boolean;
    /**
     * @deprecated The verification_result tool is now registered via the standard
     * goal tools extension. No generated extension file needed.
     */
    /** Get all active (in-flight) verifications, optionally filtered by goalId */
    getActiveVerifications(goalId?: string): ActiveVerification[];
    /**
     * Look up the active verification entry for a single signal id. Used by
     * the gate_signal REST handler to read back the `startedAt` stamped by
     * `beginVerification` so it can emit `gate_verification_started` AFTER
     * its own `gate_signal_received` broadcast. See goal
     * "Fix WS event ordering: signal_received must precede verification_started".
     */
    getActiveVerification(signalId: string): ActiveVerification | undefined;
    /**
     * Synchronously enumerate verification steps and seed the activeVerifications
     * map for `signal.id`. Returns the `GateSignalStep[]` shaped exactly for the
     * caller to write into `signal.verification.steps` *before* invoking
     * `gateStore.recordSignal(signal)`.
     *
     * Why this exists: the gate_signal REST handler used to create the signal
     * with `steps: []`, record it, and then fire-and-forget `verifyGateSignal()`
     * which built the active entry several `await`s later. Between `recordSignal`
     * and that async write, any consumer reading the gate-store or
     * `getActiveVerifications()` saw an empty step list — a race window of
     * 15-30s on multi-step gates with verification-harness setup cost. By
     * splitting enumeration (synchronous, cheap) from execution (async,
     * expensive) and inlining the enumeration into the REST handler before
     * `recordSignal`, both stores agree from the very first persisted state.
     *
     * Returns an empty array for gates with no `verify[]` steps — the caller
     * should still record the signal and `verifyGateSignal` will auto-pass it.
     *
     * Idempotent: calling twice for the same signal returns the same enumeration
     * without re-stamping `startedAt`.
     *
     * Does NOT broadcast `gate_verification_started` — the caller must emit
     * that event AFTER its own `gate_signal_received` broadcast to preserve
     * WS event ordering. See goal "Fix WS event ordering: signal_received
     * must precede verification_started".
     */
    beginVerification(signal: GateSignal, gate: WorkflowGate): GateSignalStep[];
    /**
     * Check if any verification sessions for a given signalId are still alive.
     *
     * "Alive" means we have evidence the OS process / agent session is still
     * doing useful work — not merely that the persisted `status === "running"`
     * flag survives on disk after a restart.
     *
     * Rules:
     * - `waiting` steps → alive (haven't started yet, phase-gated).
     * - LLM/agent steps with `sessionId` → alive iff sessionManager has the
     *   session.
     * - Command steps → alive iff THIS process started the child
     *   (`step.bootEpoch === this.bootEpoch`) AND the recorded `pid` is
     *   still running. After a server restart, bootEpoch always differs,
     *   so persisted-running command steps correctly read as not-alive and
     *   the duplicate-detection path can reclaim the gate.
     */
    areVerificationSessionsAlive(signalId: string): boolean;
    /**
     * Return session IDs from persisted active verifications that are still running.
     * Used by SessionManager to skip orphan cleanup for sessions that will be resumed.
     */
    getResumingSessionIds(): Set<string>;
    /** Persist active verifications to disk. */
    private _persistActive;
    /** Load persisted active verifications from disk. */
    private _loadActive;
    /**
     * Resume verifications that were interrupted by a server restart.
     * For running steps with sessionIds, attempts to extract or obtain a verdict
     * from the restored reviewer session. Fire-and-forget from the caller.
     */
    resumeInterruptedVerifications(): Promise<void>;
    /**
     * Look up the original VerifyStep definition from the goal's snapshotted workflow.
     * Returns undefined if not found (goal deleted, workflow missing, etc.).
     */
    private _findStepDefinition;
    /**
     * Gather the context needed to re-run an LLM review step from scratch.
     * Returns null if context is unavailable (goal deleted, etc.).
     */
    private _gatherRerunContext;
    private _resumeOneVerification;
    /**
     * Try to resume an llm-review step from its existing session.
     * Returns the step result, or null if no session exists.
     */
    private _tryResumeFromSession;
    /**
     * Re-run an LLM review step from scratch — used when resume fails transiently.
     * Looks up the original step definition from the goal's workflow and runs with
     * full retry logic (3 attempts with backoff).
     */
    private _rerunLlmReviewStep;
    /**
     * Re-run an agent-qa step from scratch — used when resume fails transiently.
     */
    private _rerunAgentQaStep;
    private readonly _stateDir;
    private configCascade?;
    /** Monotonic counter used to stamp `seq` on every broadcast event. */
    private _verifSeqCounter;
    /**
     * Tracked subprocess for each live command-step, keyed by
     * `${signalId}:${stepIndex}`. Used by `cancelVerification` /
     * `cancelStaleVerifications` to tree-kill the running shell on cancel,
     * and by `shutdown()` for graceful gateway exit.
     */
    private _trackedCommandChildren;
    /**
     * Tracked-child keys that were killed by an explicit cancellation rather
     * than a timeout or natural exit. Read in `runCommandStep`'s close
     * handler so the resolved output carries the cancellation marker even
     * after the parent `ActiveVerification` entry has been purged.
     */
    private _cancelledTrackedKeys;
    private readonly broadcastFn;
    constructor(stateDir: string, 
    /** @deprecated Resolve per-goal via projectContextManager instead. */
    gateStore: GateStore | undefined, _rawBroadcastFn: (goalId: string, event: any) => void, roleStore: RoleStore, preferencesStore?: PreferencesStore | undefined, sessionManager?: import("./session-manager.js").SessionManager | undefined, teamManager?: import("./team-manager.js").TeamManager | undefined, projectConfigStore?: ProjectConfigStore | undefined, projectContextManager?: ProjectContextManager, configCascade?: import("./config-cascade.js").ConfigCascade);
    /**
     * Resolve a role from the cascade so project-level overrides apply, falling
     * back to the server-level role store when the cascade is unavailable
     * (e.g. unit tests). Returns undefined if the role does not exist.
     */
    private resolveRoleForGoal;
    private resolveProjectConfigStore;
    private resolveToolActivationDeps;
    /**
     * Pick a component to source `config.qa_*` from when an agent-qa step
     * does not declare `component:` explicitly. Preference order:
     *   1. First component whose `config.qa_start_command` is set.
     *   2. Component whose `name` matches the project name.
     *   3. `components[0]`.
     * Returns undefined when no components are configured.
     */
    private resolveDefaultQaComponentName;
    private resolveGateStore;
    /** Register a callback to notify the team lead agent when verification completes. */
    setTeamLeadNotifier(fn: (goalId: string, message: string) => void): void;
    /**
     * Tree-kill any tracked command-step subprocess registered under the given
     * signalId. Uses SIGTERM with a 1s SIGKILL escalation so cancellation is
     * observable within ~1s (single-timer path, no setInterval poll).
     */
    private _killTrackedForSignal;
    /**
     * Drain every pending human-signoff resolver whose key matches the given
     * signalId. Used by `cancelStaleVerifications` / `cancelAllVerifications`
     * so a re-signal or goal-complete unblocks any parked `await promise`
     * inside `verifyGateSignal`'s human-signoff branch — the awaited promise
     * resolves with `{ cancelled: true }` and the outer `active.cancelled`
     * short-circuit handles the rest of the cleanup.
     */
    private _drainPendingSignoffsForSignal;
    /**
     * Graceful shutdown — kill every in-flight tracked subprocess tree so
     * orphan chromium / playwright descendants don't survive the gateway exit.
     */
    shutdown(): void;
    /**
     * Cancel ALL in-flight verifications for a goal (all gates).
     * Called when a goal completes, a team is torn down, or the goal is shelved.
     */
    cancelAllVerifications(goalId: string): Promise<void>;
    /**
     * Cancel any in-flight verifications for the same (goalId, gateId).
     * Terminates reviewer sessions and removes from activeVerifications.
     */
    cancelStaleVerifications(goalId: string, gateId: string): Promise<void>;
    /**
     * Cancel in-flight verifications for any matching gate in one synchronous
     * marking pass before awaiting reviewer-session cleanup. This lets callers
     * invalidate several gates without a later verification completing between
     * per-gate awaits and re-marking a reset gate.
     */
    cancelStaleVerificationsForGates(goalId: string, gateIds: string[]): Promise<void>;
    private notifyTeamLead;
    /**
     * Verify a gate signal asynchronously (fire-and-forget from caller).
     * Updates signal verification results and gate status when done.
     */
    verifyGateSignal(signal: GateSignal, gate: WorkflowGate, cwd: string, goalBranch?: string, primaryBranch?: string, allGateStates?: Map<string, {
        metadata?: Record<string, string>;
        content?: string;
        status?: string;
        injectDownstream?: boolean;
    }>, goalSpec?: string): Promise<void>;
    /**
     * Spawn a one-shot reviewer sub-agent to perform an LLM-powered code review.
     * Follows the pattern from src/server/skills/sub-agent.ts.
     */
    private runLlmReviewStep;
    /**
     * Run an LLM review step via SessionManager (visible in UI as a proper session).
     */
    private runLlmReviewViaSession;
    /**
     * Build the kickoff message sent to a QA-tester sub-agent. Exposed as a
     * static helper so unit tests can assert that the resolved component name
     * is threaded into a `[QA-TEST CONTEXT]` block. The /qa-test skill reads
     * this block in Step 1 to disambiguate when multiple components carry
     * `config.qa_start_command`.
     */
    static buildQaKickoffMessage(args: {
        stepName: string;
        prompt?: string;
        branch?: string;
        commit?: string;
        componentName?: string;
    }): string;
    /**
     * Spawn a one-shot test-engineer sub-agent to perform QA testing.
     * Similar to runLlmReviewViaSession() but with test-engineer role and QA-specific prompt.
     */
    private runAgentQaStep;
    /**
     * Legacy direct-RpcBridge path for LLM review (invisible to UI).
     * Used when SessionManager is not available.
     */
    private runLlmReviewDirect;
    /**
     * Substitute namespaced variables in a template string.
     *
     * Namespaces:
     * - {{branch}}, {{master}}, etc. — built-in goal variables
     * - {{project.key}} — from project config (.bobbit/config/project.yaml)
     * - {{agent.key}} — from the signal's metadata (provided by the agent)
     * - {{gate_id.meta.key}} — from an upstream gate's metadata
     * - {{goal_spec}} — the goal specification text
     *
     * Legacy bare references like {{typecheck_command}} are NOT resolved to
     * prevent accidental cross-namespace collisions. Use the explicit namespace.
     */
    private substituteVars;
    /**
     * Spawn a command-type verification step.
     *
     * Two execution modes:
     *
     * 1. **Detached survival mode** (Layer 1 — default for non-container,
     *    streamed steps): the child is spawned with `detached: true` and
     *    stdout/stderr redirected to files under
     *    `<stateDir>/verifications/<signalId>/<stepIndex>.{out,err}`. A small
     *    shell wrapper writes the exit code to `<stepIndex>.exit` via an
     *    atomic `mv tmp → exit` so that even SIGKILL of the gateway leaves
     *    either no exit file (child still running) or a complete one. We
     *    `unref()` the child, stamp pid + bootEpoch + file paths onto the
     *    persisted `ActiveVerification.step`, and from the parent we tail
     *    the files for live broadcast. On gateway restart,
     *    `_resumeCommandStep` re-attaches by polling the exit file.
     *
     * 2. **Attached pipe mode** (fallback): used when running inside a docker
     *    container (`containerId` set) or when no `streamCtx` is available
     *    (i.e. no signal/step to anchor file paths on). Docker steps stay on
     *    this path because writing the exit file inside the container while
     *    persisting state on the host adds complexity that isn't required by
     *    the acceptance criteria — container survival across host gateway
     *    restart is intentionally out of scope.
     */
    private runCommandStep;
    /**
     * Poll the per-step stdout/stderr files for new bytes and broadcast each
     * chunk as a `gate_verification_step_output` event, mirroring the live
     * UI broadcast path of the legacy attached-pipe mode. Returns a stop
     * function that does a final flush before clearing the interval.
     */
    private _startFileTailers;
    /**
     * Resume a command-type step that was running when the gateway died.
     *
     * Strategy (see `ActiveVerification` jsdoc for context):
     *
     * 1. If `exitFile` already exists — the wrapper completed before we got
     *    back — read it plus the stdout/stderr tails and finalize via the
     *    same `matchExpectFailure` / pass-fail logic the live path uses.
     * 2. Else if `pid` is still alive — the detached child outlived the
     *    gateway and is still chugging away. Poll for the exit file with
     *    the remaining timeout budget computed from `startedAt`.
     * 3. Else — process is gone and there's no exit file. The child was
     *    killed (OOM, manual kill, antivirus). Finalize as failed.
     *
     * Returns null when there's nothing to resume (no exit file recorded,
     * e.g. the step pre-dates Layer 1 or used the attached-mode fallback)
     * so the caller can fall through to the legacy "no session id" failure.
     */
    private _resumeCommandStep;
}
//# sourceMappingURL=verification-harness.d.ts.map