From a74345fd8d1cf81e2a9830449809447b975f17bc Mon Sep 17 00:00:00 2001 From: Tim Ji Date: Mon, 16 Mar 2026 10:53:51 +0800 Subject: [PATCH 1/3] feat(sync): ensure front matter consistency for monorepo specs --- .../templates/workflows/archive-change.ts | 6 +++++ .../workflows/bulk-archive-change.ts | 2 ++ src/core/templates/workflows/sync-specs.ts | 22 +++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/src/core/templates/workflows/archive-change.ts b/src/core/templates/workflows/archive-change.ts index 1c37ffde0..4fec3512e 100644 --- a/src/core/templates/workflows/archive-change.ts +++ b/src/core/templates/workflows/archive-change.ts @@ -66,6 +66,9 @@ export function getArchiveChangeSkillTemplate(): SkillTemplate { If user chooses sync, use Task tool (subagent_type: "general-purpose", prompt: "Use Skill tool to invoke openspec-sync-specs for change ''. Delta spec analysis: "). Proceed to archive regardless of choice. + **Front matter consistency (after sync):** + Ensure newly synced specs follow existing front matter conventions. See sync-specs step 3e for full logic (monorepo detection, opt-out via \`monorepoAppLabel: false\` in \`openspec/config.yaml\`). + 5. **Perform the archive** Create the archive directory if it doesn't exist: @@ -181,6 +184,9 @@ export function getOpsxArchiveCommandTemplate(): CommandTemplate { If user chooses sync, use Task tool (subagent_type: "general-purpose", prompt: "Use Skill tool to invoke openspec-sync-specs for change ''. Delta spec analysis: "). Proceed to archive regardless of choice. + **Front matter consistency (after sync):** + Ensure newly synced specs follow existing front matter conventions. See sync-specs step 3e for full logic (monorepo detection, opt-out via \`monorepoAppLabel: false\` in \`openspec/config.yaml\`). + 5. **Perform the archive** Create the archive directory if it doesn't exist: diff --git a/src/core/templates/workflows/bulk-archive-change.ts b/src/core/templates/workflows/bulk-archive-change.ts index d57db8aa0..fec9693ec 100644 --- a/src/core/templates/workflows/bulk-archive-change.ts +++ b/src/core/templates/workflows/bulk-archive-change.ts @@ -125,6 +125,7 @@ This skill allows you to batch-archive changes, handling spec conflicts intellig - Use the openspec-sync-specs approach (agent-driven intelligent merge) - For conflicts, apply in resolved order - Track if sync was done + - Ensure front matter consistency on newly synced specs (see sync-specs step 3e) b. **Perform the archive**: \`\`\`bash @@ -372,6 +373,7 @@ This skill allows you to batch-archive changes, handling spec conflicts intellig - Use the openspec-sync-specs approach (agent-driven intelligent merge) - For conflicts, apply in resolved order - Track if sync was done + - Ensure front matter consistency on newly synced specs (see sync-specs step 3e) b. **Perform the archive**: \`\`\`bash diff --git a/src/core/templates/workflows/sync-specs.ts b/src/core/templates/workflows/sync-specs.ts index 34da4276e..33c966eb7 100644 --- a/src/core/templates/workflows/sync-specs.ts +++ b/src/core/templates/workflows/sync-specs.ts @@ -71,6 +71,17 @@ This is an **agent-driven** operation - you will read delta specs and directly e - Add Purpose section (can be brief, mark as TBD) - Add Requirements section with the ADDED requirements + e. **Ensure front matter consistency** on newly synced specs (common in monorepos where specs need to indicate which app/package they belong to): + - If \`openspec/config.yaml\` contains \`monorepoAppLabel: false\` → skip + - Sample existing main specs in \`openspec/specs/\` for YAML front matter convention + - If existing specs use front matter → apply same pattern to newly created/updated specs + - If no existing specs have front matter AND project is single-app → skip + - If no existing specs have front matter AND project is a monorepo (multiple apps/packages detected): + + **Prompt options:** + - "Establish front matter convention (recommended)" — suggest \`app: \` based on project structure, apply to newly synced specs + - "Skip and don't ask again" — add \`monorepoAppLabel: false\` to \`openspec/config.yaml\` + 4. **Show summary** After applying all changes, summarize: @@ -210,6 +221,17 @@ This is an **agent-driven** operation - you will read delta specs and directly e - Add Purpose section (can be brief, mark as TBD) - Add Requirements section with the ADDED requirements + e. **Ensure front matter consistency** on newly synced specs (common in monorepos where specs need to indicate which app/package they belong to): + - If \`openspec/config.yaml\` contains \`monorepoAppLabel: false\` → skip + - Sample existing main specs in \`openspec/specs/\` for YAML front matter convention + - If existing specs use front matter → apply same pattern to newly created/updated specs + - If no existing specs have front matter AND project is single-app → skip + - If no existing specs have front matter AND project is a monorepo (multiple apps/packages detected): + + **Prompt options:** + - "Establish front matter convention (recommended)" — suggest \`app: \` based on project structure, apply to newly synced specs + - "Skip and don't ask again" — add \`monorepoAppLabel: false\` to \`openspec/config.yaml\` + 4. **Show summary** After applying all changes, summarize: From fcd8f509bbe4af4d7a61846ded18046a4784baed Mon Sep 17 00:00:00 2001 From: Tim Ji Date: Mon, 16 Mar 2026 15:43:32 +0800 Subject: [PATCH 2/3] fix(sync): support both .yaml and .yml config file extensions Co-Authored-By: Claude Opus 4.6 (1M context) --- src/core/templates/workflows/sync-specs.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/templates/workflows/sync-specs.ts b/src/core/templates/workflows/sync-specs.ts index 33c966eb7..e5b33ea5c 100644 --- a/src/core/templates/workflows/sync-specs.ts +++ b/src/core/templates/workflows/sync-specs.ts @@ -72,7 +72,7 @@ This is an **agent-driven** operation - you will read delta specs and directly e - Add Requirements section with the ADDED requirements e. **Ensure front matter consistency** on newly synced specs (common in monorepos where specs need to indicate which app/package they belong to): - - If \`openspec/config.yaml\` contains \`monorepoAppLabel: false\` → skip + - If the project config (\`openspec/config.yaml\` or \`openspec/config.yml\`) contains \`monorepoAppLabel: false\` → skip - Sample existing main specs in \`openspec/specs/\` for YAML front matter convention - If existing specs use front matter → apply same pattern to newly created/updated specs - If no existing specs have front matter AND project is single-app → skip @@ -80,7 +80,7 @@ This is an **agent-driven** operation - you will read delta specs and directly e **Prompt options:** - "Establish front matter convention (recommended)" — suggest \`app: \` based on project structure, apply to newly synced specs - - "Skip and don't ask again" — add \`monorepoAppLabel: false\` to \`openspec/config.yaml\` + - "Skip and don't ask again" — add \`monorepoAppLabel: false\` to the existing OpenSpec config file without overwriting other keys 4. **Show summary** @@ -222,7 +222,7 @@ This is an **agent-driven** operation - you will read delta specs and directly e - Add Requirements section with the ADDED requirements e. **Ensure front matter consistency** on newly synced specs (common in monorepos where specs need to indicate which app/package they belong to): - - If \`openspec/config.yaml\` contains \`monorepoAppLabel: false\` → skip + - If the project config (\`openspec/config.yaml\` or \`openspec/config.yml\`) contains \`monorepoAppLabel: false\` → skip - Sample existing main specs in \`openspec/specs/\` for YAML front matter convention - If existing specs use front matter → apply same pattern to newly created/updated specs - If no existing specs have front matter AND project is single-app → skip @@ -230,7 +230,7 @@ This is an **agent-driven** operation - you will read delta specs and directly e **Prompt options:** - "Establish front matter convention (recommended)" — suggest \`app: \` based on project structure, apply to newly synced specs - - "Skip and don't ask again" — add \`monorepoAppLabel: false\` to \`openspec/config.yaml\` + - "Skip and don't ask again" — add \`monorepoAppLabel: false\` to the existing OpenSpec config file without overwriting other keys 4. **Show summary** From 8582ce468c06b2ef9982de665df9c7171cf59e4d Mon Sep 17 00:00:00 2001 From: Tim Ji Date: Tue, 17 Mar 2026 19:56:00 +0800 Subject: [PATCH 3/3] refactor(config): rename monorepoAppLabel to monorepo and add to ProjectConfigSchema Co-Authored-By: Claude Opus 4.6 (1M context) --- src/core/project-config.ts | 18 ++++++++++++++++++ src/core/templates/workflows/archive-change.ts | 4 ++-- src/core/templates/workflows/sync-specs.ts | 8 ++++---- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/core/project-config.ts b/src/core/project-config.ts index 6c1ea04a5..3d74a48b0 100644 --- a/src/core/project-config.ts +++ b/src/core/project-config.ts @@ -38,6 +38,13 @@ export const ProjectConfigSchema = z.object({ ) .optional() .describe('Per-artifact rules, keyed by artifact ID'), + + // Optional: whether the project is a monorepo (affects front matter sync behavior) + // Set to false to skip monorepo-related prompts (e.g., app label in spec front matter) + monorepo: z + .boolean() + .optional() + .describe('Set to false to opt out of monorepo front matter conventions'), }); export type ProjectConfig = z.infer; @@ -152,6 +159,17 @@ export function readProjectConfig(projectRoot: string): ProjectConfig | null { } } + // Parse monorepo field using Zod + if (raw.monorepo !== undefined) { + const monorepoField = z.boolean(); + const monorepoResult = monorepoField.safeParse(raw.monorepo); + if (monorepoResult.success) { + config.monorepo = monorepoResult.data; + } else { + console.warn(`Invalid 'monorepo' field in config (must be boolean)`); + } + } + // Return partial config even if some fields failed return Object.keys(config).length > 0 ? (config as ProjectConfig) : null; } catch (error) { diff --git a/src/core/templates/workflows/archive-change.ts b/src/core/templates/workflows/archive-change.ts index 4fec3512e..0399fbc2d 100644 --- a/src/core/templates/workflows/archive-change.ts +++ b/src/core/templates/workflows/archive-change.ts @@ -67,7 +67,7 @@ export function getArchiveChangeSkillTemplate(): SkillTemplate { If user chooses sync, use Task tool (subagent_type: "general-purpose", prompt: "Use Skill tool to invoke openspec-sync-specs for change ''. Delta spec analysis: "). Proceed to archive regardless of choice. **Front matter consistency (after sync):** - Ensure newly synced specs follow existing front matter conventions. See sync-specs step 3e for full logic (monorepo detection, opt-out via \`monorepoAppLabel: false\` in \`openspec/config.yaml\`). + Ensure newly synced specs follow existing front matter conventions. See sync-specs step 3e for full logic (monorepo detection, opt-out via \`monorepo: false\` in config). 5. **Perform the archive** @@ -185,7 +185,7 @@ export function getOpsxArchiveCommandTemplate(): CommandTemplate { If user chooses sync, use Task tool (subagent_type: "general-purpose", prompt: "Use Skill tool to invoke openspec-sync-specs for change ''. Delta spec analysis: "). Proceed to archive regardless of choice. **Front matter consistency (after sync):** - Ensure newly synced specs follow existing front matter conventions. See sync-specs step 3e for full logic (monorepo detection, opt-out via \`monorepoAppLabel: false\` in \`openspec/config.yaml\`). + Ensure newly synced specs follow existing front matter conventions. See sync-specs step 3e for full logic (monorepo detection, opt-out via \`monorepo: false\` in config). 5. **Perform the archive** diff --git a/src/core/templates/workflows/sync-specs.ts b/src/core/templates/workflows/sync-specs.ts index e5b33ea5c..cdc137327 100644 --- a/src/core/templates/workflows/sync-specs.ts +++ b/src/core/templates/workflows/sync-specs.ts @@ -72,7 +72,7 @@ This is an **agent-driven** operation - you will read delta specs and directly e - Add Requirements section with the ADDED requirements e. **Ensure front matter consistency** on newly synced specs (common in monorepos where specs need to indicate which app/package they belong to): - - If the project config (\`openspec/config.yaml\` or \`openspec/config.yml\`) contains \`monorepoAppLabel: false\` → skip + - If the project config (\`openspec/config.yaml\` or \`openspec/config.yml\`) contains \`monorepo: false\` → skip - Sample existing main specs in \`openspec/specs/\` for YAML front matter convention - If existing specs use front matter → apply same pattern to newly created/updated specs - If no existing specs have front matter AND project is single-app → skip @@ -80,7 +80,7 @@ This is an **agent-driven** operation - you will read delta specs and directly e **Prompt options:** - "Establish front matter convention (recommended)" — suggest \`app: \` based on project structure, apply to newly synced specs - - "Skip and don't ask again" — add \`monorepoAppLabel: false\` to the existing OpenSpec config file without overwriting other keys + - "Skip and don't ask again" — add \`monorepo: false\` to the existing OpenSpec config file without overwriting other keys 4. **Show summary** @@ -222,7 +222,7 @@ This is an **agent-driven** operation - you will read delta specs and directly e - Add Requirements section with the ADDED requirements e. **Ensure front matter consistency** on newly synced specs (common in monorepos where specs need to indicate which app/package they belong to): - - If the project config (\`openspec/config.yaml\` or \`openspec/config.yml\`) contains \`monorepoAppLabel: false\` → skip + - If the project config (\`openspec/config.yaml\` or \`openspec/config.yml\`) contains \`monorepo: false\` → skip - Sample existing main specs in \`openspec/specs/\` for YAML front matter convention - If existing specs use front matter → apply same pattern to newly created/updated specs - If no existing specs have front matter AND project is single-app → skip @@ -230,7 +230,7 @@ This is an **agent-driven** operation - you will read delta specs and directly e **Prompt options:** - "Establish front matter convention (recommended)" — suggest \`app: \` based on project structure, apply to newly synced specs - - "Skip and don't ask again" — add \`monorepoAppLabel: false\` to the existing OpenSpec config file without overwriting other keys + - "Skip and don't ask again" — add \`monorepo: false\` to the existing OpenSpec config file without overwriting other keys 4. **Show summary**