/** * A/B regenerate-and-compare engine for JSON file restore. * * Classifies each field of the three managed CLEO JSON config files into one * of five categories (identical, machine-local, user-intent, project-identity, * auto-detect, unknown) and resolves conflicts deterministically per ADR-038 §10 * and T311 spec §6. * * PURE MODULE — no I/O, no global state, no disk writes. All output is returned * as a {@link JsonRestoreReport} for the caller (T357) to format and persist. * * @task T354 * @epic T311 * @why ADR-038 §10 — intelligent A/B regenerate-and-compare for restore. * Classifies each field of imported JSON files into 4 categories * (machine-local, user-intent, project-identity, auto-detect, unknown) * and resolves conflicts deterministically. * @what Pure classification engine. Reads regenerated A from regenerators.ts, * imported B from caller, returns a JsonRestoreReport for T357 to format. * @see packages/contracts/src/backup-manifest.ts * @see packages/core/src/store/regenerators.ts * @module restore-json-merge */ /** * The three JSON filenames managed by the restore engine. * * Each filename maps to a distinct classification ruleset (spec §6.3). * * @task T354 * @epic T311 */ export type FilenameForRestore = 'config.json' | 'project-info.json' | 'project-context.json'; /** * Five-way field category taxonomy per T311 spec §6.3 / ADR-038 §10. * * - `identical` — A and B are byte-equal (JSON.stringify match); no conflict. * - `machine-local` — field is expected to differ between machines; keep A. * - `user-intent` — field represents user-applied config in config.json; keep B. * - `project-identity` — field identifies the project in project-info.json; keep B. * - `auto-detect` — field is auto-detected from the local environment; keep A. * - `unknown` — no classification rule matched; flag for manual review. * * @task T354 * @epic T311 */ export type FieldCategory = 'identical' | 'machine-local' | 'user-intent' | 'project-identity' | 'auto-detect' | 'unknown'; /** * Resolution applied to a field when building the merged output. * * - `A` — use the locally regenerated value. * - `B` — use the imported value. * - `manual-review` — no safe auto-resolution; local value (A) is kept as a * safe default pending operator review. * * @task T354 * @epic T311 */ export type Resolution = 'A' | 'B' | 'manual-review'; /** * Classification and resolution for a single leaf field in the comparison. * * Produced by {@link regenerateAndCompare} for every leaf path encountered * during the recursive walk of both objects. * * @task T354 * @epic T311 */ export interface FieldClassification { /** JSON dot-path of the field, e.g. "brain.embeddingProvider" or "testing.framework". */ path: string; /** Value from locally regenerated content (A). `undefined` when absent in A. */ local: unknown; /** Value from the imported bundle (B). `undefined` when absent in B. */ imported: unknown; /** * Taxonomy category per spec §6.3 classification rules. * `identical` means `JSON.stringify(A) === JSON.stringify(B)` — no conflict. */ category: FieldCategory; /** * The resolution applied (or to be applied) to disk. * `A` = use local value; `B` = use imported value; * `manual-review` = operator must decide. */ resolution: Resolution; /** Human-readable explanation for the resolution. */ rationale: string; } /** * Complete A/B comparison result for one JSON file. * * Returned by {@link regenerateAndCompare}. Caller persists `applied` to disk * and writes the full report to `.cleo/restore-conflicts.md`. * * @task T354 * @epic T311 */ export interface JsonRestoreReport { /** Which file was compared. */ filename: FilenameForRestore; /** The locally regenerated object (A). */ localGenerated: unknown; /** The imported object (B). */ imported: unknown; /** * Per-field classification results. * All leaf fields — including identical ones — are listed here. */ classifications: FieldClassification[]; /** The final merged object produced by applying all resolutions. */ applied: unknown; /** * Count of fields with `resolution === 'manual-review'`. * Equals the number of `unknown`-category classifications. */ conflictCount: number; } /** * Input to {@link regenerateAndCompare}. * * The caller supplies the imported content (B) and the locally regenerated * content (A). The engine classifies and resolves each field. * * @task T354 * @epic T311 */ export interface RegenerateAndCompareInput { /** Which of the three managed JSON files is being compared. */ filename: FilenameForRestore; /** Parsed content of the imported file (B — from bundle json/ directory). */ imported: unknown; /** * Locally regenerated content (A). * * When provided, the engine uses this value directly as the A side. * When omitted (or when `filename` is provided with a `projectRoot`), * the engine calls the appropriate generator from regenerators.ts. * * Providing this field allows callers to pass a pre-computed A — useful * in tests and in the CLI import command to avoid redundant regeneration. */ localGenerated: unknown; } /** * Runs the A/B regenerate-and-compare classification engine for a single * JSON file. * * Does NOT write anything to disk. Returns a {@link JsonRestoreReport} * containing the per-field classifications, the merged `applied` result, and * a count of fields that require manual review. * * The `localGenerated` field on the input is used directly as the A side. * Pass the result of the appropriate generator from `regenerators.ts` (or any * equivalent object for testing). * * Classification precedence (spec §6.3): * identical > machine-local > user-intent > project-identity > auto-detect > unknown * * @task T354 * @epic T311 * @param input - Filename, imported content (B), and locally regenerated content (A). * @returns A complete `JsonRestoreReport` for the caller to persist. */ export declare function regenerateAndCompare(input: RegenerateAndCompareInput): JsonRestoreReport; /** * Convenience wrapper that runs `regenerateAndCompare` for all three JSON * files in a single call, generating the A sides from `projectRoot`. * * Useful in the CLI import command when all three files are present in the * bundle. Each generator runs independently. * * Does NOT write anything to disk. * * @task T354 * @epic T311 * @param projectRoot - Absolute path to the project root (for A generation). * @param importedConfig - Imported config.json content (B). * @param importedInfo - Imported project-info.json content (B). * @param importedContext - Imported project-context.json content (B). * @returns Object containing one `JsonRestoreReport` per managed JSON file. */ export declare function regenerateAndCompareAll(projectRoot: string, importedConfig: unknown, importedInfo: unknown, importedContext: unknown): { config: JsonRestoreReport; projectInfo: JsonRestoreReport; projectContext: JsonRestoreReport; }; //# sourceMappingURL=restore-json-merge.d.ts.map