/**
 * Branch-lock and owner-override authentication contracts for T1118.
 *
 * Defines types, error codes, and interfaces for the four-layer agent
 * branch-protection system:
 *
 * - L1: Git worktree isolation per spawned agent (create + merge-complete + cleanup)
 * - L2: git-shim binary on PATH for harness-agnostic enforcement
 * - L3: Filesystem hardening via chmod (+ optional chattr on Linux)
 * - L4: Owner-override HMAC session authentication with TTY + rate-limit gates
 *
 * Worktree integration uses `git merge --no-ff` per ADR-062. The legacy
 * cherry-pick path was removed in T1624.
 *
 * @task T1118
 * @adr ADR-055
 * @adr ADR-062
 */
// ---------------------------------------------------------------------------
// Error codes for branch-lock enforcement
// ---------------------------------------------------------------------------
/**
 * Error codes emitted by the branch-lock + override-auth system.
 *
 * @task T1118
 * @task T1501
 * @task T1502
 */
export const BRANCH_LOCK_ERROR_CODES = {
    /** L2: git shim blocked a branch-mutating operation. */
    E_GIT_OP_BLOCKED: 'E_GIT_OP_BLOCKED',
    /** L1: spawn attempted without a worktree handle. */
    E_WORKTREE_REQUIRED: 'E_WORKTREE_REQUIRED',
    /** L1: worktree path does not exist or is not a valid git worktree. */
    E_WORKTREE_INVALID: 'E_WORKTREE_INVALID',
    /** L1: merge --no-ff failed during worktree.complete (ADR-062). */
    E_MERGE_FAILED: 'E_MERGE_FAILED',
    /** L3: filesystem harden failed. */
    E_FS_HARDEN_FAILED: 'E_FS_HARDEN_FAILED',
    /** L4a: HMAC token invalid or missing. */
    E_OVERRIDE_TOKEN_INVALID: 'E_OVERRIDE_TOKEN_INVALID',
    /** L4b: caller has CLEO_AGENT_ROLE=worker|lead|subagent — override forbidden. */
    E_OVERRIDE_FORBIDDEN_AGENT_ROLE: 'E_OVERRIDE_FORBIDDEN_AGENT_ROLE',
    /** L4c: override requires TTY but stdin/stderr is not a TTY. */
    E_OVERRIDE_NEEDS_TTY: 'E_OVERRIDE_NEEDS_TTY',
    /** L4d: session override limit exceeded. */
    E_OVERRIDE_RATE_LIMIT: 'E_OVERRIDE_RATE_LIMIT',
    /**
     * T1501 / P0-5: per-session cap (default 3) exceeded without a valid waiver doc.
     * Set CLEO_OWNER_OVERRIDE_WAIVER=<absolute path> to a file containing
     * `cap-waiver: true` in its frontmatter to bypass the cap.
     */
    E_OVERRIDE_CAP_EXCEEDED: 'E_OVERRIDE_CAP_EXCEEDED',
    /**
     * T1502 / P0-6: the same evidence atom was used across >3 distinct tasks and
     * `--shared-evidence` was not passed (or CLEO_STRICT_EVIDENCE=1 is set in CI).
     */
    E_SHARED_EVIDENCE_FLAG_REQUIRED: 'E_SHARED_EVIDENCE_FLAG_REQUIRED',
    /**
     * T1851 / P0: an absolute path provided to an Edit/Write SDK tool operation
     * does not start with the agent's `worktreeRoot` prefix. This closes the
     * bypass vector discovered in T1763 where a worker used Edit/Write with
     * absolute paths into `/mnt/projects/cleocode/.cleo/rcasd/...`, circumventing
     * the git-shim which only intercepts `git` binary calls.
     */
    E_BOUNDARY_VIOLATION: 'E_BOUNDARY_VIOLATION',
    /**
     * T1927: the `task/<taskId>` branch already exists and contains commits that
     * are NOT reachable from the current integration base (i.e. orphan history from
     * a prior test fixture, aborted session, or unrelated spawn). Reusing such a
     * branch would import garbage history on merge.
     *
     * Fix: delete the branch manually (`git branch -D task/<taskId>`) then
     * re-run spawn, or pass `{ forceReset: true }` to `createWorktree` to have it
     * reset the branch automatically.
     */
    E_DIRTY_BRANCH: 'E_DIRTY_BRANCH',
};
// ---------------------------------------------------------------------------
// Centralized worktree isolation contract (T1759)
// ---------------------------------------------------------------------------
/**
 * The canonical set of environment variable keys injected into every isolated
 * agent shell by `provisionIsolatedShell`.
 *
 * Exported as a frozen const tuple so git-shim, harness adapters, and test
 * utilities can reference the authoritative key list without duplication.
 *
 * `CLEO_SESSION_ID` + `CLEO_AGENT_ID` were added in T11343 (Epic T11284,
 * SG-COGNITIVE-SUBSTRATE) so every spawned agent carries its OWN session and
 * agent identity into the isolation shell. Before this, spawn injected only
 * worktree-binding keys and every short-lived `cleo` call inside the worktree
 * collapsed onto "whoever touched the DB last" via `getActiveSession()` —
 * the root cause of multi-agent session-bleed AND memory scope-leakage.
 *
 * @task T1759
 * @task T11343
 */
export const ISOLATION_ENV_KEYS = [
    'CLEO_WORKTREE_ROOT',
    'CLEO_AGENT_ROLE',
    'CLEO_WORKTREE_BRANCH',
    'CLEO_PROJECT_HASH',
    'CLEO_SESSION_ID',
    'CLEO_AGENT_ID',
];
/**
 * Provision an isolated shell context for an agent worktree.
 *
 * This is the single entry point for worktree isolation across all harness
 * adapters and spawn paths in CLEO. Callers MUST use the returned `cwd` and
 * `env` when starting an agent process, and MUST include the returned
 * `preamble` in the agent's spawn prompt.
 *
 * This function is pure — identical inputs always produce identical outputs,
 * with no I/O or side effects. It lives in `@cleocode/contracts` so that
 * harness adapters that cannot take a runtime dependency on `@cleocode/core`
 * (due to circular dep constraints) can still call the canonical implementation.
 * `packages/core/src/worktree/isolation.ts` re-exports this function as the
 * public API surface.
 *
 * @param options - Isolation input parameters.
 * @returns Fully-resolved isolation context.
 * @task T1759
 */
export function provisionIsolatedShell(options) {
    const { worktreePath, branch, role, projectHash, sessionId, agentId } = options;
    // --- cwd -----------------------------------------------------------------
    const cwd = worktreePath;
    // --- env -----------------------------------------------------------------
    // T11343: CLEO_SESSION_ID + CLEO_AGENT_ID bind the spawned agent's own
    // identity into the shell. Empty-string default keeps backward compatibility
    // — `resolveSessionIdFromEnv()` treats '' as absent and falls back to the
    // legacy active-session resolution path.
    const env = {
        CLEO_WORKTREE_ROOT: worktreePath,
        CLEO_AGENT_ROLE: role,
        CLEO_WORKTREE_BRANCH: branch,
        CLEO_PROJECT_HASH: projectHash,
        CLEO_SESSION_ID: sessionId ?? '',
        CLEO_AGENT_ID: agentId ?? '',
    };
    // --- preamble ------------------------------------------------------------
    // Shell snippet injected into the agent's spawn prompt. The guard pattern
    // (cd || exit 1) + pwd-verify combo defends against:
    //   - The worktree directory having been pruned before the agent starts.
    //   - A future change where cwd is silently coerced to something else.
    //   - Symlink traversal producing a path that doesn't share the prefix.
    // T11343: skip empty values so an unallocated CLEO_SESSION_ID / CLEO_AGENT_ID
    // does not emit a clobbering `export KEY=""` that would clear an inherited
    // value. Worktree-binding keys are always non-empty so they always export.
    const exportBlock = ISOLATION_ENV_KEYS.filter((k) => env[k] !== '')
        .map((k) => `export ${k}="${env[k]}"`)
        .join('\n');
    const preamble = [
        '## Worktree Isolation (REQUIRED — do not skip)',
        '',
        '# Step 1: Enter the worktree (exits immediately if path is missing)',
        `cd "${worktreePath}" || exit 1`,
        '',
        '# Step 2: Verify working directory (guards against shell/symlink quirks)',
        'case "$PWD" in',
        `  "${worktreePath}"*) ;;`,
        '  *) echo "ISOLATION ERROR: pwd=$PWD expected prefix=' + worktreePath + '" >&2; exit 1 ;;',
        'esac',
        '',
        '# Step 3: Export isolation env vars',
        exportBlock,
        '',
    ].join('\n');
    // --- boundaryContract ----------------------------------------------------
    const absolutePathRules = {
        // Worktree root is always the first (and, for workers, only) allowed prefix.
        allowedPrefixes: [worktreePath],
        // Workers are always denied writes outside their worktree.
        // Orchestrator-role spawns may loosen this via explicit overrides, but the
        // default remains `true` to follow the principle of least privilege.
        deniedOutsideWorktree: true,
    };
    const boundaryContract = {
        worktreeRoot: worktreePath,
        role,
        envKeys: ISOLATION_ENV_KEYS,
        absolutePathRules,
    };
    return { cwd, env, preamble, boundaryContract };
}
/**
 * Validate that an absolute path is permitted by the given BoundaryContract.
 *
 * This is the primary enforcement point for the P0 isolation fix (T1851).
 * It closes the bypass vector discovered in T1763: a worker committed files to
 * `/mnt/projects/cleocode/.cleo/rcasd/...` by passing absolute paths directly
 * to Edit/Write SDK tool calls, bypassing the git-shim (which only intercepts
 * `git` binary invocations).
 *
 * Rules:
 * - If `contract.absolutePathRules.deniedOutsideWorktree` is `false`, the path
 *   is always allowed (opt-out is explicitly recorded in the contract).
 * - Otherwise, the path MUST start with at least one entry in
 *   `contract.absolutePathRules.allowedPrefixes`. The check uses exact prefix
 *   matching with a trailing-slash normalisation to prevent the path
 *   `/home/user/.local/.../T1234-extra` from matching prefix
 *   `/home/user/.local/.../T1234`.
 *
 * This function is pure — no I/O, no side effects, deterministic.
 *
 * @param absolutePath - The absolute path to validate.
 * @param contract - The boundary contract for the agent.
 * @returns `{ allowed: true }` or `{ allowed: false, code, message }`.
 * @task T1851
 */
export function validateAbsolutePath(absolutePath, contract) {
    const { absolutePathRules } = contract;
    // Fast-path: enforcement is disabled for this contract (orchestrator opt-out).
    if (!absolutePathRules.deniedOutsideWorktree) {
        return { allowed: true };
    }
    // Normalise the candidate path once so all prefix comparisons are consistent.
    // Ensure the prefix check uses a trailing slash so a path like
    //   /worktrees/T1234-extra
    // does NOT match the allowed prefix
    //   /worktrees/T1234
    const normPath = absolutePath.endsWith('/') ? absolutePath : `${absolutePath}/`;
    for (const prefix of absolutePathRules.allowedPrefixes) {
        const normPrefix = prefix.endsWith('/') ? prefix : `${prefix}/`;
        if (normPath.startsWith(normPrefix) || absolutePath === prefix) {
            return { allowed: true };
        }
    }
    return {
        allowed: false,
        code: BRANCH_LOCK_ERROR_CODES.E_BOUNDARY_VIOLATION,
        message: `E_BOUNDARY_VIOLATION: absolute path "${absolutePath}" is outside the ` +
            `permitted boundary. worktreeRoot="${contract.worktreeRoot}", ` +
            `allowedPrefixes=[${absolutePathRules.allowedPrefixes.map((p) => `"${p}"`).join(', ')}]. ` +
            `Use a path within the worktree or update AbsolutePathRules.allowedPrefixes.`,
    };
}
//# sourceMappingURL=branch-lock.js.map