openspec/changes/unify-template-generation-pipeline/design.md
OpenSpec currently has strong building blocks (workflow templates, command adapters, generation helpers), but orchestration concerns are distributed:
init/update/legacy-upgrade each run similar write pipelines with slight differencesThe design goal is to preserve current behavior while making extension points explicit and deterministic.
Goals:
Non-Goals:
WorkflowManifestDecision: Represent each workflow once in a manifest entry containing canonical skill and command definitions plus metadata defaults.
Suggested shape:
interface WorkflowManifestEntry {
workflowId: string; // e.g. 'explore', 'ff', 'onboard'
skillDirName: string; // e.g. 'openspec-explore'
skill: SkillTemplate;
command?: CommandTemplate;
commandId?: string;
tags: string[];
compatibility: string;
}
Rationale:
ToolProfileRegistry for capability wiringDecision: Add a tool profile layer that maps tool IDs to generation capabilities and behavior.
Suggested shape:
interface ToolProfile {
toolId: string;
skillsDir?: string;
commandAdapterId?: string;
transforms: string[];
}
Rationale:
AI_TOOLS, adapter registry, and detection logicDecision: Model transforms as ordered plugins with scope + phase + applicability.
Suggested shape:
interface ArtifactTransform {
id: string;
scope: 'skill' | 'command' | 'both';
phase: 'preAdapter' | 'postAdapter';
priority: number;
applies(ctx: GenerationContext): boolean;
transform(content: string, ctx: GenerationContext): string;
}
Execution order:
preAdapter transformspostAdapter transformsRationale:
init/updateArtifactSyncEngineDecision: Introduce a single orchestration engine used by all generation entry points.
Responsibilities:
(workflows × selected tools × artifact kinds)Rationale:
Decision: Add strict checks in tests (and optional runtime assertions in dev builds) for:
license, compatibility, metadata) present for all manifest entriesRationale:
Risk: Migration complexity A broad refactor can destabilize generation paths. → Mitigation: introduce in phases with parity tests before cutover.
Risk: Over-abstraction Too many layers can obscure simple flows. → Mitigation: keep interfaces minimal and colocate registries with generation code.
Trade-off: More upfront structure Adding manifest/profile/transform registries increases conceptual surface area. → Accepted: this cost is offset by reduced drift and easier extension.
getSkillTemplates/getCommandContents to derive from manifestArtifactSyncEngine and switch init to use it with parity checksupdate and legacy upgrade flows to same engine