// The TS plane's reader for the shared stage registry (`shared/registry.yaml`). // // Twin of perk/substrate/registry.py: both planes parse the SAME bundled file (no codegen). The // extension drives in-session transitions from the parsed registry. The Python CLI is the // authoritative validator (`perk registry check`); this side does a thin structural assertion. import { readFileSync } from "node:fs"; import { join } from "node:path"; import { parse } from "./miniYaml.ts"; import { sharedDir } from "./resources.ts"; export interface RegistryStage { id: string; command: string; doors: Record; predecessors: string[]; successors: string[]; requires?: string[]; reads?: string[]; writes?: string[]; } export interface Registry { schemaVersion: number; stages: RegistryStage[]; } /** The registry state-key for the active plan->branch ref (contracts.md §8.1/§8.4). */ export const PLAN_REF_STATE_KEY = "cache.plan-ref"; /** * Whether a stage *consumes* the plan-ref selector — i.e. its `requires ∪ reads` lists * `cache.plan-ref`. The worktree stages (implement/submit/address/land/learn) do; the * root `worktree: none` stages (plan/objective-plan/save) do not. An unknown stage id is * treated as non-consuming (false). */ export function stageConsumesPlanRef(registry: Registry, stageId: string): boolean { const stage = registry.stages.find((s) => s.id === stageId); if (stage === undefined) return false; const keys = [...(stage.requires ?? []), ...(stage.reads ?? [])]; return keys.includes(PLAN_REF_STATE_KEY); } /** Parse the bundled `registry.yaml`. Throws on a missing file or unexpected shape. */ export function loadRegistry(): Registry { const path = join(sharedDir(), "registry.yaml"); const data = parse(readFileSync(path, "utf8")) as unknown; if (typeof data !== "object" || data === null) { throw new Error(`perk: ${path} is not a mapping`); } const record = data as Record; const stages = record.stages; if (!Array.isArray(stages) || stages.length === 0) { throw new Error(`perk: ${path} has no stages`); } return { schemaVersion: typeof record.schema_version === "number" ? record.schema_version : 0, stages: stages.map((raw) => raw as RegistryStage), }; }