#!/usr/bin/env node
/**
 * seal-orchestration (C3-C6, §5) — собрать sealable-объект, сверить judged_hash (SD-1),
 * поставить печать ТОЛЬКО на реальном GO (SE-3), со штампом artifact_id (SD-3) и judge_mode.
 * Печать-движок инъектируется (в проде freezeArtifact/freezePlan Машины 2).
 *
 * NB (фикс при сборке, systematic-debugging): judge_mode НЕ входит в судимое представление —
 * judged_hash считается над ЧИСТЫМ sealable-объектом (sealableArtifact/sealablePlan). Поэтому
 * для артефакта judge_mode добавляется в ПЕЧАТЬ через обёртку freezeImpl ПОСЛЕ хеш-сверки
 * sealOnApproval (иначе её внутренний contentHash({...artifact,judge_mode}) разошёлся бы с
 * judged_hash чистого объекта → TOCTOU-false → артефакт никогда не печатался бы).
 */
import { buildArtifact } from './artifact-from-spec.mjs';
import { parsePlanSteps } from './plan-steps-parse.mjs';
import { parsePlanSkills } from './plan-skills.mjs';
import { contentHash, sealOnApproval } from './judge-seal-channel.mjs';
import { freezeArtifact, freezePlan } from './plan-lock.mjs';

export function sealableArtifact(md) { return buildArtifact(md); }              // {sections, source_sha}
export function sealablePlan(md) { return { steps: parsePlanSteps(md), skills: parsePlanSkills(md) }; } // {steps,skills}
export function judgedHashOf(obj) { return contentHash(obj); }

function isRealGo(v) { return !!(v && v.wired === true && v.decision === 'GO'); }

/** Печать артефакта на реальном GO (carve-out деньги/тупик — через sealOnApproval). */
export function sealArtifact({ md, verdict, key, judgeMode, nowMs, freezeImpl = freezeArtifact }) {
  if (!isRealGo(verdict)) return { sealed: false, reason: 'нет реального GO (SE-3)' };
  const artifact = sealableArtifact(md);
  if (verdict.judged_hash !== judgedHashOf(artifact)) return { sealed: false, reason: 'judged_hash mismatch (SD-1/TOCTOU)' };
  // judge_mode впрыскивается в печать ПОСЛЕ хеш-сверки sealOnApproval (над чистым artifact).
  const freezeWithMode = ({ artifact: a, key: k, nowMs: n }) =>
    freezeImpl({ artifact: { ...a, judge_mode: judgeMode }, key: k, nowMs: n });
  return sealOnApproval({ artifact, verdict, key, nowMs, freezeImpl: freezeWithMode });
}

/** Печать плана: штамп artifact_id текущего артефакта (SD-3); нет артефакта → fail-CLOSE (VA-1). */
export function sealPlan({ md, currentArtifact, verdict, key, judgeMode, nowMs, freezeImpl = freezePlan }) {
  if (!isRealGo(verdict)) return { sealed: false, reason: 'нет реального GO (SE-3)' };
  if (!currentArtifact || !currentArtifact.artifact_id) return { sealed: false, reason: 'нет текущего артефакта (VA-1/SD-3)' };
  let planObj;
  try { planObj = sealablePlan(md); } catch (e) { return { sealed: false, reason: `парс плана fail-CLOSE: ${e.message}` }; }
  if (verdict.judged_hash !== judgedHashOf(planObj)) return { sealed: false, reason: 'judged_hash mismatch (SD-1/TOCTOU)' };
  const seal = freezeImpl({ steps: planObj.steps, skills: planObj.skills, artifactId: currentArtifact.artifact_id, judgeMode, key, nowMs });
  return { sealed: true, seal };
}
