Skip to content

Guard release version consistency and name the process in Accessibility errors - #33

Merged
Jeomon merged 1 commit into
mainfrom
ci/version-consistency-guard
Aug 1, 2026
Merged

Guard release version consistency and name the process in Accessibility errors#33
Jeomon merged 1 commit into
mainfrom
ci/version-consistency-guard

Conversation

@Jeomon

@Jeomon Jeomon commented Aug 1, 2026

Copy link
Copy Markdown
Member

Two follow-ups to #32. v0.3.12 already resynced the version strings; this addresses the parts that made the issue hard to diagnose and easy to reintroduce.

Guard against version drift

The project declares its version in five files, each consumed by a different channel:

file channel
pyproject.toml PyPI
uv.lock locked workspace member
manifest.json Claude Desktop extension
package.json Pi Agent extension
server.json MCP registry (twice)

The 0.3.9, 0.3.10 and 0.3.11 release commits each touched only pyproject.toml (plus uv.lock/CHANGELOG), so the shipped metadata was left at 0.3.8, 0.3.5 and 0.3.6. Because manifest.json is what the extension is published from, the extension kept advertising and installing 0.3.8 — which predates the 0.3.10 AXIsProcessTrustedWithOptions startup prompt. Affected users could never reach the version that actually requests Accessibility access, and nothing in CI noticed for three releases.

scripts/check_versions.py compares all six version strings against each other and against the release tag. publish.yml runs it before uv build, so a mismatch fails the release before anything reaches PyPI.

$ python scripts/check_versions.py v0.3.12
  ok   pyproject.toml:project.version   0.3.12
  ok   uv.lock:macos-mcp                0.3.12
  ok   manifest.json:version            0.3.12
  ok   package.json:version             0.3.12
  ok   server.json:version              0.3.12
  ok   server.json:packages[0].version  0.3.12

All 6 version strings agree and match 0.3.12.

The regression test reconstructs the exact 0.3.11 tree and asserts the guard would have blocked that release.

Name the process in permission failures

Missing permissions: Accessibility.
Required permissions not granted.

This gave the user no target. The MCP host is already in the Accessibility list and toggled on, nothing called macos-mcp ever appears there, and the interpreter can't be added through the + picker — so the instruction was unfollowable, which is most of what made #32 painful.

The message now names sys.executable, points at the native "would like to control this computer" dialog as the reliable fix, and warns against adding the binary by hand (greyed out for uv-managed Python, and breaks on the next uv update). Screen-Recording-only failures are unchanged, since they carry no such ambiguity.

Testing

187 passed (171 before, +16). New coverage for version extraction, drift detection, tag matching, exit codes, and the guidance text.

Refs #32

…ion errors

Two follow-ups to #32, both aimed at the parts that made it hard to
diagnose and easy to reintroduce.

Guard against version drift. The project declares its version in five
files, each consumed by a different channel: pyproject.toml (PyPI),
uv.lock, manifest.json (Claude Desktop extension), package.json (Pi
extension) and server.json (MCP registry, twice). The 0.3.9, 0.3.10 and
0.3.11 releases bumped only pyproject.toml, so the extension kept
shipping 0.3.8 -- predating the 0.3.10 AXIsProcessTrustedWithOptions
prompt -- and nothing in CI noticed. scripts/check_versions.py compares
all six strings against each other and against the release tag, and
publish.yml runs it before `uv build`, so a mismatch now fails the
release before anything reaches PyPI. Its regression test reconstructs
the 0.3.11 tree and asserts the guard would have blocked that release.

Name the process in permission failures. "Required permissions not
granted: Accessibility" gave the user no target: the MCP host is already
granted, nothing called "macos-mcp" ever appears in the Accessibility
list, and the interpreter cannot be added through the "+" picker. The
message now names sys.executable, points at the native "would like to
control this computer" dialog as the reliable fix, and warns against
adding the binary by hand. Screen-Recording-only failures are unchanged,
since they carry no such ambiguity.

Refs #32
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

CI: Guard release version drift and clarify Accessibility permission errors

⚙️ Configuration changes 🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Add release CI step to validate versions across pyproject, lock, and extension metadata.
• Add scripts/check_versions.py and tests to block tag mismatches and drift.
• Include sys.executable in Accessibility failures and keep Screen Recording messaging unchanged.
Diagram

graph TD
  subgraph Release["Release pipeline"]
    direction TD
    A["Git tag (vX.Y.Z)"] --> B["publish.yml"] --> C["scripts/check_versions.py"] --> D["uv build"] --> E["PyPI + extension metadata"]
  end
  subgraph Runtime["Permission error path"]
    direction TD
    F["macos-mcp server"] --> G["validate_permissions"] -->|"Accessibility missing"| H["accessibility_guidance"]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Single source of truth + generated metadata
  • ➕ Eliminates multi-file drift by generating manifest/package/server versions from pyproject at build time
  • ➕ Reduces ongoing maintenance and the need to remember five separate bumps
  • ➖ Requires additional tooling/templating and changes to current publishing flow
  • ➖ Harder to keep generated files reviewable if they are committed artifacts
2. Release-time auto-bump script (instead of only a guard)
  • ➕ Actively updates all version fields and reduces human error
  • ➕ Can update CHANGELOG and tags in one command for maintainers
  • ➖ More moving parts and higher risk of an incorrect automated edit
  • ➖ Still needs guardrails/tests to ensure the script itself stays correct
3. Use setuptools-scm (or similar) for dynamic versioning
  • ➕ Version derived from tags, reducing manual version updates
  • ➕ Works well for PyPI builds when properly configured
  • ➖ Does not automatically solve non-Python metadata files unless they also consume the derived version
  • ➖ May complicate extension/registry packaging workflows that expect explicit JSON versions

Recommendation: The current approach (a hard CI guard plus regression tests) is the right near-term fix: it prevents publishing inconsistent artifacts without reshaping the release pipeline. Consider a follow-up to reduce the number of manually maintained version fields (generation or an auto-bump tool), but keep the guard either way as a backstop.

Files changed (6) +389 / -1

Bug fix (1) +30 / -1
permissions.pyInclude interpreter identity in Accessibility permission failures +30/-1

Include interpreter identity in Accessibility permission failures

• Adds accessibility_guidance() to explain that Accessibility permission must be granted to the running interpreter (sys.executable), not the host app. Appends this guidance to warnings/errors only when Accessibility is missing, leaving Screen Recording-only failures unchanged.

src/macos_mcp/permissions.py

Tests (2) +205 / -0
test_check_versions.pyAdd unit coverage for version extraction and drift detection +148/-0

Add unit coverage for version extraction and drift detection

• Adds tests for version collection, error handling when metadata fields are missing, tag matching (including v-prefix), and exit-code behavior. Includes a regression test that reconstructs the historical 0.3.11 drift scenario and asserts it would be rejected.

tests/test_check_versions.py

test_permissions.pyPin Accessibility guidance content and inclusion behavior +57/-0

Pin Accessibility guidance content and inclusion behavior

• Adds unit tests ensuring the guidance names sys.executable, references the native consent dialog, and warns against manual System Settings picker entry. Verifies guidance is included in fatal messages when Accessibility is missing and omitted for Screen Recording-only failures.

tests/test_permissions.py

Documentation (1) +6 / -0
CHANGELOG.mdDocument version-consistency guard and improved Accessibility guidance +6/-0

Document version-consistency guard and improved Accessibility guidance

• Updates the Unreleased section to describe the new release version-drift guard and the more actionable Accessibility permission error messaging. Links the motivation back to the #32 incident.

CHANGELOG.md

Other (2) +148 / -0
publish.ymlFail releases when version strings drift from the tag +8/-0

Fail releases when version strings drift from the tag

• Adds a pre-build step that runs scripts/check_versions.py with the pushed tag name. This blocks publishing if any version-bearing file disagrees with the release tag.

.github/workflows/publish.yml

check_versions.pyAdd version-consistency checker across all distribution metadata +140/-0

Add version-consistency checker across all distribution metadata

• Introduces a standalone script that extracts versions from pyproject.toml, uv.lock, manifest.json, package.json, and server.json (including per-package entries). It validates internal consistency and optionally enforces a match to a provided v-prefixed tag, emitting clear output and non-zero exit codes on failure.

scripts/check_versions.py

@Jeomon
Jeomon merged commit 2f38593 into main Aug 1, 2026
@Jeomon
Jeomon deleted the ci/version-consistency-guard branch August 1, 2026 06:11
@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (4) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 9 rules

Grey Divider


Remediation recommended

1. Changelog lines exceed 100 📘 Rule violation ✧ Quality
Description
New CHANGELOG entries are written as single very long lines, exceeding the 100-character maximum and
reducing readability in common diff/review views.
Code

CHANGELOG.md[R11-15]

+- Release builds now verify that every declared version string agrees before publishing. `scripts/check_versions.py` compares `pyproject.toml`, `uv.lock`, `manifest.json`, `package.json` and `server.json` (twice) against each other and against the release tag, and `publish.yml` runs it ahead of the build so a mismatched tag fails before anything reaches PyPI — the drift that caused #32 would have been blocked at 0.3.9
+
+### Changed
+- Accessibility permission failures now name the process that needs the grant (`sys.executable`) instead of just the permission. The message also points at the native "would like to control this computer" consent dialog as the reliable fix, and warns against adding the interpreter by hand in the System Settings "+" picker, which is greyed out for uv-managed Python and breaks on the next uv update (#32)
+
Evidence
PR Compliance ID 289678 requires that changed lines be wrapped to keep each line <= 100 characters.
The newly added CHANGELOG bullets are single lines that far exceed this limit.

Rule 289678: Enforce 100-Character Maximum Line Length
CHANGELOG.md[11-15]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New CHANGELOG entries exceed the 100-character maximum line length.

## Issue Context
Compliance requires a max line length of 100 unless explicitly justified.

## Fix Focus Areas
- CHANGELOG.md[11-15]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Single quotes used in Python 📘 Rule violation ✧ Quality
Description
New Python code introduces single-quoted string literals, violating the required double-quote string
literal style and creating inconsistent quoting in the changed modules.
Code

scripts/check_versions.py[R37-40]

+_UV_LOCK_MACOS_MCP = re.compile(
+    r'^\[\[package\]\]\nname = "macos-mcp"\nversion = "([^"]+)"',
+    re.MULTILINE,
+)
Evidence
PR Compliance ID 289679 requires double quotes for string literals. The PR adds single-quoted
literals in scripts/check_versions.py (raw regex pattern and inline literals) and in
tests/test_check_versions.py (a single-quoted multi-line literal passed to write_text).

Rule 289679: Use double quotes for all string literals
scripts/check_versions.py[37-40]
scripts/check_versions.py[120-120]
tests/test_check_versions.py[86-86]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Changed/new Python code uses single-quoted string literals where double quotes are required.

## Issue Context
The compliance rule requires double quotes for all string literals in languages where both are supported (including Python).

## Fix Focus Areas
- scripts/check_versions.py[37-40]
- scripts/check_versions.py[120-120]
- tests/test_check_versions.py[86-86]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Tests missing type annotations 📘 Rule violation ✧ Quality
Description
New test/helper functions are introduced without parameter and/or return type annotations, which
violates the requirement to type all function signatures in changed code.
Code

tests/test_check_versions.py[R30-47]

+def _load_module():
+    """Load scripts/check_versions.py, which is not an installed package."""
+    path = REPO_ROOT / "scripts" / "check_versions.py"
+    spec = importlib.util.spec_from_file_location("check_versions", path)
+    module = importlib.util.module_from_spec(spec)
+    spec.loader.exec_module(module)
+    return module
+
+
+check_versions = _load_module()
+
+
+@pytest.fixture
+def repo(tmp_path):
+    """A throwaway copy of the repo's version-bearing files."""
+    for filename in VERSIONED_FILES:
+        shutil.copy(REPO_ROOT / filename, tmp_path / filename)
+    return tmp_path
Evidence
PR Compliance ID 289681 requires type hints on all function parameters and return values in
new/modified code. The PR adds functions such as _load_module() and the repo fixture without
return type annotations (and test methods are also unannotated).

Rule 289681: Require type hints on all function parameters and return values
tests/test_check_versions.py[30-47]
tests/test_check_versions.py[62-65]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New/modified function definitions lack type hints on parameters and/or return values.

## Issue Context
The compliance rule requires explicit type annotations for all function parameters and return values in changed code.

## Fix Focus Areas
- tests/test_check_versions.py[30-47]
- tests/test_check_versions.py[62-65]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (2)
4. Public functions lack structured docstrings 📘 Rule violation ✧ Quality
Description
New public functions have docstrings that omit required structured sections (e.g., Args: /
Returns:) or lack docstrings altogether, making the API harder to understand and maintain.
Code

scripts/check_versions.py[R43-49]

+def collect_versions(root: Path = REPO_ROOT) -> dict[str, str]:
+    """Extract every declared version string, keyed by a human-readable label.
+
+    Raises:
+        ValueError: if a file is missing the version field entirely, which is
+            just as much a packaging bug as a stale value.
+    """
Evidence
PR Compliance ID 289682 requires structured docstrings for public functions, including Args: and
Returns: (and Raises: when applicable). The new collect_versions() docstring documents
Raises: but does not document its argument (root) or its return value, and check()/main()
are also missing the required structured sections.

Rule 289682: Require structured docstrings for public functions
scripts/check_versions.py[43-49]
scripts/check_versions.py[82-84]
scripts/check_versions.py[102-104]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Public functions added/modified in this PR are missing required structured docstring sections (e.g., `Args:` and `Returns:`), or are missing docstrings.

## Issue Context
Compliance requires structured docstrings for public functions.

## Fix Focus Areas
- scripts/check_versions.py[43-49]
- scripts/check_versions.py[82-84]
- scripts/check_versions.py[102-104]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Brittle uv.lock version parse 🐞 Bug ☼ Reliability
Description
scripts/check_versions.py extracts the macos-mcp version from uv.lock using a regex that requires an
exact, adjacent-line layout for name/version. If uv.lock generation changes key ordering/format
(still valid TOML), the release workflow can fail even when the lockfile correctly contains the
macos-mcp version.
Code

scripts/check_versions.py[R37-62]

+_UV_LOCK_MACOS_MCP = re.compile(
+    r'^\[\[package\]\]\nname = "macos-mcp"\nversion = "([^"]+)"',
+    re.MULTILINE,
+)
+
+
+def collect_versions(root: Path = REPO_ROOT) -> dict[str, str]:
+    """Extract every declared version string, keyed by a human-readable label.
+
+    Raises:
+        ValueError: if a file is missing the version field entirely, which is
+            just as much a packaging bug as a stale value.
+    """
+    versions: dict[str, str] = {}
+
+    pyproject = tomllib.loads((root / "pyproject.toml").read_text())
+    try:
+        versions["pyproject.toml:project.version"] = pyproject["project"]["version"]
+    except KeyError as exc:
+        raise ValueError("pyproject.toml is missing [project] version") from exc
+
+    lock_text = (root / "uv.lock").read_text()
+    match = _UV_LOCK_MACOS_MCP.search(lock_text)
+    if match is None:
+        raise ValueError("uv.lock has no [[package]] entry for macos-mcp")
+    versions["uv.lock:macos-mcp"] = match.group(1)
Evidence
The regex explicitly requires [[package]] followed immediately by name = "macos-mcp" and then
immediately by version = ..., which is a formatting assumption rather than a semantic requirement.
The actual uv.lock is TOML with additional per-package fields (e.g., source, dependencies), so
structured parsing via tomllib would be resilient to ordering/format changes.

scripts/check_versions.py[37-62]
uv.lock[1341-1346]
uv.lock[1-16]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`scripts/check_versions.py` currently uses a strict regex to find the `macos-mcp` package version in `uv.lock`. This approach is brittle because `uv.lock` is TOML and key ordering/formatting can legitimately change, which would cause false failures in the release guard.

## Issue Context
The guard is intended to prevent version drift. A spurious failure due to lockfile formatting changes would block publishing and create unnecessary release friction.

## Fix Focus Areas
- scripts/check_versions.py[37-63]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

6. Uncaught second metadata read 🐞 Bug ☼ Reliability
Description
main() calls collect_versions() inside a try/except, but then calls check() which calls
collect_versions() again outside that handler. If the second parse/read fails (OSError/JSON/TOML
errors), the script can exit with a traceback instead of returning code 1 with a clear error
message.
Code

scripts/check_versions.py[R102-113]

+def main(argv: list[str]) -> int:
+    expected = argv[1] if len(argv) > 1 else None
+
+    try:
+        versions = collect_versions()
+    except (OSError, ValueError, json.JSONDecodeError, tomllib.TOMLDecodeError) as exc:
+        print(f"error: could not read version metadata: {exc}", file=sys.stderr)
+        return 1
+
+    problems = check(expected)
+    width = max(len(label) for label in versions)
+    target = expected.removeprefix("v") if expected else None
Evidence
check() always calls collect_versions(root) internally, and main() calls collect_versions()
before invoking check(expected); the exception handler in main() only covers the first call.
This creates an unnecessary second read and a small unhandled-exception window.

scripts/check_versions.py[82-85]
scripts/check_versions.py[105-113]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`main()` already reads/parses all version files via `collect_versions()`, but then calls `check()` which re-reads them and can raise exceptions outside `main()`'s guarded block.

## Issue Context
This is low-likelihood in CI, but when it does happen it degrades diagnostics (traceback) and makes exit semantics less predictable.

## Fix Focus Areas
- scripts/check_versions.py[82-99]
- scripts/check_versions.py[102-113]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread CHANGELOG.md
Comment on lines +11 to +15
- Release builds now verify that every declared version string agrees before publishing. `scripts/check_versions.py` compares `pyproject.toml`, `uv.lock`, `manifest.json`, `package.json` and `server.json` (twice) against each other and against the release tag, and `publish.yml` runs it ahead of the build so a mismatched tag fails before anything reaches PyPI — the drift that caused #32 would have been blocked at 0.3.9

### Changed
- Accessibility permission failures now name the process that needs the grant (`sys.executable`) instead of just the permission. The message also points at the native "would like to control this computer" consent dialog as the reliable fix, and warns against adding the interpreter by hand in the System Settings "+" picker, which is greyed out for uv-managed Python and breaks on the next uv update (#32)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

1. Changelog lines exceed 100 📘 Rule violation ✧ Quality

New CHANGELOG entries are written as single very long lines, exceeding the 100-character maximum and
reducing readability in common diff/review views.
Agent Prompt
## Issue description
New CHANGELOG entries exceed the 100-character maximum line length.

## Issue Context
Compliance requires a max line length of 100 unless explicitly justified.

## Fix Focus Areas
- CHANGELOG.md[11-15]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread scripts/check_versions.py
Comment on lines +37 to +40
_UV_LOCK_MACOS_MCP = re.compile(
r'^\[\[package\]\]\nname = "macos-mcp"\nversion = "([^"]+)"',
re.MULTILINE,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

2. Single quotes used in python 📘 Rule violation ✧ Quality

New Python code introduces single-quoted string literals, violating the required double-quote string
literal style and creating inconsistent quoting in the changed modules.
Agent Prompt
## Issue description
Changed/new Python code uses single-quoted string literals where double quotes are required.

## Issue Context
The compliance rule requires double quotes for all string literals in languages where both are supported (including Python).

## Fix Focus Areas
- scripts/check_versions.py[37-40]
- scripts/check_versions.py[120-120]
- tests/test_check_versions.py[86-86]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +30 to +47
def _load_module():
"""Load scripts/check_versions.py, which is not an installed package."""
path = REPO_ROOT / "scripts" / "check_versions.py"
spec = importlib.util.spec_from_file_location("check_versions", path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module


check_versions = _load_module()


@pytest.fixture
def repo(tmp_path):
"""A throwaway copy of the repo's version-bearing files."""
for filename in VERSIONED_FILES:
shutil.copy(REPO_ROOT / filename, tmp_path / filename)
return tmp_path

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Tests missing type annotations 📘 Rule violation ✧ Quality

New test/helper functions are introduced without parameter and/or return type annotations, which
violates the requirement to type all function signatures in changed code.
Agent Prompt
## Issue description
New/modified function definitions lack type hints on parameters and/or return values.

## Issue Context
The compliance rule requires explicit type annotations for all function parameters and return values in changed code.

## Fix Focus Areas
- tests/test_check_versions.py[30-47]
- tests/test_check_versions.py[62-65]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread scripts/check_versions.py
Comment on lines +43 to +49
def collect_versions(root: Path = REPO_ROOT) -> dict[str, str]:
"""Extract every declared version string, keyed by a human-readable label.

Raises:
ValueError: if a file is missing the version field entirely, which is
just as much a packaging bug as a stale value.
"""

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

4. Public functions lack structured docstrings 📘 Rule violation ✧ Quality

New public functions have docstrings that omit required structured sections (e.g., Args: /
Returns:) or lack docstrings altogether, making the API harder to understand and maintain.
Agent Prompt
## Issue description
Public functions added/modified in this PR are missing required structured docstring sections (e.g., `Args:` and `Returns:`), or are missing docstrings.

## Issue Context
Compliance requires structured docstrings for public functions.

## Fix Focus Areas
- scripts/check_versions.py[43-49]
- scripts/check_versions.py[82-84]
- scripts/check_versions.py[102-104]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread scripts/check_versions.py
Comment on lines +37 to +62
_UV_LOCK_MACOS_MCP = re.compile(
r'^\[\[package\]\]\nname = "macos-mcp"\nversion = "([^"]+)"',
re.MULTILINE,
)


def collect_versions(root: Path = REPO_ROOT) -> dict[str, str]:
"""Extract every declared version string, keyed by a human-readable label.

Raises:
ValueError: if a file is missing the version field entirely, which is
just as much a packaging bug as a stale value.
"""
versions: dict[str, str] = {}

pyproject = tomllib.loads((root / "pyproject.toml").read_text())
try:
versions["pyproject.toml:project.version"] = pyproject["project"]["version"]
except KeyError as exc:
raise ValueError("pyproject.toml is missing [project] version") from exc

lock_text = (root / "uv.lock").read_text()
match = _UV_LOCK_MACOS_MCP.search(lock_text)
if match is None:
raise ValueError("uv.lock has no [[package]] entry for macos-mcp")
versions["uv.lock:macos-mcp"] = match.group(1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

5. Brittle uv.lock version parse 🐞 Bug ☼ Reliability

scripts/check_versions.py extracts the macos-mcp version from uv.lock using a regex that requires an
exact, adjacent-line layout for name/version. If uv.lock generation changes key ordering/format
(still valid TOML), the release workflow can fail even when the lockfile correctly contains the
macos-mcp version.
Agent Prompt
## Issue description
`scripts/check_versions.py` currently uses a strict regex to find the `macos-mcp` package version in `uv.lock`. This approach is brittle because `uv.lock` is TOML and key ordering/formatting can legitimately change, which would cause false failures in the release guard.

## Issue Context
The guard is intended to prevent version drift. A spurious failure due to lockfile formatting changes would block publishing and create unnecessary release friction.

## Fix Focus Areas
- scripts/check_versions.py[37-63]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread scripts/check_versions.py
Comment on lines +102 to +113
def main(argv: list[str]) -> int:
expected = argv[1] if len(argv) > 1 else None

try:
versions = collect_versions()
except (OSError, ValueError, json.JSONDecodeError, tomllib.TOMLDecodeError) as exc:
print(f"error: could not read version metadata: {exc}", file=sys.stderr)
return 1

problems = check(expected)
width = max(len(label) for label in versions)
target = expected.removeprefix("v") if expected else None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Informational

6. Uncaught second metadata read 🐞 Bug ☼ Reliability

main() calls collect_versions() inside a try/except, but then calls check() which calls
collect_versions() again outside that handler. If the second parse/read fails (OSError/JSON/TOML
errors), the script can exit with a traceback instead of returning code 1 with a clear error
message.
Agent Prompt
## Issue description
`main()` already reads/parses all version files via `collect_versions()`, but then calls `check()` which re-reads them and can raise exceptions outside `main()`'s guarded block.

## Issue Context
This is low-likelihood in CI, but when it does happen it degrades diagnostics (traceback) and makes exit semantics less predictable.

## Fix Focus Areas
- scripts/check_versions.py[82-99]
- scripts/check_versions.py[102-113]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant