/* Top-level system manifest schema (RFC-0025): binds a client application to a cosmic identity, a single Biome, and optional Constellations. Do not introduce app-specific runtime composition or deployment behavior into this reusable package source file. RFC-1283: step 1 — publication.gate in systemManifestSchema Per-site PBP publication gate mode (warn | enforce, absent = warn) consumed by PUBGATE-01 in pbp.content.validate and PUBGATE-02 in dist.content-references.validate. RFC-1293: require timing semantics on time-bound policy fields via contractTerms.gate (RFC-1293) policySchema gains an optional timing map (pbpTimingSchema: startEvent, clock, timezone, businessHours, boundary) inherited by every specialized policy. pbp.content.validate sweeps each pbp/* entity's frontmatter — TIMED_FIELDS registry paths plus every ISO-8601 duration literal — and emits PBP-TIMING-01..04 at contractTerms.gate severity (warn|enforce in system.md; absent = warn). enforce also makes pbp.terms-reference.validate behave as --strict. New sites register with contractTerms.gate: enforce (sternsystem-register stub + both onboarding system templates). Live warpgogol run: 129 PBP-TIMING-01 warnings across policies/offerings/documents/claims — the owner's contract-decision agenda; no timing values authored, no site gate flipped. Known registry gap: renewal.renewalPeriod/reminder are not ISO-shaped and escape the sweep (documented in the RFC evidence). Canary C10 active. RFC-1294: require verbatim source provenance for external local facts (RFC-1294) localFacts[] entries gain sourceUrl/excerpt/excerptLang/scope/reviewedBy/ reviewedAt. surface.evidence.validate sweeps every blueprint dataset record's localFacts once per source file and emits LOCALFACT-01 (missing sourceUrl/excerpt), LOCALFACT-02 (off-host sourceUrl — dot-boundary subdomains of the descriptor endpoint host pass), LOCALFACT-03 (normalized number absent from the verbatim excerpt), LOCALFACT-04 (stale asOf+reviewEvery, always a warning). systemManifestSchema gains provenance.gate (warn|enforce, absent = warn; new sites register enforce). The bake renders 'Geprüfte lokale Fakten' only when every rendered fact carries a human review record — otherwise the neutral 'Lokaler Kontext' — plus a Quellen block listing each distinct sourceUrl with its asOf date. Live warpgogol run: 40 LOCALFACT-01 warnings across the 12 city demand records — the owner's research backlog; no excerpts, review records, or gate flips authored. Canary C11 active. RFC-1307: step 6 — onboarding docs switched to intake path, manifest comment re-sourced, generated artifacts refreshed RFC-1307: canonical org-singleton PBP paths, seeded hreflang+publication.gate, full rollback ADR-0062, RFC-0303, RFC-0377, RFC-1106, RFC-1161, RFC-1187, RFC-1209, RFC-1229 */ import { z } from "zod"; import { systemSharedContextSchema, systemGrowthSchema } from "./growth.ts"; import { systemReleaseSchema } from "./release.ts"; import { systemTextSchema, systemTypographySchema } from "./text.ts"; import { systemIntegrationsSchema } from "./integrations.ts"; import { systemVerificationSchema } from "./verification.ts"; import { systemShellContractSchema } from "./shell-contract.ts"; import { camAuthoritySchema } from "./authority.ts"; import { resourceBudgetConfigSchema } from "../../../policy/resource-budget.ts"; import { semanticPageTypeSchema, articleMetadataSchema, pageOutputSchema } from "./page-output.ts"; /** * The per-app system.yaml manifest. Binds the client app to its cosmic identity. * One file per app — stored at apps//system.yaml. * * Constraints enforced here (structural): * - identity.biome is a scalar (single biome per app — multi-biome is permanently forbidden) * - constellations references are kebab-case slugs matching files in packages/werkstatt-site/src/domain/ontology/constellations/ * * Cross-file constraints (enforced by validators, not this schema): * - identity.systemStar must match a StarCatalog entry used in one page manifest * - identity.biome must match an existing biome YAML id * - constellations[] entries must resolve to existing constellation YAMLs */ /** * RFC-0052 crawl policy block (`robots:`) consumed by robots.generate and * robots.validate. RFC-0784 adds contentSignal; RFC-1161 derives the named * AI-training bot group from it (ai-train=no → Disallow group for * AI_TRAINING_CRAWLER_REGISTRY train tokens). */ export const systemRobotsSchema = z.object({ defaultPolicy: z.enum(["allow", "disallow"]).optional(), disallowedPaths: z.array(z.string()).optional(), allowedPaths: z.array(z.string()).optional(), crawlerBlocklist: z.array(z.string()).optional(), crawlerAllowlist: z.array(z.string()).optional(), sitemap: z.string().optional(), customRules: z .array( z.object({ userAgent: z.string().min(1), disallow: z.array(z.string()).optional().default([]), allow: z.array(z.string()).optional().default([]), }), ) .optional(), /** RFC-0784 Content-Signal tokens (e.g. "ai-train=no", "search=yes"). */ contentSignal: z.array(z.string()).optional(), }); export const systemManifestSchema = z.object({ /** * Stable kebab-case slug for this app — matches the apps// directory name. * Example: "my-app" */ app: z .string() .regex(/^[a-z][a-z0-9-]*$/, "app must be kebab-case (lowercase letters, digits, hyphens)"), /** * Semantic version of this system manifest. */ version: z.string().regex(/^\d+\.\d+\.\d+(?:-[a-z0-9.-]+)?$/i, "version must be valid semver"), /** * Cosmic identity block — assigns this app its place in the cosmic overlay. */ identity: z.object({ /** * The IAU star name that identifies the "home star" of this system. * Must match a StarCatalog entry that is also used as the cosmicName * of at least one page manifest in this app. * Validated cross-file by system.manifest.validate. */ systemStar: z.string().min(1), /** * The single Biome applied to this app. * Must match a biome id from packages/werkstatt-site/src/domain/ontology/biomes/. * Multi-biome per app is permanently forbidden (DNA-23, RFC-0025). * Validated cross-file by biome.contract.validate. */ biome: z.string().min(1), /** * Human-readable tagline for this system, shown in the Cosmic Passport. * Example: "Building a better future for local communities" */ tagline: z.string().min(1).optional(), /** * Canonical FQDN for this app (without protocol or path). * Seeded by sternsystem.register --intake from the intake's * classification.domain when it is confirmed; left unset while the domain * is an open onboarding gap (RFC-1307). */ domain: z.string().min(1).optional(), /** * ADR-0062: App-level default CTA target — the pageId of the contact/CTA page. * Per-page `ctaTarget` in pages[] overrides this app-level default. * Used by resolve-route.ts to populate `ctaTarget` in PageRouteData. */ ctaTarget: z.string().min(1).optional(), /** * RFC-0096: Operator details consumed by legal.scaffold to fill * Impressum / Datenschutz stubs. */ legal: z .object({ responsibleName: z.string().min(1).optional(), address: z.string().min(1).optional(), email: z.string().min(1).optional(), }) .optional(), }), /** * Internationalization configuration (RFC-0038). * Declares default language and supported language map. */ i18n: z .object({ default: z.string().min(1), supported: z.record(z.string(), z.any()), }) .optional(), /** * RFC-1283: PBP publication gate mode; absent = "warn". * `enforce` turns references into non-published business-profile entities * into PUBGATE-01 violations (pbp.content.validate) and makes distinctive * draft-entity values in built output fail PUBGATE-02 * (dist.content-references.validate); `warn` logs the same findings as * warnings. Register-from-intake writes `enforce` for new sites; legacy * sites stay on `warn` until a cleanup mission. */ publication: z .object({ gate: z.enum(["warn", "enforce"]), }) .strict() .optional(), /** * RFC-1293: contract-terms gate; absent = "warn". * `enforce` turns missing/invalid `timing` semantics on time-bound policy * fields into PBP-TIMING-01..04 violations (pbp.content.validate) and makes * TERMS-DRIFT-* findings blocking in pbp.terms-reference.validate; `warn` * logs the same findings as warnings. New sites register with `enforce`; * legacy sites stay on `warn` until the owner decides the timing semantics. */ contractTerms: z .object({ gate: z.enum(["warn", "enforce"]), }) .strict() .optional(), /** * RFC-1294: external local-fact provenance gate; absent = "warn". * `enforce` turns missing verbatim-source fields, off-host source URLs, and * numbers absent from the recorded excerpt into LOCALFACT-01..03 violations * (surface.evidence.validate); `warn` logs the same findings as warnings. * LOCALFACT-04 (staleness) is always a warning. New sites register with * `enforce`; legacy sites stay on `warn` until the owner sources the facts. */ provenance: z .object({ gate: z.enum(["warn", "enforce"]).optional(), }) .strict() .optional(), /** * RFC-1307: ISO 3166-1 alpha-2 legal jurisdiction seeded from the intake * classification at birth (sternsystem.register --intake). Optional — * pre-intake systems carry no value. */ legalJurisdiction: z .string() .regex(/^[A-Z]{2}$/, "legalJurisdiction must be an ISO 3166-1 alpha-2 code") .optional(), /** * Constellations (composition patterns) active in this app. * Each entry is a kebab-case slug matching a file in packages/werkstatt-site/src/domain/ontology/constellations/. * Validated cross-file by constellation.compose.validate. * Empty array = no composition-pattern validation is performed. */ constellations: z .array(z.string().regex(/^[a-z][a-z0-9-]*$/, "constellation slug must be kebab-case")) .optional() .default([]), /** * Client-editable surface declarations — kebab-case keys of content sections * that the client may edit without engineering involvement (DNA-22, RFC-0025). * Validated by client.edit.validate at deploy time. */ clientEditable: z.array(z.string().min(1)).optional().default([]), /** * RFC-1259: optional Content-Authority Model block. When present, it is the * single authorization model consulted by write paths — `clientEditable[]` * becomes its `studio-gate-client` projection, pseo autonomy scopes its * `pseo-generator` instance, and approval gates first-class declarations. * Absent ⇒ legacy behavior (no CAM consultation). Validated by * `authority.model.validate` (CAM-01..05). */ authority: camAuthoritySchema.optional(), sharedContext: systemSharedContextSchema.optional(), /** * Per-page composition pins (RFC-0026, RFC-0048). * Each entry pins the section versions active for a given page. * Validated by page.block.validate — every blocks[].use in page content must * appear in the matching pageId's planets[] list. */ pages: z .array( z.object({ /** * RFC-0048: Stable page identifier across languages (e.g., "privacyPolicy"). * Used as the canonical key for page lookup and route resolution. */ pageId: z.string().min(1), /** * RFC-0048: Language-keyed route slugs (e.g., { de: "datenschutz", en: "privacy" }). * Maps each supported language to its localized URL slug. */ routes: z.record(z.string(), z.string()).optional(), /** * RFC-0048 (legacy): URL route path for this page (e.g. "/", "/spenden"). * Kept for backward compatibility during migration; use pageId + routes instead. */ route: z.string().min(1).optional(), /** * RFC-0097: Explicit locale opt-in. When set, the page exists only in * these locales — runtime resolvers, getStaticPaths, and the * language-switcher all short-circuit silently for locales outside * the set. Use for pages whose existence is locale-dependent * (Impressum is DE/AT/CH-only; US-specific privacy pages are en-only). * Each entry must be a key of `i18n.supported`; cross-validation lives * in `system.contract.validate`. */ locales: z.array(z.string().min(1)).optional(), /** * RFC-0042/RFC-0050: semantic role of the page. Optional — pages without * a semanticType do not enter the semantic model (no JSON-LD / llms output). */ semanticType: semanticPageTypeSchema.optional(), /** * RFC-0377: optional audience override for this page. When absent, the twin generator * falls back to the SemanticPageType derivation map (AUDIENCE_BY_PAGE_TYPE). */ audience: z.string().min(1).optional(), /** * RFC-0229: optional parent pageId for the breadcrumb hierarchy. When set, the page's * breadcrumb trail becomes Home → …parent chain… → self instead of the flat Home → self. * The referenced pageId must exist in this `pages[]` list and the chain must be acyclic — * `breadcrumb.trail.validate` enforces both. Omit for top-level pages (Home → self). */ parentPageId: z.string().min(1).optional(), /** * Standalone page: exists in the route registry for link/semantic-target * resolution but is rendered by a dedicated .astro file (not [...slug].astro). * Excluded from getStaticPathsForDefaultLang to avoid route conflicts. */ standalone: z.boolean().optional(), /** * RFC-0143: per-page output projection container (sitemap, llms, …). * Closed object — only known generator keys are accepted. */ output: pageOutputSchema.optional(), /** * RFC-0803: per-page deployment gating. When production is false, * the page is excluded from production builds (no HTML output, no * sitemap entry, no navigation links, no llms.txt entry). The page * remains visible in astro dev. Defaults to true. */ deployment: z .object({ production: z.boolean().default(true), }) .optional(), /** RFC-0167: article metadata — when present, the page emits Article JSON-LD. */ article: articleMetadataSchema.optional(), /** * The cosmicName of the page archetype from StarCatalog. * Must match the cosmicName of the page's manifest.yaml. */ cosmicStar: z.string().min(1), /** * Optional constellation slug for this page. * When present, constellation.compose.validate checks that the page's * planets match the constellation's required slots. */ constellation: z.string().optional(), /** * Optional shell block configuration for this page. * Controls page-level shell elements like background. */ shell: z .object({ background: z .object({ enabled: z.boolean(), cosmicMoon: z.string().min(1), pin: z.string().min(1), props: z.record(z.string(), z.unknown()).optional(), }) .optional(), /** * RFC-1229: bound shell profile name — resolves the page to a * shellContract.profiles entry. Outranks routeProfiles globs. */ profile: z.string().min(1).optional(), }) .optional(), /** * Ordered list of planet section pins active on this page. * Each cosmicPlanet must appear in PlanetCatalog and be used * in the page's block-declarative content. */ planets: z .array( z.object({ /** PlanetCatalog name for this section archetype. */ cosmicPlanet: z.string().min(1), /** * Semver pin against packages/ui section version. * "latest" is accepted at MVP for apps without strict version pins. */ pin: z.string().min(1), }), ) .optional() .default([]), /** * RFC-0922 / ADR-0062: per-page section nav toggle. When false, the * page template skips extractSectionNavItems and the Header secondary * nav strip is omitted. Defaults to true when absent. */ sectionNav: z.boolean().optional(), /** * ADR-0062: per-page CTA target override. When present, overrides the * app-level `identity.ctaTarget` for this page. */ ctaTarget: z.string().min(1).optional(), }), ) .optional() .default([]), /** * Growth layer binding (RFC-0027). * When absent, the growth layer is inactive and is omitted. * When present, growth.vendor.adapter determines which adapter is loaded on the client. */ growth: systemGrowthSchema.optional(), /** * Release configuration (RFC-0028). * Controls Cosmic Passport, Star Map, and Pulsar heartbeat. * When absent, passport page is not rendered but passport.json is still emitted. */ release: systemReleaseSchema.optional(), /** * RFC-0052 crawl policy (`robots:`) for robots.generate/robots.validate. * RFC-1161: contentSignal ai-train=no derives the named AI-training * Disallow group in robots.txt. */ robots: systemRobotsSchema.optional(), /** * RFC-0235: egress text normalization. Strips AI-authorship typographic * signals from all public output at build egress. Adapter, not source rewrite. * Absent ⇒ all signals on. Client-writable. */ text: systemTextSchema.optional(), /** * RFC-1070: typography validator configuration. Allowed tokens and * per-locale abbreviation overrides. Client-writable. */ typography: systemTypographySchema.optional(), /** * RFC-1209: resource-budget declaration. `resourceBudget` carries per-site * byte/request ceilings (`maxPageWeightKb`, `maxJsKb`, `maxRequests`, * `maxThirdPartyOrigins`), an `enforce` mode (advisory default; blocking is * per-site opt-in), and optional `routeOverrides`. When omitted the site * resolves to the workspace baseline in `RESOURCE_BUDGET_BASELINE` * (`@warpgogol/werkstatt-shared/policy/resource-budget`). Measured on-disk * against `dist/client` by `resource.budget.validate`; the public report is * emitted to `public/.well-known/resource-budget.json` by * `resource.budget.report`. Client-writable. */ resourceBudget: resourceBudgetConfigSchema.optional(), /** * RFC-0169: subscription billing binding. `billing.stripeCustomerId` is the * Stripe customer whose active Entitlements gate this site's paid modules. */ billing: z .object({ stripeCustomerId: z.string().min(1).optional(), // RFC-0196: offline/dogfood Programmatic Surface index budget (top-K indexable pages by // substance). With Stripe, the budget is derived from the tier lookup-key (PSEO_TIER_BUDGET). pseoIndexBudget: z.number().int().min(0).optional(), // RFC-0240: offline/dogfood regional-hub-or-higher pseo tier unlock (unlocks d3 region hubs). // With Stripe, this is derived from the active tier lookup-key (PSEO_REGIONAL_TIERS). pseoRegionalUnlocked: z.boolean().optional(), }) .optional(), /** * RFC-0169: offline entitlement override for dev/CI (deterministic, no network). * When present, wins over Stripe resolution. */ entitlementsOverride: z.array(z.string().min(1)).optional(), /** * RFC-0193: explicit Programmatic Surface adoption. `surface.blueprints` lists the Blueprint ids * (in packages/werkstatt-site/src/domain/ontology/blueprints/) this site compiles. When declared, only listed Blueprints * apply (and blueprint.validate requires their datasets). When omitted, the engine falls back to * implicit opt-in by datasets (a Blueprint applies if the app ships its dataset collection). */ surface: z .object({ blueprints: z.array(z.string().min(1)).optional(), /** * RFC-1259: per-module authored surface config — kept through parse so * RFC-0278 `autonomy.scopes[]` reaches consumers (previously stripped as * an undeclared key). Loose: modules may carry additional keys. */ modules: z .record( z.string(), z.looseObject({ masterLocale: z.string().min(1).optional(), publishedLocales: z.array(z.string().min(1)).optional(), autonomy: z .looseObject({ scopes: z .array( z.looseObject({ fieldClass: z.string().min(1), locale: z.string().min(1), level: z.string().min(1).optional(), ceiling: z.string().min(1).optional(), sinceAt: z.string().min(1).optional(), evidenceRef: z.string().min(1).optional(), }), ) .optional(), }) .optional(), }), ) .optional(), }) .optional(), /** * RFC-0211..0218: Content Knowledge Lifecycle policy. `freshness.soonWindowDays` * sets the expiring-soon horizon; `freshness.critical` maps subject-address globs * to a criticality so the maintenance-plan gate (RFC-0216) can promote selected * facts (e.g. price, legal) from advisory amber to blocking red. */ knowledge: z .object({ freshness: z .object({ soonWindowDays: z.number().int().positive().optional().default(30), critical: z .array( z.object({ match: z.string().min(1), criticality: z.enum(["advisory", "important", "blocking"]), }), ) .optional() .default([]), }) .optional(), derivation: z .object({ critical: z .array( z.object({ match: z.string().min(1), criticality: z.enum(["advisory", "important", "blocking"]), }), ) .optional() .default([]), }) .optional(), /** * RFC-0216: maintenance-plan policy. Controls how the planner schedules and * routes tasks: leadTimeDays (how far before validUntil a task is raised), * the default owner for tasks the claim does not name, and a glob→criticality * map that promotes specific subjects above their trigger's default. */ plan: z .object({ leadTimeDays: z.number().int().positive().optional().default(30), defaultOwner: z.string().min(1).optional(), criticalityMap: z .array( z.object({ match: z.string().min(1), criticality: z.enum(["advisory", "important", "blocking"]), }), ) .optional() .default([]), }) .optional(), }) .optional(), /** * RFC-0171: content-source adapter selection (Content Source Provider port). * Defaults to the filesystem adapter; CMS adapters are opt-in per app. */ contentSource: z .object({ adapter: z.enum(["fs", "cms-git", "cms-api"]).default("fs"), options: z.record(z.string(), z.string()).optional(), }) .optional(), /** * RFC-0168: Integration Port configuration (outbound channels + optional CRM). * When absent, channels self-enable by secret presence and the integration * validators are a no-op pass. */ integrations: systemIntegrationsSchema.optional(), /** * RFC-0182: Deployment and infrastructure residency configuration. * Declares Cloudflare Regional Services policy and allowed execution zones. */ deployment: z .object({ cloudflare: z .object({ /** * Hostnames to validate for Regional Services. * Defaults to [identity.domain] when empty. */ hostnames: z.array(z.string().min(1)).optional().default([]), /** * Regional Services policy — empty array means all zones acceptable. */ regionalServices: z .object({ allowedZones: z .array(z.enum(["eu", "us"])) .optional() .default([]), }) .optional(), }) .optional(), }) .optional(), /** * RFC-0257: App-level output projection. Controls build-time artifact generation * that applies to the whole app, not individual pages. */ output: z .object({ /** Enable SSG PDF generation via Playwright during build.post. Default false. */ printPdf: z.boolean().optional(), }) .optional(), /** * RFC-0286: Agent Surface policy. Absent ⇒ enabled: true — the read tier * (knowledge files, discovery, OpenAPI; RFC-0287/0289) is free visibility like * llms.txt. Set `enabled: false` to disable the whole agent surface for a site. */ agent: z .object({ enabled: z.boolean().optional(), /** RFC-0287: domains to withhold from the knowledge tier even if content exists. */ knowledgeDisabled: z.array(z.string().min(1)).optional(), /** RFC-0288: capability ids to withhold from the action tier even if otherwise active. */ actionsDisabled: z.array(z.string().min(1)).optional(), /** * RFC-1187: worker-hosted agent authorization server — opt-in. When * `auth.enabled` is true AND a signing key is configured at generation * time, the site emits OAuth discovery metadata (RFC 8414 + RFC 9728), * registration/token/JWKS routes, and the auth.md key_proof flow. * Absent/false preserves the anonymous baseline. */ auth: z .object({ enabled: z.boolean().optional(), }) .strict() .optional(), }) .strict() .optional(), /** * RFC-0487: Business model declaration. Closed enum — currently only "b2b-only". * When declared, b2b.model.validate checks that no B2C-specific page IDs, route * slugs, navigation labels, or consumer-law prose references exist. * Future RFCs may add values (b2c, marketplace) as needed. */ businessModel: z.enum(["b2b-only"]).optional(), /** * RFC-0487/RFC-0509: Retired page routes — declarative audit trail of removed page slugs. * The _redirects generator reads this and emits HTTP 410 Gone tombstones or 301 redirects. * When status is 301, `to` is required and specifies the redirect target URL path. * When status is 410, `to` is forbidden (tombstone has no redirect target). */ retiredRoutes: z .array( z.union([ z.object({ slug: z.string().min(1), status: z.literal(410), }), z.object({ slug: z.string().min(1), status: z.literal(301), to: z.string().min(1), }), ]), ) .optional() .default([]), /** * UI-level rendering toggles. Currently controls the column order of * controlled-responsibility-block sections (split-list body). When * `responsibilityBlock.swapOrder` is true, the page handler swaps * primaryItems/secondaryItems and labels.primary/labels.secondary at * render time. Default false = render as authored in content. * RFC-1087: `codeHighlightTheme` selects the Shiki theme for build-time * syntax highlighting. Default "github-light" when absent. */ ui: z .object({ responsibilityBlock: z .object({ swapOrder: z.boolean().optional().default(false), }) .optional(), codeHighlightTheme: z.string().min(1).optional(), }) .optional(), /** * Search engine verification config (RFC-0909). * Declares per-engine verification method and token. * When absent, search.verification.validate emits SEARCH-VERIFY-01. */ verification: systemVerificationSchema.optional(), /** * RFC-1229: site shell contract — named shell profiles (required regions + * document-level script policy) and route→profile bindings enforced on * emitted markup by site.shell.contract.validate. Absent = report mode * with no route bindings (implicit "default" profile assumed). */ shellContract: systemShellContractSchema.optional(), /** * RFC-0911: SEO validator configuration extension point. * Sites extend the built-in de/uk anchor-text stop-list via * `seo.anchorText.extraStopPhrases` (lang → phrases). */ seo: z .object({ anchorText: z .object({ extraStopPhrases: z.record(z.string(), z.array(z.string())).optional(), }) .optional(), }) .optional(), }); export type SystemManifest = z.infer; /** A single page route pin entry from system.yaml pages[]. */ export type SystemPagePin = NonNullable[number]; /** A single planet pin within a page-route entry. */ export type SystemPlanetPin = NonNullable[number]; /** * ADR-0062: Lenient schema for the `system` Astro content collection. * * `systemManifestSchema` is the strict validation schema used by `system.manifest.validate`. * The content collection schema needs to be more lenient: * - `.partial()` — all top-level fields are optional (system.md content varies per site) * - `.loose()` — unknown top-level keys pass through (e.g. `policy`, `seo`) * * This schema is used in `src/content/config.ts` via `defineCollection({ schema: systemCollectionSchema })`. * It provides compile-time typing for `getCollection("system")` consumers, eliminating `as any` casts. */ export const systemCollectionSchema = systemManifestSchema.partial().loose(); /** Inferred type for the `system` content collection data. */ export type SystemCollectionData = z.infer;