/*
Maintains packages/surface/src/visibility.ts as an authored surface authored module so agents can evolve it without rediscovering local boundaries.
- Do not model per-user analytics rows or external API clients.
- RFC-0282: initial implementation of visibility feedback-loop contracts.
*/
import { z } from "zod";
export const visibilitySourceSchema = z.enum(["gsc", "manual"]);
export const clusterActionSchema = z.enum(["expand", "hold", "prune", "enrich", "escalate"]);
export const visibilitySnapshotSchema = z.object({
clusterId: z.string().min(1),
windowStart: z.string().datetime(),
windowEnd: z.string().datetime(),
indexedPages: z.number().int().nonnegative(),
eligiblePages: z.number().int().nonnegative(),
impressions: z.number().int().nonnegative(),
clicks: z.number().int().nonnegative(),
uniqueQueries: z.number().int().nonnegative(),
avgPosition: z.number().positive().optional(),
source: visibilitySourceSchema,
importedAt: z.string().datetime(),
cannibalizingQueries: z.array(z.string().min(1)).optional(),
coreClicksBaseline: z.number().int().nonnegative().optional(),
coreClicksCurrent: z.number().int().nonnegative().optional(),
});
export const clusterOutcomeSchema = z.object({
clusterId: z.string().min(1),
surfaceId: z.string().min(1).optional(),
depth: z.number().int().nonnegative().optional(),
eligiblePages: z.number().int().nonnegative(),
indexedPages: z.number().int().nonnegative(),
impressions: z.number().int().nonnegative(),
clicks: z.number().int().nonnegative(),
uniqueQueries: z.number().int().nonnegative(),
indexationRate: z.number().min(0),
medianImpressionsPerPage: z.number().min(0),
queryDiversityShare: z.number().min(0),
positiveDemand: z.boolean(),
anomalies: z.array(z.string().min(1)),
proposedAction: clusterActionSchema,
rationale: z.string().min(1),
});
export type VisibilitySource = z.infer;
export type ClusterAction = z.infer;
export type VisibilitySnapshot = z.infer;
export type ClusterOutcome = z.infer;