/**
 * SafeGitExecutor — the single funnel for destructive git invocations.
 *
 * Background: PR #96 (DESTRUCTIVE-TOOL-TARGET-GUARDS-SPEC) added
 * `assertNotInstarSourceTree` to the constructors of `GitSyncManager`,
 * `BranchManager`, and `HandoffManager`. Five days later, Incident B recurred
 * because test fixtures invoked `execFileSync('git', ['add', '-A'], { cwd })`
 * directly — bypassing the manager constructors entirely.
 *
 * This module is the funnel layer described in
 * `docs/specs/COMPREHENSIVE-DESTRUCTIVE-TOOL-CONTAINMENT-SPEC.md`. Every
 * destructive git invocation in the codebase routes through one of three
 * methods on this class, each of which calls `assertNotInstarSourceTree`
 * BEFORE spawning git. Read-only verbs may go through `readSync`, which
 * also runs the assertion (defense-in-depth against repo-local aliases).
 *
 * Constraints enforced:
 *   - Every directory the subprocess could mutate is canonicalized via
 *     `realpathSync` and passed through `assertNotInstarSourceTree`. This
 *     covers `opts.cwd`, the `-C <dir>` target, and the path values in
 *     `--git-dir=<path>`, `--work-tree=<path>`. Any one being the instar
 *     source causes the call to throw before subprocess spawn.
 *   - Caller-supplied env has the git-redirection denylist stripped:
 *     GIT_DIR, GIT_WORK_TREE, GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY,
 *     GIT_COMMON_DIR, GIT_NAMESPACE, GIT_CONFIG*, GIT_CEILING_DIRECTORIES,
 *     GIT_DISCOVERY_ACROSS_FILESYSTEM.
 *   - GIT_CONFIG_GLOBAL=/dev/null + GIT_CONFIG_SYSTEM=/dev/null +
 *     GIT_CONFIG_NOSYSTEM=1 are injected unconditionally. This disables
 *     user-level and system-level git config (including aliases that could
 *     rebind a "read-only" verb to a destructive command).
 *   - A JSON line is appended to .instar/audit/destructive-ops.jsonl per
 *     call (fail-soft on log write failure).
 */
import { type ChildProcess } from 'node:child_process';
import { SourceTreeGuardError } from './SourceTreeGuard.js';
/**
 * Verbs treated as destructive. Closed enumeration; additions are spec changes.
 */
export declare const DESTRUCTIVE_GIT_VERBS: ReadonlySet<string>;
/**
 * Read-only verbs explicitly safe to call. Closed enumeration.
 */
export declare const READONLY_GIT_VERBS: ReadonlySet<string>;
/**
 * Verbs that are "data-pull": they touch the object database and FETCH_HEAD
 * but DO NOT modify the working tree, committed refs (heads/tags/remotes),
 * or any source file. From SourceTreeGuard's "protect the instar source"
 * standpoint they are read-tier. Permitted on the instar source tree ONLY
 * when the caller opts in via `SafeGitOptions.sourceTreeReadOk: true`.
 *
 * Currently: `fetch` only. (`ls-remote` is pure-read and already in
 * READONLY_GIT_VERBS — it goes through readSync which never trips the
 * source-tree guard.)
 */
export declare const SOURCE_TREE_READ_TIER_VERBS: ReadonlySet<string>;
declare function repoHasLocalIdentity(cwd: string): boolean;
declare function _resetLocalIdentityCacheForTest(): void;
declare function sanitizeEnv(callerEnv?: NodeJS.ProcessEnv, cwd?: string): NodeJS.ProcessEnv;
interface ExtractedTargets {
    verb: string;
    /** All directories the subprocess could mutate (canonical). Includes:
     * - opts.cwd
     * - the `-C <dir>` target
     * - the path values in `--git-dir=<path>` and `--work-tree=<path>`
     *   (and the equivalent `-C <dir>` form)
     */
    targets: string[];
}
/**
 * Walk the args array skipping leading pre-verb global flags. Return:
 *   { verb, targets: [opts.cwd, -C target, --git-dir target, --work-tree target] }
 *
 * Targets are canonicalized via `realpathSync`. If realpath fails on an
 * existing path the raw value is passed through; the assertion handles
 * uncanonicalizable inputs in fail-closed fashion.
 */
declare function extractVerbAndTargets(args: readonly string[], cwd: string | undefined): ExtractedTargets;
/**
 * Ambiguous-verb shape check. Returns true if (verb, verbArgs) describes
 * a read-only invocation. Returns false for unambiguously-destructive
 * shapes. Returns null if the verb isn't ambiguous (caller uses verb-set
 * membership).
 */
declare function isReadOnlyShape(verb: string, verbArgs: readonly string[]): boolean | null;
interface AuditEntry {
    timestamp: string;
    executor: 'git' | 'fs';
    operation: string;
    verb?: string;
    target: string;
    outcome: 'allowed' | 'denied';
    reason?: string;
    caller?: string;
}
export declare function appendAuditEntry(entry: AuditEntry): void;
export interface CredentialResolutionEntry {
    timestamp: string;
    kind: 'resolution' | 'boot-coherence';
    /** For kind 'resolution': which way sanitizeEnv resolved identity. */
    decision?: 'repo-local-strip' | 'host-identity-inject';
    /** Identity env vars deleted because the repo-local identity is authoritative. */
    strippedKeys?: string[];
    /** Identity env vars injected from the host identity (repo had none). */
    injectedKeys?: string[];
    cwd?: string;
    /** For kind 'boot-coherence': the agent's expected identity and its source. */
    expected?: {
        name?: string;
        email?: string;
        source: string;
    };
    /** Surfaces whose identity disagrees with (or shadows) the expected one. */
    divergences?: Array<{
        surface: string;
        detail: string;
    }>;
    /** Whether a machine-global gh CLI auth state file exists (presence only). */
    ghHostsPresent?: boolean;
}
declare function _resetCredentialAuditDedupeForTest(): void;
export declare function appendCredentialResolutionEntry(entry: CredentialResolutionEntry): void;
/** Minimal git-config parse: the VALUE of `key` in the [user] section, if any. */
declare function configGet(file: string, key: 'name' | 'email'): string | undefined;
export interface BootCredentialCoherenceReport {
    expected: {
        name?: string;
        email?: string;
        source: string;
    };
    divergences: Array<{
        surface: string;
        detail: string;
    }>;
    ghHostsPresent: boolean;
    sampledAt: string;
}
/**
 * Boot-time credential coherence sample (Inc-P3d). Reads the agent's
 * EXPECTED identity from the repo-local git config of `agentDir`, compares
 * the machine's other identity surfaces against it (inherited identity env
 * vars, the machine-global ~/.gitconfig), and notes whether a machine-global
 * gh CLI auth state exists. Pure fs reads — no subprocess (the P3a lesson:
 * funnel-adjacent probes must not consume mocked child_process sequences).
 * Records one 'boot-coherence' line in credential-resolution.jsonl and
 * returns the report. Never throws; returns null if sampling itself broke.
 */
export declare function auditBootCredentialCoherence(agentDir?: string, homeDirOverride?: string): BootCredentialCoherenceReport | null;
export interface SafeGitOptions {
    /** The directory the git command will mutate. Defaults to process.cwd(). */
    cwd?: string;
    /** Caller label for error messages and audit log. */
    operation: string;
    /** stdio passthrough, default 'pipe'. Accepts the same shapes execFileSync does. */
    stdio?: 'pipe' | 'inherit' | 'ignore' | Array<'pipe' | 'inherit' | 'ignore' | number | null | undefined>;
    /** Encoding for return value, default 'utf-8'. */
    encoding?: BufferEncoding;
    /** Timeout in ms, default 30000. */
    timeout?: number;
    /** Env passthrough — denylist for git-redirection variables applied. */
    env?: NodeJS.ProcessEnv;
    /** Optional: input string for the subprocess stdin. */
    input?: string | Buffer;
    /** Maximum buffer for stdout/stderr (execFileSync). */
    maxBuffer?: number;
    /**
     * Opt-in: allow execSync/spawn against the instar source tree for a small
     * allowlist of data-pull verbs that do not mutate committed refs or the
     * working tree (currently `fetch` and `ls-remote`). Defaults false.
     *
     * Why this exists: SourceTreeGuard refuses non-readonly git ops on the
     * instar source tree (the 2026-04-22 incident-class). But the release-
     * readiness watchdog (LAYER B of RELEASE-READINESS-VISIBILITY-SPEC) and
     * its sibling FeatureRolloutReconciler canonical scan (LAYER C) NEED to
     * pull the canonical `main` ref into the agent's own instar checkout —
     * by-definition a source tree — to do their job. `git fetch <remote>
     * <branch> --no-tags --no-recurse-submodules` writes only to FETCH_HEAD
     * (transient) and the object database; it does not modify the working
     * tree or any committed refs, so from the source-protection standpoint
     * it is read-tier. `ls-remote` is pure read. This flag is the narrow,
     * audited escape hatch the spec referenced.
     */
    sourceTreeReadOk?: boolean;
    /**
     * Opt-in: allow the InstarWorktreeManager's narrow source-tree operations.
     *
     * `instar worktree create` must run against a validated instar source
     * checkout to create a sibling worktree. That requires a tiny set of git
     * reads, `git worktree add/prune`, and per-worktree `user.name` /
     * `user.email` config writes. This flag does not allow general source-tree
     * mutation; every permitted shape is enumerated by
     * `isSourceTreeWorktreeManagerInvocation`.
     */
    sourceTreeWorktreeManagerOk?: boolean;
}
export declare class SafeGitExecutorError extends Error {
    readonly code = "INSTAR_SAFE_GIT_EXECUTOR";
    constructor(message: string);
}
export declare class SafeGitExecutor {
    /**
     * Synchronous destructive git execution.
     *
     * 1. Extract the verb and ALL target directories from args + opts.cwd.
     * 2. Canonicalize each target and call assertNotInstarSourceTree on each.
     * 3. Verify the verb is in DESTRUCTIVE_GIT_VERBS (or is an ambiguous verb
     *    in destructive shape). Read-only verbs throw — callers must use readSync.
     * 4. Strip env denylist; inject GIT_CONFIG_GLOBAL/SYSTEM=/dev/null.
     * 5. Spawn git via execFileSync; return stdout.
     *
     * Throws SourceTreeGuardError if any target is the instar source tree.
     * Throws SafeGitExecutorError on classification mismatch or arg errors.
     */
    static execSync(args: readonly string[], opts: SafeGitOptions): string;
    /**
     * Async/streaming variant. Same guard semantics as execSync.
     */
    static spawn(args: readonly string[], opts: SafeGitOptions): ChildProcess;
    /**
     * Read-only escape valve. Runs `git <args>` after verifying:
     *   - Verb is in READONLY_GIT_VERBS (or is an ambiguous verb in read-only shape).
     *   - assertNotInstarSourceTree passes against every target (defense-in-depth
     *     against repo-local aliases that could rebind a "read-only" verb).
     */
    static readSync(args: readonly string[], opts: SafeGitOptions): string;
    /**
     * Verb-aware dispatcher. Routes to readSync for known read-only verbs and to
     * execSync otherwise. Use this from generic git() helpers that take dynamic
     * args and don't know at the call site whether the verb is destructive.
     */
    static run(args: readonly string[], opts: SafeGitOptions): string;
}
/** @internal — exposed for SafeGitExecutor.test.ts */
export declare const _internal: {
    extractVerbAndTargets: typeof extractVerbAndTargets;
    isReadOnlyShape: typeof isReadOnlyShape;
    sanitizeEnv: typeof sanitizeEnv;
    GIT_ENV_DENYLIST: ReadonlySet<string>;
    repoHasLocalIdentity: typeof repoHasLocalIdentity;
    _resetLocalIdentityCacheForTest: typeof _resetLocalIdentityCacheForTest;
    _resetCredentialAuditDedupeForTest: typeof _resetCredentialAuditDedupeForTest;
    configGet: typeof configGet;
};
export { SourceTreeGuardError };
//# sourceMappingURL=SafeGitExecutor.d.ts.map