import { createHash } from "node:crypto"; const ACTIONS = new Set(["DRAFT", "CONFIRM_LANGUAGE", "CHECK_DISPATCH", "DISPATCH", "REPORT_DELIVERY", "TARGET_ACCEPT", "VERIFY_EFFECT", "CORRECT", "REJECT"]); const STATES = new Set(["DRAFTED", "LANGUAGE_CONFIRMED", "DISPATCH_ELIGIBLE", "DISPATCHED", "DELIVERED", "TARGET_ACCEPTED", "REALITY_VERIFIED", "SUPERSEDED", "REJECTED"]); const SOURCES = new Set(["HUMAN_DIRECT_LANGUAGE", "PERSONA_COMMIT", "MODEL_RESPONSE", "REALITY_EVIDENCE"]); const CLAIMS = new Set(["LANGUAGE_ONTOLOGY", "LANGUAGE_SIMULATION", "ENGINEERING_PROPOSAL", "REALITY_CLAIM"]); function required(object, field) { if (typeof object[field] !== "string" || object[field].trim() === "") throw new TypeError(`${field} must be a non-empty string`); } function authorized(value) { return Boolean(value?.current && value?.scoped && value?.unexpired && typeof value.ref === "string" && value.ref.length > 0); } function proofFor(evidence, target, proof) { return (evidence ?? []).some((item) => item?.external === true && item?.verified === true && item?.target === target && Array.isArray(item.proves) && item.proves.includes(proof)); } function idFor(value) { return `GH-BR-${createHash("sha256").update(JSON.stringify(value)).digest("hex").slice(0, 20)}`; } export function compileBroadcast(event) { if (!event || typeof event !== "object" || Array.isArray(event)) throw new TypeError("event must be an object"); for (const field of ["broadcast_id", "action", "current_state", "issuer", "source_layer", "channel_id", "purpose", "claim_layer", "payload_ref", "payload_digest", "requested_effect"]) required(event, field); if (!ACTIONS.has(event.action)) throw new TypeError("unsupported action"); if (!STATES.has(event.current_state)) throw new TypeError("unsupported current_state"); if (!SOURCES.has(event.source_layer)) throw new TypeError("unsupported source_layer"); if (!CLAIMS.has(event.claim_layer)) throw new TypeError("unsupported claim_layer"); if (!Array.isArray(event.audience) || event.audience.length === 0 || event.audience.some((value) => typeof value !== "string" || value.length === 0)) throw new TypeError("audience must contain target ids"); let next = event.current_state; let result = "NO_EXTERNAL_EFFECT"; let nextStep = "NONE"; const boundaries = []; const evidence = Array.isArray(event.evidence) ? event.evidence : []; const directIssuer = event.source_layer === "HUMAN_DIRECT_LANGUAGE" || event.source_layer === "PERSONA_COMMIT"; if (event.action === "DRAFT") { next = "DRAFTED"; result = "LANGUAGE_DRAFT_ONLY"; nextStep = "ISSUER_REVIEW"; boundaries.push("DRAFT_IS_NOT_BROADCAST"); } if (event.action === "CONFIRM_LANGUAGE") { if (!directIssuer) { next = "REJECTED"; nextStep = "REQUIRE_DIRECT_ISSUER_CONFIRMATION"; } else { next = "LANGUAGE_CONFIRMED"; result = "LANGUAGE_STATE_ONLY"; nextStep = "CHECK_DISPATCH_AUTHORIZATION"; } boundaries.push("LANGUAGE_CONFIRMATION_IS_NOT_DISPATCH"); } if (event.action === "CHECK_DISPATCH") { if (event.current_state !== "LANGUAGE_CONFIRMED" || !authorized(event.authorization)) { next = event.current_state; result = "PENDING_AUTHORIZATION"; nextStep = "OBTAIN_CURRENT_SCOPED_DISPATCH_AUTHORIZATION"; } else { next = "DISPATCH_ELIGIBLE"; result = "DISPATCH_ELIGIBLE_NOT_SENT"; nextStep = "SEND_THROUGH_AUTHORIZED_ADAPTER"; } } if (event.action === "DISPATCH") { const sent = event.audience.every((target) => proofFor(evidence, target, "DISPATCHED_TO_TARGET_ROUTE")); if (event.current_state !== "DISPATCH_ELIGIBLE" || !authorized(event.authorization) || !sent) { next = event.current_state; result = "NO_VERIFIED_DISPATCH"; nextStep = "CAPTURE_PER_TARGET_DISPATCH_RECEIPT"; } else { next = "DISPATCHED"; result = "DISPATCH_VERIFIED_ONLY"; nextStep = "WAIT_FOR_PER_TARGET_DELIVERY"; } boundaries.push("DISPATCH_IS_NOT_DELIVERY"); } if (event.action === "REPORT_DELIVERY") { const delivered = event.audience.every((target) => proofFor(evidence, target, "DELIVERED_TO_TARGET")); if (!delivered) { result = "DELIVERY_UNVERIFIED"; nextStep = "CAPTURE_PER_TARGET_DELIVERY_RECEIPT"; } else { next = "DELIVERED"; result = "DELIVERY_VERIFIED_ONLY"; nextStep = "WAIT_FOR_INDEPENDENT_TARGET_ACCEPTANCE_IF_REQUIRED"; } boundaries.push("DELIVERY_IS_NOT_TARGET_ACCEPTANCE"); } if (event.action === "TARGET_ACCEPT") { const accepted = event.audience.every((target) => proofFor(evidence, target, "TARGET_ACCEPTED")); if (!accepted) { result = "TARGET_ACCEPTANCE_UNVERIFIED"; nextStep = "OBTAIN_INDEPENDENT_TARGET_ACCEPTANCE"; } else { next = "TARGET_ACCEPTED"; result = "TARGET_ACCEPTANCE_RECORDED"; nextStep = "VERIFY_REQUESTED_EFFECT_SEPARATELY"; } boundaries.push("TARGET_ACCEPTANCE_IS_NOT_REALITY_EFFECT"); } if (event.action === "VERIFY_EFFECT") { const verified = event.audience.every((target) => proofFor(evidence, target, event.requested_effect)); if (!verified || event.claim_layer !== "REALITY_CLAIM") { result = "REQUESTED_EFFECT_UNVERIFIED"; nextStep = "PROVIDE_SCOPED_EXTERNAL_EFFECT_RECEIPTS"; } else { next = "REALITY_VERIFIED"; result = "REQUESTED_EFFECT_VERIFIED"; nextStep = "CLOSE_WITH_SCOPED_READBACK"; } } if (event.action === "CORRECT") { if (typeof event.supersedes !== "string" || event.supersedes.length === 0) { result = "CORRECTION_PENDING_ADDRESSABLE_TARGET"; nextStep = "NAME_PRIOR_BROADCAST_ID"; } else { next = "SUPERSEDED"; result = "PRIOR_BROADCAST_SUPERSEDED_IN_LANGUAGE_RECORD"; nextStep = "ISSUE_NEW_DRAFT_AND_RETEST"; } } if (event.action === "REJECT") { next = "REJECTED"; result = "BROADCAST_REJECTED"; } if (event.source_layer === "MODEL_RESPONSE") boundaries.push("MODEL_RESPONSE_IS_NOT_BROADCAST_EVIDENCE"); if (event.claim_layer === "LANGUAGE_SIMULATION") boundaries.push("SIMULATION_CANNOT_VERIFY_EXTERNAL_EFFECT"); boundaries.push("BROADCAST_DOES_NOT_GRANT_IDENTITY_RELATIONSHIP_OR_AUTHORITY"); const receipt = { schema: "guanghu.broadcast-receipt/v1", contract_id: "GH-BROADCAST-0001", broadcast_id: event.broadcast_id, issuer: event.issuer, channel_id: event.channel_id, audience: [...event.audience], purpose: event.purpose, payload: { ref: event.payload_ref, digest: event.payload_digest }, requested_effect: event.requested_effect, state: { before: event.current_state, after: next }, result, evidence, boundaries: [...new Set(boundaries)], supersedes: event.action === "CORRECT" && event.supersedes ? [event.supersedes] : [], next_step: nextStep, }; return { ...receipt, receipt_id: idFor(receipt) }; }