/* Defines RFC-0073 shared content-discipline contracts for atom coverage, voice profile, and unplaced-atom reasoning. Do not read files from disk. Do not perform app-specific validation logic. RFC-0073: Introduce shared content-discipline contracts. */ import { z } from "zod"; export const contentAtomIntentValues = [ "heading", "subheading", "body", "lead", "cta-primary", "cta-secondary", "trust", "proof", "stat-value", "stat-label", "faq-question", "faq-answer", "meta-title", "meta-description", "og-title", "og-description", "legal-prose", "process-step", "comparison-row", "microcopy", "error-message", "confirmation", "placeholder", "consent", ] as const; export const unplacedReasonValues = [ "legal-deferred", "redundant", "out-of-scope-for-mvp", "client-deprecated", "quality-concern", ] as const; export const contentAtomIntentSchema = z.enum(contentAtomIntentValues); export const unplacedReasonSchema = z.enum(unplacedReasonValues); export const contentAtomSchema = z .object({ id: z.string().regex(/^atom-\d{4,}$/), sourceId: z.string().min(1), sourceLines: z.tuple([z.number().int().positive(), z.number().int().positive()]), intent: contentAtomIntentSchema, pageHint: z.string().min(1), sectionHint: z.string().min(1), faqQuestion: z.string().min(1).optional(), paraphraseOf: z .string() .regex(/^atom-\d{4,}$/) .optional(), text: z.string().min(1), }) .strict(); export const contentAtomsFileSchema = z .object({ // RFC-0082: payload is read via parseOnboardingArtifactPayload, which strips // RFC-0076 metadata keys before validation, so .strict() is safe again. client: z.string().min(1), language: z.string().regex(/^[a-z]{2}$/), materialsHash: z.string().min(1), generatedAt: z.string().min(1), atoms: z.array(contentAtomSchema), }) .strict(); export const preferredPhrasingSchema = z .object({ avoid: z.string().min(1), prefer: z.string().min(1), }) .strict(); export const voiceProfileSchema = z .object({ // RFC-0082: payload is read via parseOnboardingArtifactPayload, which strips // RFC-0076 metadata keys before validation, so .strict() is safe again. client: z.string().min(1), language: z.string().regex(/^[a-z]{2}$/), register: z.enum(["Sie", "Du", "you", "informal"]), forbiddenPhrases: z.array(z.string().min(1)).default([]), preferredPhrasings: z.array(preferredPhrasingSchema).default([]), mandatoryPhrases: z.array(z.string().min(1)).default([]), allowedQuotes: z.array(z.string().min(1)).default([]), }) .strict(); export const coverageLedgerEntrySchema = z .object({ atomId: z.string().regex(/^atom-\d{4,}$/), reason: unplacedReasonSchema, note: z.string().min(1), }) .strict(); export type ContentAtomIntent = z.infer; export type ContentAtom = z.infer; export type ContentAtomsFile = z.infer; export type UnplacedReason = z.infer; export type PreferredPhrasing = z.infer; export type VoiceProfile = z.infer; export type CoverageLedgerEntry = z.infer;