fix(bundler): reject unsupported schema_version in _merge_config (align readers) (#3711)

bundle-catalogs.yml has two readers that are meant to agree: commands_impl/
catalog_config._read (bundle catalog list/add/remove) and models/catalog.
_merge_config (the resolution path feeding bundle search/info/install via
load_source_stack). _read rejects an unsupported MAJOR schema_version;
_merge_config never checked it, so a file written by a newer/incompatible
Spec Kit (e.g. schema_version '2.0') was silently parsed under v1 assumptions
on the exact path where install_policy governs trust — the two readers
disagreed. #3623 (non-list catalogs) and #3659 (top-level non-mapping) already
aligned these two readers guard-by-guard; this is the last unaligned guard.

Add the same forward-compatible major-version check to _merge_config. Promote
CONFIG_SCHEMA_VERSION to models/catalog.py as the single source of truth and
import it in catalog_config.py (was a local duplicate) so the two cannot drift.
Absent schema_version stays valid (backward compatible); matching major stays
valid.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ali jawwad
2026-07-27 18:19:01 +05:00
committed by GitHub
parent 015d125667
commit 59e63699b8
3 changed files with 61 additions and 2 deletions

View File

@@ -14,14 +14,13 @@ from .. import BundlerError
from ..lib.yamlio import dump_yaml, ensure_within, load_yaml
from ..models.catalog import (
CONFIG_FILENAME,
CONFIG_SCHEMA_VERSION,
BUILTIN_DEFAULT_STACK,
CatalogSource,
InstallPolicy,
Scope,
)
CONFIG_SCHEMA_VERSION = "1.0"
_BUILTIN_IDS = {raw["id"] for raw in BUILTIN_DEFAULT_STACK}
# Windows absolute paths like ``C:\catalog.json`` parse with a single-letter

View File

@@ -15,6 +15,11 @@ from .. import BundlerError
from ..lib.yamlio import ensure_within, load_yaml
CONFIG_FILENAME = "bundle-catalogs.yml"
# Supported bundle-catalogs.yml schema (major version). Both readers of the
# file — this module's _merge_config and commands_impl/catalog_config._read —
# reject an unsupported major version so a file written by a newer/incompatible
# Spec Kit fails fast instead of being parsed under the wrong assumptions.
CONFIG_SCHEMA_VERSION = "1.0"
class InstallPolicy(str, Enum):
@@ -267,6 +272,23 @@ def _merge_config(by_id: dict[str, CatalogSource], config_path: Path, scope: Sco
f"Malformed catalog config at {config_path}: expected a mapping at "
f"the top level, got {type(data).__name__}."
)
# Reject an unsupported major schema version, matching the sibling reader
# commands_impl/catalog_config._read. Without this, a file written by a
# newer/incompatible Spec Kit was silently parsed under v1 assumptions on
# the resolution path (bundle search/install), while the other reader
# rejected it — the two readers disagreed. An absent schema_version stays
# valid (backward compatible with configs that omit it).
schema_version = data.get("schema_version")
if schema_version is not None and (
str(schema_version).strip().split(".")[0]
!= CONFIG_SCHEMA_VERSION.split(".")[0]
):
raise BundlerError(
f"Unsupported catalog config schema version "
f"'{str(schema_version).strip()}' at {config_path}; this Spec Kit "
f"understands version {CONFIG_SCHEMA_VERSION}. The file may have been "
"written by a newer version or is corrupt."
)
catalogs = data.get("catalogs")
if catalogs is None:
return