/*
Manifest resolver: reads section/component manifest YAML from packages/ui/src
and returns the composed propsSchema for a given cosmicName. Concentrates all
filesystem I/O, YAML parsing, and fragment composition behind one interface.
- Do not define Zod schemas here — pure resolution and I/O.
- Do not validate cross-system.yaml references — that is page.block.validate's job.
- RFC-0484: suppress console.debug for directories without manifests — group directories are expected to have none.
- RFC-1097: step 6 — compass.migrate codemod run
Mechanical v1 to v2 header migration across the workspace: 942 files rewritten — CHANGE_SUMMARY windows collapsed into history, forbidden v1 blocks stripped, KEY_DECISIONS seeded from @ai-invariant comments (5 files) or TODO placeholders (103 files), blocks reordered to canonical order.
- RFC-1097: sweep — werkstatt-engine clean
Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics.
- RFC-1204: effectsParity flag on section manifests + resolver lookup
- RFC-1261
*/
import { join } from "node:path";
import { parse as parseYaml } from "yaml";
import { sectionManifestSchema } from "../manifest.ts";
import { composeManifestPropsSchema } from "../shared-section-props/index.ts";
import { createDefaultIO, type WorkspaceIO } from "@warpgogol/werkstatt-shared/kernel/workspace-io";
/**
* Reads the manifest YAML for the section assigned to `planetName` and returns
* its `propsSchema` (JSON Schema object) along with the section's `semanticId`
* and current `version`.
*
* Returns null when no manifest is found for the given planet name, or when
* the manifest has no `propsSchema` declared.
*
* @param cosmicName — PlanetCatalog or passport-reserved MoonCatalog name
* @param packagesUiRoot — absolute path to packages/ui/src (for locating manifests)
*
* Usage (in page.block.validate):
* ```ts
* const schema = await getSectionPropsSchema("Europa", "/workspace/packages/ui/src");
* ```
*/
export async function getSectionPropsSchema(
cosmicName: string,
packagesUiRoot: string,
io: WorkspaceIO = createDefaultIO().io,
): Promise<{
propsSchema: Record;
semanticId: string;
version: string;
} | null> {
for (const manifest of await collectSectionManifests(packagesUiRoot, io)) {
if (manifest.cosmicName !== cosmicName) continue;
const compose = manifest.propsSchemaCompose;
const local = manifest.propsSchema;
// RFC-0101 + RFC-0102 + RFC-0103: when the manifest declares
// propsSchemaCompose, merge the referenced shared fragments before
// validation. A manifest without either field disables strict prop
// validation (existing behaviour preserved).
if (!compose && !local) return null;
const composed = composeManifestPropsSchema({
compose,
local: local as Record | undefined,
});
return {
propsSchema: composed,
semanticId: manifest.semanticId,
version: manifest.version,
};
}
return null;
}
/**
* RFC-1261: resolves the `localePolicy` map of the section manifest assigned
* to `cosmicName` (for content-ops mirror field-path policy). Returns
* undefined when no manifest matches or it declares no policy.
*/
export async function getSectionLocalePolicy(
cosmicName: string,
packagesUiRoot: string,
io: WorkspaceIO = createDefaultIO().io,
): Promise | undefined> {
for (const manifest of await collectSectionManifests(packagesUiRoot, io)) {
if (manifest.cosmicName !== cosmicName) continue;
return manifest.localePolicy;
}
return undefined;
}
/**
* RFC-1204: map of `cosmicName → effectsParity` for every section manifest
* that opted into sibling effect-signature parity (only `"strict"` exists
* today, so the map value is the literal). Sections without the flag are
* absent from the map — consumers treat absence as "no cross-page parity".
*
* Not a getSectionPropsSchema extension: that function only resolves
* manifests that declare propsSchema/propsSchemaCompose and returns a
* per-cosmicName record, whereas parity consumers need the full opt-in set
* in one pass.
*/
export async function resolveEffectsParityFlags(
packagesUiRoot: string,
io: WorkspaceIO = createDefaultIO().io,
): Promise