/** Stable shape written to disk; bump schemaVersion if breaking changes are made. */
export interface BuildOutputRecord {
    schemaVersion: 1;
    jobId: string;
    appId: string;
    platform: 'ios' | 'android';
    buildMode: 'debug' | 'release';
    status: string;
    outputUrl: string | null;
    qrCodeAscii: string | null;
    qrCodePngPath: string | null;
    finishedAt: string;
}
export interface WriteBuildOutputRecordInput {
    jobId: string;
    appId: string;
    platform: 'ios' | 'android';
    buildMode: 'debug' | 'release';
    status: string;
    outputUrl: string | null;
}
/**
 * Write a build-output record to `recordPath` (JSON) and, when a URL is available,
 * a PNG QR code to `<recordPath>.qr.png`. Returns the parsed record exactly as
 * written so callers can log a summary without re-reading the file.
 *
 * Failures rendering the PNG are non-fatal — the JSON is always written. The
 * record's `qrCodePngPath` field is null when the PNG could not be produced.
 *
 * Hardening (hostile-review 2026-06-12): the record and PNG paths are unlinked
 * before writing so a pre-planted symlink is replaced instead of followed; the
 * record is written with mode 0600 (outputUrl is a signed download URL); a
 * created parent directory gets mode 0700; and when the record lives under the
 * shared tmpdir, a parent directory that is a symlink or owned by another user
 * is refused.
 */
export declare function writeBuildOutputRecord(recordPath: string, input: WriteBuildOutputRecordInput, onWarn?: (msg: string) => void): Promise<BuildOutputRecord>;
/**
 * Returns a deterministic temp-file path for the build output record for a
 * given (appId, platform) pair. Both the build hand-off (command emit) and
 * the confirm (record read) call this helper so that the path is never passed
 * back and forth across an MCP boundary.
 *
 * appId is sanitized: all `/` and `\` characters are replaced with `_`, and
 * any `..` sequences are replaced with `_`, to prevent path traversal.
 *
 * The record lives in a per-user `capgo-build-records-<uid>` subdirectory
 * (created with mode 0700 by `writeBuildOutputRecord`) so that on shared-tmp
 * systems another user can neither pre-create nor read the record file
 * (hostile-review 2026-06-12).
 */
export declare function defaultBuildRecordPath(appId: string, platform: 'ios' | 'android'): string;
/**
 * Remove a build output record and its companion QR PNG. Absent paths are a
 * no-op. Called before a new build hand-off so a record left behind by an
 * earlier build can never be read as the new build's result.
 */
export declare function removeBuildOutputRecord(recordPath: string): Promise<void>;
/**
 * Thrown by `readBuildOutputRecord` when the record file EXISTS but cannot be
 * used: unreadable (permissions), a symbolic link, not valid JSON (e.g. a
 * truncated write), or an unexpected shape. Distinct from the `null` return
 * (no record yet) so callers polling for a build result can surface the
 * failure instead of waiting forever.
 */
export declare class BuildRecordReadError extends Error {
    readonly recordPath: string;
    constructor(message: string, recordPath: string);
}
/**
 * Read a build output record from `path`.
 *
 * Returns `null` ONLY when the file does not exist yet (ENOENT — the build has
 * not finished). Every other failure mode (unreadable file, a symlink at the
 * record path, malformed JSON, missing/wrong-type fields) throws
 * `BuildRecordReadError`: a present-but-corrupt record is a surfaced failure,
 * never "still waiting".
 *
 * Shape rules (hostile-review 2026-06-12):
 *  - every record owes the `jobId`/`status`/`outputUrl` trio (forward-tolerant
 *    baseline for future schemaVersions);
 *  - a `schemaVersion: 1` record is validated strictly against the full
 *    `BuildOutputRecord` shape — the v1 writer has always emitted it, and
 *    checkBuild correlates `appId`/`platform` against the build it launched.
 */
export declare function readBuildOutputRecord(path: string): Promise<BuildOutputRecord | null>;
