diff --git a/STANDARDS_VERSION b/STANDARDS_VERSION new file mode 100644 index 0000000..81c871d --- /dev/null +++ b/STANDARDS_VERSION @@ -0,0 +1 @@ +1.10.0 diff --git a/VERSION b/VERSION index 169f19b..0eed1a2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.11.0 \ No newline at end of file +1.12.0 diff --git a/scaffold/create-tool.py b/scaffold/create-tool.py index ae7cc36..1b41d8b 100644 --- a/scaffold/create-tool.py +++ b/scaffold/create-tool.py @@ -20,7 +20,7 @@ TEMPLATES_DIR = Path(__file__).parent / "templates" -VERSION_FILE = Path(__file__).parent.parent / "VERSION" +STANDARDS_VERSION_FILE = Path(__file__).parent.parent / "STANDARDS_VERSION" LICENSE_FILES = { "cc-by-nc-nd-4.0": "CC-BY-NC-ND-4.0", @@ -42,23 +42,23 @@ def slugify(name: str) -> str: def read_standards_version() -> str: - """Read the meta-repo VERSION at generation time. + """Read the meta-repo STANDARDS_VERSION at generation time. New tool repos are pre-aligned with the current standards version, so the - value here is not a runtime decision. If VERSION is missing or unreadable, - fail loudly rather than silently substituting a default - a wrong version - would defeat the drift-checker invariant. + value here is not a runtime decision. If STANDARDS_VERSION is missing or + unreadable, fail loudly rather than silently substituting a default - a + wrong version would defeat the drift-checker invariant. """ try: - raw = VERSION_FILE.read_text(encoding="utf-8").strip() + raw = STANDARDS_VERSION_FILE.read_text(encoding="utf-8").strip() except FileNotFoundError: print( - f"Error: VERSION file not found at {VERSION_FILE}. " + f"Error: STANDARDS_VERSION file not found at {STANDARDS_VERSION_FILE}. " "The scaffold must run from a working copy of Developer-Tools-Directory." ) sys.exit(1) except OSError as e: - print(f"Error: could not read {VERSION_FILE}: {e}") + print(f"Error: could not read {STANDARDS_VERSION_FILE}: {e}") sys.exit(1) if not re.fullmatch(r"\d+\.\d+\.\d+", raw): print( diff --git a/scripts/drift_check/cli.py b/scripts/drift_check/cli.py index 898553e..237a74f 100644 --- a/scripts/drift_check/cli.py +++ b/scripts/drift_check/cli.py @@ -86,19 +86,19 @@ def _find_repo_root() -> Path: - """Walk up from this file to the repo that contains ``VERSION``.""" + """Walk up from this file to the repo that contains ``STANDARDS_VERSION``.""" here = Path(__file__).resolve() for candidate in (here.parent, *here.parents): - if (candidate / "VERSION").is_file(): + if (candidate / "STANDARDS_VERSION").is_file(): return candidate return here.parents[2] def _read_meta_version(repo_root: Path) -> Version: - raw = (repo_root / "VERSION").read_text(encoding="utf-8").strip() + raw = (repo_root / "STANDARDS_VERSION").read_text(encoding="utf-8").strip() v = parse_version(raw) if v is None: - raise SystemExit(f"meta-repo VERSION is not a valid semver: {raw!r}") + raise SystemExit(f"meta-repo STANDARDS_VERSION is not a valid semver: {raw!r}") return v @@ -199,7 +199,7 @@ def build_parser() -> argparse.ArgumentParser: default=None, help=( "path to the meta-repo root for resolving standards/*.md references " - "(defaults to the repo that contains VERSION)" + "(defaults to the repo that contains STANDARDS_VERSION)" ), ) p.add_argument( @@ -355,7 +355,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int: except SystemExit: raise except Exception as exc: # pragma: no cover - defensive - print(f"error: cannot read VERSION: {exc}", file=sys.stderr) + print(f"error: cannot read STANDARDS_VERSION: {exc}", file=sys.stderr) return 2 try: diff --git a/standards/versioning.md b/standards/versioning.md index 5743c5a..2a009b8 100644 --- a/standards/versioning.md +++ b/standards/versioning.md @@ -72,12 +72,24 @@ The release workflow auto-generates release notes grouped by commit type: `CHANGELOG.md` is maintained manually for curated, human-readable release history. It is not auto-generated. -## What a MINOR bump means for ecosystem standards +## Two version files in the meta-repo -The meta-repo's `VERSION` file carries the ecosystem-wide standards version. It follows the same SemVer rules, but each component has a second, standards-specific meaning for tool repos that embed a `standards-version` signal in their agent files. +The meta-repo root contains two separate version files that serve distinct purposes and move independently. -- **MAJOR** (e.g., `1.x.y` → `2.0.0`) — an incompatible change to the standards themselves. New required elements, removed fields, or restructured file conventions that existing tool repos will fail to validate against without re-alignment. -- **MINOR** (e.g., `1.6.x` → `1.7.0`) — ecosystem standards changed in a way that tool repos need to re-align with. Typical triggers: new required elements in agent files, changed frontmatter schemas, new required standards references, restructured validation rules, or new checks in the drift checker that introduce findings for existing tool-repo content. A mechanical rollout session across the tool repos is typically scheduled after a MINOR bump. -- **PATCH** (e.g., `1.7.0` → `1.7.1`) — bug fixes, clarifications, or additions that do not change the standards surface. Tool repos with a PATCH-behind signal are reported as `info` by the drift checker — visible in verbose runs but not blocking CI. +### `VERSION` - meta-repo release version -The drift checker enforces this mapping via the `same-major-minor` signal policy (see `standards/drift-checker.config.json`). Tool repos whose `standards-version` differs from the meta-repo's `VERSION` in MAJOR or MINOR are reported as `error`; PATCH differences are `info`; tool values ahead of meta are `warn` (either an in-flight rollout or a missed meta-repo bump, both worth surfacing). +Tracks the release history of the meta-repo itself (registry additions, scaffold changes, doc updates, new CI workflows). Bumped by `release.yml` on every qualifying push to `main` using the same conventional-commit rules as tool repos. Registry additions (`feat:`) force a MINOR bump here. This number appears in GitHub Releases and the release changelog but has no direct meaning to tool repos. + +### `STANDARDS_VERSION` - ecosystem standards surface version + +Tracks the version of the standards that tool repos are expected to comply with. Tool repos embed this value in their `` markers in CLAUDE.md, AGENTS.md, and ROADMAP.md. The drift checker compares each tool repo's embedded marker against this file, not against `VERSION`. + +`STANDARDS_VERSION` moves **only** when the standards surface actually changes in a way that requires tool repos to update: + +- **MAJOR** (e.g., `1.x.y` to `2.0.0`) - an incompatible change to the standards themselves. New required elements, removed fields, or restructured file conventions that existing tool repos will fail to validate against without re-alignment. +- **MINOR** (e.g., `1.6.x` to `1.7.0`) - standards changed in a way that tool repos need to re-align with. Typical triggers: new required elements in agent files, changed frontmatter schemas, new required standards references, restructured validation rules, or new checks in the drift checker that introduce findings for existing tool-repo content. A fleet-wide re-stamp session is typically scheduled after a MINOR bump. +- **PATCH** (e.g., `1.7.0` to `1.7.1`) - clarifications or additions that do not change the standards surface. Tool repos with a PATCH-behind marker are reported as `info` by the drift checker - visible in verbose runs but not blocking CI. + +Registry-only changes, scaffold improvements, docs additions, and other meta-repo work that does not change what tool repos are required to contain **do not** bump `STANDARDS_VERSION`, even if they bump `VERSION`. + +The drift checker enforces this mapping via the `same-major-minor` signal policy (see `standards/drift-checker.config.json`). Tool repos whose `standards-version` marker differs from `STANDARDS_VERSION` in MAJOR or MINOR are reported as `error`; PATCH differences are `info`; tool values ahead of meta are `warn`. diff --git a/tests/test_cli.py b/tests/test_cli.py index a8d58ad..2e61965 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -9,11 +9,11 @@ @pytest.fixture def meta_repo(tmp_path: Path) -> Path: - """Isolated meta-repo for CLI tests. Pins meta VERSION to 1.6.3 so that + """Isolated meta-repo for CLI tests. Pins STANDARDS_VERSION to 1.6.3 so that the on-disk fixtures (which still carry 1.6.3 signals) read as clean.""" root = tmp_path / "meta" root.mkdir() - (root / "VERSION").write_text("1.6.3", encoding="utf-8") + (root / "STANDARDS_VERSION").write_text("1.6.3", encoding="utf-8") (root / "standards").mkdir() (root / "standards" / "required-refs.json").write_text( '{"version": 1, "requirements": {"cursor-plugin": {}, "mcp-server": {}}}', diff --git a/tests/test_cli_remote_flags.py b/tests/test_cli_remote_flags.py index c1834a6..26325ad 100644 --- a/tests/test_cli_remote_flags.py +++ b/tests/test_cli_remote_flags.py @@ -16,7 +16,7 @@ def meta_repo(tmp_path: Path) -> Path: root = tmp_path / "meta" root.mkdir() - (root / "VERSION").write_text("1.6.3", encoding="utf-8") + (root / "STANDARDS_VERSION").write_text("1.6.3", encoding="utf-8") (root / "standards").mkdir() (root / "standards" / "required-refs.json").write_text( '{"version": 1, "requirements": {"cursor-plugin": {}, "mcp-server": {}}}', @@ -48,7 +48,7 @@ def test_remote_without_token_errors(capsys, meta_repo: Path, monkeypatch): def test_all_without_registry_errors(capsys, tmp_path: Path): bare = tmp_path / "bare-meta" bare.mkdir() - (bare / "VERSION").write_text("1.6.3", encoding="utf-8") + (bare / "STANDARDS_VERSION").write_text("1.6.3", encoding="utf-8") rc = cli.main([ "--all", "--meta-repo", str(bare),