/* RFC-1317: onboardingDecisionLedgerSchema — the Zod mirror of docs/specs/werkstatt-onboarding/schemas/onboarding-decision.schema.yaml. The onboarding decision ledger is the single decision window of one onboarding (D14): append-only OD-NN entries over a closed topic set, written by onboarding.decision.record into missions/[missionId]/onboarding.decisions.yaml and checked by onboarding.decision.validate (ONDEC-01…07). Do not encode ONDEC discipline rules here — schema mirrors the vendored contract only; topic/status coupling is enforced by the validator. Do not add fields beyond the spec schema — strict objects mirror additionalProperties:false (DNA-19); divergence is a spec amendment, never a silent edit. Field-for-field mirror of onboarding-decision.schema.yaml — the spec file is normative. capabilityGap reuses capabilityGapSchema (DNA-103 single-sourcing) — the vendored schema $refs it identically. recommendation is a required first-class field; options carry label+consequence only. RFC-1317: created — ONBOARDING_DECISION_SCHEMA_ID, topic/resolutionPath/status tuples, entry + ledger schemas, inferred types. RFC-1317: step 1 — onboarding-decision Zod contract + parity test RFC-1317: step 5: docs sync + Compass header repair — AGENTS.md onboarding composer contract, technology.xml/knowledge-graph.xml nodes, app-onboarding-guide commands, regenerated command/ecosystem manifests, /MODULE_CONTRACT closers + KD trims on RFC-1317 files */ import { z } from "zod"; import { capabilityGapSchema } from "./capability-gap.ts"; export const ONBOARDING_DECISION_SCHEMA_ID = "werkstatt:onboarding-decisions/v1"; /** Closed topic set — everything outside it is logged, never asked (D14). */ export const ONBOARDING_DECISION_TOPICS = [ "capability-gap", "family-confirmation", "biome-confirmation", "page-set-deviation", "contested-claim-publication", "asset-rights-risk", "locale-launch", "hard-stop", "auto-resolved-log", ] as const; /** The eight operator-facing topics — `auto-resolved-log` is the ninth, log-only. */ export const ONBOARDING_OPERATOR_TOPICS = ONBOARDING_DECISION_TOPICS.filter( (t) => t !== "auto-resolved-log", ); export const ONBOARDING_RESOLUTION_PATHS = [ "codebase", "convention", "profile", "policy", "evidence", "none", ] as const; export const ONBOARDING_DECISION_STATUSES = [ "open", "answered", "auto-resolved", "deferred", ] as const; const odIdSchema = z.string().regex(/^OD-[0-9]{2,}$/); const onboardingDecisionOptionSchema = z .object({ label: z.string(), consequence: z.string(), }) .strict(); export const onboardingDecisionEntrySchema = z .object({ id: odIdSchema, /** Closed set of what reaches the operator (D14). */ topic: z.enum(ONBOARDING_DECISION_TOPICS), question: z.string(), options: z.array(onboardingDecisionOptionSchema).optional(), /** The composer's recommended answer, first-class (RFC-1250 style). */ recommendation: z.string(), resolutionPath: z.enum(ONBOARDING_RESOLUTION_PATHS), /** Intake ids, gap ids, capability-gap ids, PBP entity ids. */ refs: z.array(z.string()).optional(), /** Inline capability-gap record when topic is capability-gap. */ capabilityGap: capabilityGapSchema.optional(), status: z.enum(ONBOARDING_DECISION_STATUSES), answer: z.string().optional(), answeredAt: z.iso.datetime().optional(), }) .strict(); export const onboardingDecisionLedgerSchema = z .object({ schema: z.literal(ONBOARDING_DECISION_SCHEMA_ID), systemId: z.string(), missionId: z.string(), intakeId: z.string(), createdAt: z.iso.datetime(), items: z.array(onboardingDecisionEntrySchema), }) .strict(); export type OnboardingDecisionTopic = (typeof ONBOARDING_DECISION_TOPICS)[number]; export type OnboardingResolutionPath = (typeof ONBOARDING_RESOLUTION_PATHS)[number]; export type OnboardingDecisionStatus = (typeof ONBOARDING_DECISION_STATUSES)[number]; export type OnboardingDecisionEntry = z.infer; export type OnboardingDecisionLedger = z.infer;