/**
 * Trace-replay v2 — `olam create --from-manifest <path>` support.
 *
 * Reads + validates a fork manifest written by `scripts/olam-trace-replay.mjs`
 * (v1, ADR 041) and projects it into the inputs `olam create` needs:
 * a `task` to dispatch and a default `name` to derive when the operator
 * omits `--name`.
 *
 * Validation is strict-enough to catch malformed JSON, missing
 * `newPrompt`, and shape-drift from the v1 schema, but tolerant of
 * forward-compatible extensions (extra fields are ignored). Errors carry
 * the path so the operator's first reflex — re-read the manifest — is
 * already wired up.
 *
 * Pure module: no fs/network side effects beyond the single read.
 * Side effects (logging, spinner, world provisioning) live in create.ts
 * where they can be unit-tested against the action surface.
 */
import { readFileSync } from 'node:fs';
/**
 * Read + parse a fork manifest. Throws a descriptive Error on:
 *   - file missing / unreadable
 *   - malformed JSON
 *   - missing/blank originalWorldId
 *   - missing/blank newPrompt
 *
 * `forkPoint` fields default to `null`/`0` when absent — the v1 manifest
 * always emits them, but tolerating their absence keeps the projection
 * forward-compatible with manifests authored by hand for testing.
 */
export function parseForkManifest(path) {
    let raw;
    try {
        raw = readFileSync(path, 'utf8');
    }
    catch (err) {
        const reason = err?.code === 'ENOENT'
            ? 'file does not exist'
            : err.message;
        throw new Error(`--from-manifest: cannot read ${path} (${reason})`);
    }
    let json;
    try {
        json = JSON.parse(raw);
    }
    catch (err) {
        throw new Error(`--from-manifest: ${path} is not valid JSON (${err.message})`);
    }
    if (!isPlainObject(json)) {
        throw new Error(`--from-manifest: ${path} root must be a JSON object`);
    }
    const originalWorldId = json.originalWorldId;
    if (typeof originalWorldId !== 'string' || originalWorldId.trim().length === 0) {
        throw new Error(`--from-manifest: ${path} missing required string "originalWorldId"`);
    }
    const newPrompt = json.newPrompt;
    if (typeof newPrompt !== 'string' || newPrompt.trim().length === 0) {
        throw new Error(`--from-manifest: ${path} has no "newPrompt" — re-run trace-replay with --with-prompt "<alternate dispatch>"`);
    }
    const fp = isPlainObject(json.forkPoint) ? json.forkPoint : {};
    const resolvedPhase = typeof fp.resolvedPhase === 'string' ? fp.resolvedPhase : null;
    const resolvedSpanId = typeof fp.resolvedSpanId === 'string' ? fp.resolvedSpanId : null;
    const resolvedAt = typeof fp.resolvedAt === 'number' ? fp.resolvedAt : 0;
    return {
        originalWorldId: originalWorldId.trim(),
        forkPoint: { resolvedPhase, resolvedSpanId, resolvedAt },
        newPrompt,
        manifestPath: path,
    };
}
/**
 * Derive a default world name from the manifest's originalWorldId.
 * Pattern: `fork-<originalWorldId>` truncated at 40 chars to match the
 * existing slug bound used by `defaultNameFromPrompt` in create.ts.
 */
export function defaultNameFromManifest(manifest) {
    const base = `fork-${manifest.originalWorldId}`
        .toLowerCase()
        .replace(/[^a-z0-9-]/g, '-')
        .replace(/-+/g, '-')
        .replace(/^-|-$/g, '');
    return (base || 'fork').slice(0, 40);
}
/**
 * Human-readable provenance line printed at `olam create --from-manifest`
 * time so the operator can confirm the fork they intended.
 */
export function provenanceLine(manifest) {
    const phase = manifest.forkPoint.resolvedPhase ?? 'end-of-trace';
    return `Forking from ${manifest.originalWorldId} @ phase=${phase} (manifest: ${manifest.manifestPath})`;
}
function isPlainObject(v) {
    return typeof v === 'object' && v !== null && !Array.isArray(v);
}
//# sourceMappingURL=from-manifest.js.map