/* RFC-0508: canonical Participant schema — a unified entity model for humans, AI agents, organization units, external specialists, partner organizations, and service accounts. Extends the People collection (RFC-0200) with type-specific responsibility/authority/evidence fields, lifecycle statuses, consent records for humans, and public/private visibility. Do not define route generation or page rendering logic — that lives in @warpgogol/werkstatt-site/share/astro. Do not define validation rules — that lives in @warpgogol/werkstatt-site/checks/src/participant.ts. Do not remove PERSON_AFFILIATIONS from person.ts — it is retained for human governance JSON-LD. RFC-0508: initial creation — Participant schema with six types, statuses, relationships, consent, and type-specific fields. RFC-1257: structured CtaTarget + scenario-plumbed contact links */ import { z } from "zod"; import { PERSON_AFFILIATIONS } from "./person.ts"; import { ctaTargetSchema } from "./section-cta.ts"; export const PARTICIPANT_TYPES = [ "human", "ai-agent", "organization-unit", "external-specialist", "partner-organization", "service-account", ] as const; export type ParticipantType = (typeof PARTICIPANT_TYPES)[number]; export const PARTICIPANT_RELATIONSHIPS = [ "founder", "board", "team", "patron", "author", "advisor", "contractor", "partner", "service", ] as const; export type ParticipantRelationship = (typeof PARTICIPANT_RELATIONSHIPS)[number]; export const PARTICIPANT_STATUSES = [ "active", "temporarily-unavailable", "on-leave", "former", "retired", "suspended", "draft", ] as const; export type ParticipantStatus = (typeof PARTICIPANT_STATUSES)[number]; export const CONSENT_APPROVED_FIELDS = [ "lifespan.born", "lifespan.died", "location", "bio", "photo", "sameAs", ] as const; export type ConsentApprovedField = (typeof CONSENT_APPROVED_FIELDS)[number]; const consentSchema = z .object({ consentRecordId: z.string().min(1), approvedFields: z.array(z.enum(CONSENT_APPROVED_FIELDS)).default([]), approvedMedia: z.array(z.string()).default([]), consentDate: z.string().min(1), withdrawalRoute: z.string().min(1).optional(), profileReviewer: z.string().min(1), }) .strict(); const responsibilitySchema = z .object({ summary: z.string(), scope: z.string().optional(), pbpReferences: z.array(z.string()).default([]), }) .strict(); const authoritySchema = z .object({ canSignFor: z.array(z.string()).default([]), canCommitTo: z.array(z.string()).default([]), escalationRoute: z.string().optional(), }) .strict(); const evidenceSchema = z .object({ claims: z .array( z .object({ claimId: z.string(), sourceRef: z.string(), verifiedAt: z.string(), }) .strict(), ) .default([]), disclosures: z .array( z .object({ type: z.string(), text: z.string(), url: z.string().optional(), }) .strict(), ) .default([]), }) .strict(); const contactSchema = z .object({ primaryRoute: z.string().optional(), pgpFingerprint: z.string().optional(), responseTime: z.string().optional(), }) .strict(); const lifespanSchema = z .object({ born: z.union([z.string(), z.number()]).optional(), died: z.union([z.string(), z.number()]).optional(), }) .strict(); const aiAgentFieldsSchema = z .object({ modelId: z.string(), provider: z.string(), technicalStand: z .object({ agentId: z.string(), toolsetVersion: z.string(), lastEvaluatedAt: z.string(), capabilities: z.array(z.string()).default([]), }) .strict(), rightsMatrix: z .object({ canPublish: z.boolean().default(false), canEditContent: z.boolean().default(false), canManageUsers: z.boolean().default(false), canAccessFinancials: z.boolean().default(false), }) .strict(), accountableHumanId: z.string().min(1), }) .strict(); const organizationUnitFieldsSchema = z .object({ unitName: z.string(), parentUnit: z.string().optional(), leadParticipantId: z.string().optional(), }) .strict(); const externalSpecialistFieldsSchema = z .object({ specialty: z.string(), organization: z.string().optional(), engagementType: z.string().optional(), contractRef: z.string().optional(), }) .strict(); const partnerOrganizationFieldsSchema = z .object({ organizationName: z.string(), partnershipType: z.string().optional(), contactRoute: z.string().optional(), }) .strict(); const serviceAccountFieldsSchema = z .object({ serviceName: z.string(), provider: z.string(), scopeDescription: z.string(), credentialsRef: z.string(), }) .strict(); export const participantSchema = z .object({ participantId: z.string().min(1), participantType: z.enum(PARTICIPANT_TYPES), publicName: z.string().min(1), slug: z.string().min(1), status: z.enum(PARTICIPANT_STATUSES), relationshipType: z.enum(PARTICIPANT_RELATIONSHIPS), responsibility: responsibilitySchema.optional(), authority: authoritySchema.optional(), evidence: evidenceSchema.optional(), contact: contactSchema.optional(), visibility: z.enum(["public", "private"]).default("private"), profileOwner: z.string().optional(), retentionClass: z.enum(["permanent", "standard", "short-term"]).default("standard"), name: z.string().optional(), role: z.string().optional(), photo: z.string().optional(), bio: z.string().optional(), affiliations: z.array(z.enum(PERSON_AFFILIATIONS)).default([]), order: z.number().int().nonnegative().optional(), lifespan: lifespanSchema.optional(), statement: z.string().optional(), stats: z.array(z.object({ label: z.string(), value: z.string() }).strict()).optional(), // RFC-1257: participant CTA target is the canonical CtaTarget union — // internal targets may carry a bounded query map (e.g. ?scenario=). cta: z.object({ label: z.string(), target: ctaTargetSchema }).strict().optional(), sameAs: z.array(z.string().url()).optional(), page: z .object({ enabled: z.boolean().default(false) }) .strict() .optional(), location: z.string().optional(), live: z .object({ src: z.string(), poster: z.string().optional(), alt: z.string().optional(), }) .strict() .optional(), consent: consentSchema.optional(), aiAgent: aiAgentFieldsSchema.optional(), organizationUnit: organizationUnitFieldsSchema.optional(), externalSpecialist: externalSpecialistFieldsSchema.optional(), partnerOrganization: partnerOrganizationFieldsSchema.optional(), serviceAccount: serviceAccountFieldsSchema.optional(), }) .strict(); export type ParticipantData = z.infer;