// The standard block registry — every shipping block under a *versioned* // registry code (`levelset.PeakCap.v1`). Versioned names are the // forward-compatibility seam: when a block's semantics change, ship a `.v2` // and keep `.v1` resolving, so a run config saved in March still produces // the same schedule in September. Renames get an alias entry, not a break. // // `createRegistry` stays side-effect-free and singleton-less; this module // just curates the defs. Apps with their own catalogs (codes, tiers, // shadows) keep building their own registries — or extend this one via // `standardRegistry(extraDefs)`. import { ConcurrentResourceCostBlock } from "./concurrentResourceCost.ts"; import { ConcurrentUnitsLimitBlock } from "./concurrentUnitsLimit.ts"; import { CrewBorrowBlock } from "./crewBorrow.ts"; import { DeadlineBlock } from "./deadline.ts"; import { GateBlock } from "./gate.ts"; import { HiringLagPenaltyBlock } from "./hiringLagPenalty.ts"; import { KBestBlock } from "./kBest.ts"; import { MakespanBlock } from "./makespan.ts"; import { MaxConcurrentResourceBlock } from "./maxConcurrentResource.ts"; import { ModeSelectionBlock } from "./modeSelection.ts"; import { OpenUnitPenaltyBlock } from "./openUnitPenalty.ts"; import { ParetoBlock } from "./pareto.ts"; import { PhaseReleaseBlock } from "./phaseRelease.ts"; import { PeakCapBlock } from "./peakCap.ts"; import { PhaseEarlinessBlock } from "./phaseEarliness.ts"; import { PhaseLatenessBlock } from "./phaseLateness.ts"; import { createRegistry, type BlockDef, type BlockRegistry, } from "./registry.ts"; import { ReleaseBlock } from "./release.ts"; import { SerialSGSBlock } from "./serialSgsSearch.ts"; import { UnimodalDeviationBlock } from "./unimodalDeviation.ts"; /** Versioned registry codes for the shipping blocks. */ export const STANDARD_CODES = { maxConcurrentResource: "levelset.MaxConcurrentResource.v1", peakCap: "levelset.PeakCap.v1", concurrentUnitsLimit: "levelset.ConcurrentUnitsLimit.v1", deadline: "levelset.Deadline.v1", release: "levelset.Release.v1", phaseRelease: "levelset.PhaseRelease.v1", modeSelection: "levelset.ModeSelection.v1", crewBorrow: "levelset.CrewBorrow.v1", concurrentResourceCost: "levelset.ConcurrentResourceCost.v1", openUnitPenalty: "levelset.OpenUnitPenalty.v1", hiringLagPenalty: "levelset.HiringLagPenalty.v1", phaseLateness: "levelset.PhaseLateness.v1", phaseEarliness: "levelset.PhaseEarliness.v1", unimodalDeviation: "levelset.UnimodalDeviation.v1", makespan: "levelset.Makespan.v1", serialSGS: "levelset.SerialSGS.v1", kBest: "levelset.KBest.v1", pareto: "levelset.Pareto.v1", gate: "levelset.Gate.v1", } as const; export function standardBlockDefs(): BlockDef[] { return [ { code: STANDARD_CODES.maxConcurrentResource, family: "constraint", tier: "estimator", block: MaxConcurrentResourceBlock, defaultShadow: { resourceUniqueId: 0, max: 1 }, assumptionTemplate: "at most {max} concurrent tasks on resource {resourceUniqueId}", }, { code: STANDARD_CODES.peakCap, family: "constraint", tier: "estimator", block: PeakCapBlock, defaultShadow: { resourceUniqueId: 0, cap: 1 }, assumptionTemplate: "peak demand on resource {resourceUniqueId} capped at {cap} units", }, { code: STANDARD_CODES.concurrentUnitsLimit, family: "constraint", tier: "estimator", block: ConcurrentUnitsLimitBlock, defaultShadow: { unitIds: [], max: 1 }, assumptionTemplate: "at most {max} work units open at once", }, { code: STANDARD_CODES.deadline, family: "constraint", tier: "estimator", block: DeadlineBlock, defaultShadow: { taskUniqueId: 0, latestFinish: 0 }, assumptionTemplate: "task {taskUniqueId} finishes by day {latestFinish}", }, { code: STANDARD_CODES.release, family: "constraint", tier: "estimator", block: ReleaseBlock, defaultShadow: { taskUniqueId: 0, earliestStart: 0 }, assumptionTemplate: "task {taskUniqueId} starts no earlier than day {earliestStart}", }, { code: STANDARD_CODES.phaseRelease, family: "constraint", tier: "estimator", block: PhaseReleaseBlock, defaultShadow: { taskUniqueIds: [0], earliestStart: 0 }, assumptionTemplate: "phase tasks start no earlier than day {earliestStart}", }, { code: STANDARD_CODES.modeSelection, family: "constraint", tier: "engineer", block: ModeSelectionBlock, defaultShadow: { taskUniqueId: 0, modes: [{ modeId: 1, durationDays: 1, resourceDemand: [] }], }, assumptionTemplate: "task {taskUniqueId} chooses among its listed duration/crew modes", }, { code: STANDARD_CODES.crewBorrow, family: "constraint", tier: "engineer", block: CrewBorrowBlock, defaultShadow: { taskUniqueIds: [0], homeResourceUniqueId: 0, borrowResourceUniqueId: 1, }, assumptionTemplate: "listed tasks may substitute resource {borrowResourceUniqueId} for home resource {homeResourceUniqueId}", }, { code: STANDARD_CODES.concurrentResourceCost, family: "scorer", tier: "estimator", block: ConcurrentResourceCostBlock, defaultShadow: { resourceUniqueId: 0, basePrice: 100, growthBase: 2, threshold: 0, }, }, { code: STANDARD_CODES.openUnitPenalty, family: "scorer", tier: "estimator", block: OpenUnitPenaltyBlock, defaultShadow: { unitIds: [], softMax: 0, weight: 1 }, }, { code: STANDARD_CODES.hiringLagPenalty, family: "scorer", tier: "estimator", block: HiringLagPenaltyBlock, defaultShadow: { resourceUniqueId: 0, trainingWeeks: 2, costPerCrewWeek: 1000, }, }, { code: STANDARD_CODES.phaseLateness, family: "scorer", tier: "estimator", block: PhaseLatenessBlock, defaultShadow: { phases: [] }, }, { code: STANDARD_CODES.phaseEarliness, family: "scorer", tier: "estimator", block: PhaseEarlinessBlock, defaultShadow: { phases: [] }, }, { code: STANDARD_CODES.unimodalDeviation, family: "scorer", tier: "engineer", block: UnimodalDeviationBlock, defaultShadow: { resourceUniqueId: 0, allowSecondPeak: false, jitterTolerance: 1, }, }, { code: STANDARD_CODES.makespan, family: "scorer", tier: "sales", block: MakespanBlock, defaultShadow: {}, }, { code: STANDARD_CODES.serialSGS, family: "search", tier: "engineer", block: SerialSGSBlock, defaultShadow: {}, }, { code: STANDARD_CODES.kBest, family: "collector", tier: "engineer", block: KBestBlock, // Placeholder scorer reference — the app rewires `by` to real scorer // instanceIds before building a config (same spirit as the id-0 // placeholders on the constraint shadows). defaultShadow: { k: 3, by: ["makespan"] }, }, { code: STANDARD_CODES.pareto, family: "collector", tier: "engineer", block: ParetoBlock, defaultShadow: { over: ["makespan", "cost"] }, }, { code: STANDARD_CODES.gate, family: "collector", tier: "engineer", block: GateBlock, // Placeholder scorer reference + bound — the app rewires `limits` to // real scorer instanceIds and caps before building a config. defaultShadow: { limits: [{ scorer: "makespan", max: 0 }], fallback: "none", }, }, ]; } /** A registry pre-loaded with the standard defs; pass extra defs to extend * (plugins, app-specific blocks, `.v2` revisions). */ export function standardRegistry( extraDefs?: readonly BlockDef[], ): BlockRegistry { return createRegistry([...standardBlockDefs(), ...(extraDefs ?? [])]); }