From f596fd6c898385023e13279fbc91520cedb4bac3 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Sat, 22 Aug 2026 14:41:58 -0700 Subject: [PATCH 1/2] Stop the 3.7 Bootstrap Probes From Reading as This Repo's Python Floor (#920) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The repo's Python floor is 3.13 (`spec/host-tools.json`'s `python3` target, `pyproject.toml`'s `target-version`/`python_version`), but that wasn't visible from every place a reader could hit a lower number first, which invites review comments that treat ordinary 3.10+ syntax (`X | None`, etc.) as needing pre-3.10 hedging. - `repo-config/configure.sh`: its `resolve_description.py` probe accepts 3.7+ (PEP 563), which is real but was worded as if it were this repo's floor rather than one script's parse minimum. Reworded the comment and the failure message to name 3.13 as the actual floor. - `spec/host-tools.json`: cross-referenced `configure.sh` and the `skills_install.sh` / `install-skills.*` bootstrap scripts from the `python3` entry's `why`, so a reader who lands on either script's 3.7 language finds the real floor next to it instead of a second number. - `python-codestyle` skill (`.agents/skills/python-codestyle/references/code-style.md`, the hand-authored source; `.github/skills/` and `.claude-plugin/fleet-skills/` regenerated via `scripts/build_dist.py`): the Type hints section already says to use modern syntax, but didn't say a 3.13 floor is why no hedging is needed. Added that, naming the two deliberate bootstrap exceptions. - `.github/copilot-instructions.md`: deleted a `Disproved Claims` entry that had gone stale. It was proved against `install.py`'s interpreter floor at 3.7 (`dbd1cdc`); #644 raised that floor to 3.11 without updating this entry, so it now asserts something about `install.py` that isn't true anymore. Its own governing rule says a stale-subject entry is deleted rather than patched to look current. Verified locally: `scripts/prose_lint.py` clean, `scripts/build_dist.py --check` clean, `scripts/repo_gate.py` clean, `pytest scripts/tests/test_host_gate.py scripts/tests/test_prose_lint.py scripts/tests/test_repo_gate.py` all pass (392 passed). 🤖 Generated with [Claude Code](https://claude.com/claude-code) ## Summary by CodeRabbit - **Documentation** - Clarified Python version support, including Python 3.13 as the primary target and Python 3.7+ compatibility for selected bootstrap scripts. - Updated type-hinting guidance to explain when compatibility annotations are required. - Expanded setup and host-tool documentation around interpreter requirements. - Removed outdated, disproven guidance. --------- Co-authored-by: Claude Sonnet 5 --- .../python-codestyle/references/code-style.md | 13 +++++++++++++ .claude-plugin/fleet-skills/.source-digest | 2 +- .../python-codestyle/references/code-style.md | 13 +++++++++++++ .github/copilot-instructions.md | 6 ------ .../python-codestyle/references/code-style.md | 13 +++++++++++++ repo-config/configure.sh | 6 ++++-- spec/host-tools.json | 2 +- 7 files changed, 45 insertions(+), 10 deletions(-) diff --git a/.agents/skills/python-codestyle/references/code-style.md b/.agents/skills/python-codestyle/references/code-style.md index b7e62b00..9ad17f29 100644 --- a/.agents/skills/python-codestyle/references/code-style.md +++ b/.agents/skills/python-codestyle/references/code-style.md @@ -44,6 +44,19 @@ - **Use modern syntax**: `list[int]` not `List[int]`, `dict[str, X]` not `Dict[str, X]`, `X | None` not `Optional[X]`, `from __future__ import annotations` only when needed for forward references. +- **Don't hedge that syntax for an older interpreter.** `pyproject.toml` pins `target-version` / + `python_version` to 3.13 for every Python profile in this repo, and `spec/host-tools.json` + carries that as the host floor `scripts/host_gate.py` enforces, so 3.10+-only syntax (`X | None`, + `match`, etc.) needs no quoting, no `typing.Union` fallback, and no `from __future__ import + annotations` guard on that account alone. Add that import only when a real forward reference + needs it, per the bullet above. Three named exceptions carry a lower floor on purpose and say so + themselves: `scripts/skills_install.sh` and the `install-skills.*` bootstrap scripts, which must + run on whatever interpreter a host already has before this floor's toolchain exists to install + one, and `spec/resolve_description.py`, which `repo-config/configure.sh`'s own bootstrap probe + accepts down to 3.7 for the same reason, and which carries `from __future__ import annotations` + for exactly that purpose rather than out of habit. No other `spec/` code has a reason to hedge, + so that import or a quoted annotation appearing anywhere else in `spec/` is a sign this one + exception got copied rather than a pattern to follow. - **Don't add `# type: ignore` to silence pyright errors without a comment** explaining the constraint. If a recurring false positive needs suppression, configure it project-wide in `[tool.pyright]`. A new port doesn't change this, fix freshly surfaced type errors rather than diff --git a/.claude-plugin/fleet-skills/.source-digest b/.claude-plugin/fleet-skills/.source-digest index 31e3ed9d..8e8ae0ac 100644 --- a/.claude-plugin/fleet-skills/.source-digest +++ b/.claude-plugin/fleet-skills/.source-digest @@ -1 +1 @@ -716018234e006002 +598e6a55149a7fde diff --git a/.claude-plugin/fleet-skills/skills/python-codestyle/references/code-style.md b/.claude-plugin/fleet-skills/skills/python-codestyle/references/code-style.md index b7e62b00..9ad17f29 100644 --- a/.claude-plugin/fleet-skills/skills/python-codestyle/references/code-style.md +++ b/.claude-plugin/fleet-skills/skills/python-codestyle/references/code-style.md @@ -44,6 +44,19 @@ - **Use modern syntax**: `list[int]` not `List[int]`, `dict[str, X]` not `Dict[str, X]`, `X | None` not `Optional[X]`, `from __future__ import annotations` only when needed for forward references. +- **Don't hedge that syntax for an older interpreter.** `pyproject.toml` pins `target-version` / + `python_version` to 3.13 for every Python profile in this repo, and `spec/host-tools.json` + carries that as the host floor `scripts/host_gate.py` enforces, so 3.10+-only syntax (`X | None`, + `match`, etc.) needs no quoting, no `typing.Union` fallback, and no `from __future__ import + annotations` guard on that account alone. Add that import only when a real forward reference + needs it, per the bullet above. Three named exceptions carry a lower floor on purpose and say so + themselves: `scripts/skills_install.sh` and the `install-skills.*` bootstrap scripts, which must + run on whatever interpreter a host already has before this floor's toolchain exists to install + one, and `spec/resolve_description.py`, which `repo-config/configure.sh`'s own bootstrap probe + accepts down to 3.7 for the same reason, and which carries `from __future__ import annotations` + for exactly that purpose rather than out of habit. No other `spec/` code has a reason to hedge, + so that import or a quoted annotation appearing anywhere else in `spec/` is a sign this one + exception got copied rather than a pattern to follow. - **Don't add `# type: ignore` to silence pyright errors without a comment** explaining the constraint. If a recurring false positive needs suppression, configure it project-wide in `[tool.pyright]`. A new port doesn't change this, fix freshly surfaced type errors rather than diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 8febbd7f..b39ce5c4 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -65,12 +65,6 @@ Review effort is user-controlled. The automation observes `Lite`, `Balanced`, or - **Proved against** - the `norm` filter in `repo-config/configure.sh` on `develop` at `756a53e`. - **Delete when** - the filter stops calling `keys_unsorted`, or nothing this check runs on carries a jq older than 1.6. -- **Splitting the fallback parse in `host-setup/agent-safety/gh-write-guard.py` a line at a time mis-reads a newline inside a quoted argument, reintroducing the false deny that path exists to remove.** Raised against the branch that made a newline end a command, on the ground that a `--body` argument holding a newline and a `git push origin develop` would have that line read as a push. - - **Disproved by** - the arm being unreachable, and then by measuring it rather than resting on that. `punctuation_chars` arrived in Python 3.6, the module uses f-strings throughout, and `install.py` refuses to install below 3.7, so an interpreter that would raise the `TypeError` fails to import the module before reaching the fallback. Simulated against a `shlex` that rejects the keyword and passes everything else through, the quoted-newline example is allowed on both paths, because splitting a line whose quoting cannot be parsed leaves the quote glued to the token and the push target reads as `develop"`, matching no branch. The shape does bite one line further out, where a three-line body whose middle line is a bare `git push origin develop` denies on the forced path, and the alternative is worse where it counts: parsing the whole command at once keeps a quoted newline intact and drops every real one, so an ordinary push followed by a `gh pr create` denies under every interpreter rather than under none. - - **Proved against** - `_git_subcommand_arglists` in `host-setup/agent-safety/gh-write-guard.py` and the interpreter floor in `host-setup/agent-safety/install.py`, on `develop` at `dbd1cdc`. - - **Delete when** - the floor drops below 3.6, or the fallback stops splitting the command a line at a time. - - **Earned anyway** - a test case rather than a change. Only `ValueError` from unbalanced quoting reaches that path in practice and nothing covered it, so a finding wrong about its own reachability was right that the path was untested. - - **A description's stale commit claims are found by extracting the bare SHAs it quotes.** Not a reviewer's finding but the method this repository's own backlog specified for the `claims` check in `scripts/pr_review.py`, recorded here because a rejected method costs the same to re-propose as a declined finding costs to re-derive, and because a backlog has a place for a claim the tree contradicts and none for a method a measurement rejects. - **Disproved by** - running it over the 25 most recent merged pull requests, where it raised four references and all four were correct prose: a `develop` commit named as history, a SHA inside a pasted digest, and two commits in another repository written without a URL. Nothing in the shape of a bare SHA separates those from a claim, and separating them by meaning is the similarity heuristic [GOVERNANCE.md "Documentation Style Conventions"](../GOVERNANCE.md#documentation-style-conventions) rules out. A path arm measured on the same corpus is worse, flagging 54 of 215 backticked candidates, nearly all of them bare basenames and other repositories. - **Proved against** - the 25 most recent merged pull requests as of `develop` at `756a53e`, the corpus on which the anchored verb form that ships instead raises one reference, and that one true. diff --git a/.github/skills/python-codestyle/references/code-style.md b/.github/skills/python-codestyle/references/code-style.md index b7e62b00..9ad17f29 100644 --- a/.github/skills/python-codestyle/references/code-style.md +++ b/.github/skills/python-codestyle/references/code-style.md @@ -44,6 +44,19 @@ - **Use modern syntax**: `list[int]` not `List[int]`, `dict[str, X]` not `Dict[str, X]`, `X | None` not `Optional[X]`, `from __future__ import annotations` only when needed for forward references. +- **Don't hedge that syntax for an older interpreter.** `pyproject.toml` pins `target-version` / + `python_version` to 3.13 for every Python profile in this repo, and `spec/host-tools.json` + carries that as the host floor `scripts/host_gate.py` enforces, so 3.10+-only syntax (`X | None`, + `match`, etc.) needs no quoting, no `typing.Union` fallback, and no `from __future__ import + annotations` guard on that account alone. Add that import only when a real forward reference + needs it, per the bullet above. Three named exceptions carry a lower floor on purpose and say so + themselves: `scripts/skills_install.sh` and the `install-skills.*` bootstrap scripts, which must + run on whatever interpreter a host already has before this floor's toolchain exists to install + one, and `spec/resolve_description.py`, which `repo-config/configure.sh`'s own bootstrap probe + accepts down to 3.7 for the same reason, and which carries `from __future__ import annotations` + for exactly that purpose rather than out of habit. No other `spec/` code has a reason to hedge, + so that import or a quoted annotation appearing anywhere else in `spec/` is a sign this one + exception got copied rather than a pattern to follow. - **Don't add `# type: ignore` to silence pyright errors without a comment** explaining the constraint. If a recurring false positive needs suppression, configure it project-wide in `[tool.pyright]`. A new port doesn't change this, fix freshly surfaced type errors rather than diff --git a/repo-config/configure.sh b/repo-config/configure.sh index 5016f373..2af0f165 100755 --- a/repo-config/configure.sh +++ b/repo-config/configure.sh @@ -70,13 +70,15 @@ description="" if [ -f "$registry" ]; then # Resolved here, not near the top, so a run with no registry (an explicit model, no hub checkout) never needs Python at all. # The name python3 is not universal: native Windows can register a Microsoft Store stub under that name that resolves on PATH but fails when actually run, so this runs it rather than just checking PATH (docs/host-setup.md). - # The probe itself is spec/resolve_description.py's actual floor (PEP 563, Python 3.7+) rather than an arbitrary version number, so a too-old interpreter fails here with a clear message instead of a bare SyntaxError from the script. + # The probe checks 3.7+ (PEP 563, from __future__ import annotations) because that is the oldest interpreter resolve_description.py happens to parse and run on, not because 3.7 is this repo's supported floor. + # The repository floor is 3.13 (spec/host-tools.json's python3 target, enforced on the host by scripts/host_gate.py); resolve_description.py's own from __future__ import annotations is what lets it stay parseable below that, not a license for any other spec/ code to hedge for anything older. + # A too-old interpreter fails here with a clear message instead of a bare traceback from the script. if python3 -c "from __future__ import annotations" >/dev/null 2>&1; then py_cmd=(python3) elif py -3 -c "from __future__ import annotations" >/dev/null 2>&1; then py_cmd=(py -3) else - echo "No Python 3.7+ interpreter found (python3 or py -3). See docs/host-setup.md." >&2 + echo "No Python interpreter found (python3 or py -3) able to run resolve_description.py. This repo targets Python 3.13; see docs/host-setup.md." >&2 exit 1 fi # Delegates to spec/resolve_description.py rather than a third hand-rolled copy of description_errors(). diff --git a/spec/host-tools.json b/spec/host-tools.json index 6c8e310a..30356d3e 100644 --- a/spec/host-tools.json +++ b/spec/host-tools.json @@ -87,7 +87,7 @@ "probes": [["python3", "--version"], ["py", "-3", "--version"]], "pattern": "Python (\\d+(?:\\.\\d+)*)", "minimum": "3.13", - "why": "Every script here is standard library only, so a bare interpreter is enough and no package floor exists. The floor is the toolchain target rather than a measured breakage one version below it, which is the one entry here that reads that way and says so rather than implying a defect nobody found. pyproject.toml sets ruff target-version to py313 and mypy python_version to 3.13, so what those tools report describes 3.13 and describes no other interpreter, and a run below the floor is unverified rather than known broken. Both tools run in CI through uvx at their latest releases, beside the doc linters, the registry and spec validation, the script self-tests under coverage, and the repo and prose gates. That run judges the code rather than any host, so this floor is still a configuration choice rather than an enforced result, and a host failing it has no CI failure to point at. Two hard requirements are measured, both sit lower, and they fail differently. str.removeprefix and str.removesuffix need 3.9, and each is called where the tree actually calls it: removeprefix in scripts/prose_lint.py and spec/audit.py, removesuffix in spec/audit.py alone. An older interpreter starts, runs, and raises AttributeError when it reaches one. datetime.UTC needs 3.11 and arrives through a module-level from datetime import UTC in spec/audit.py, scripts/pr_review.py and its tests, so an older interpreter raises ImportError before any of those modules run at all. Which mode a host sees is decided by the script it runs rather than by the interpreter alone: spec/audit.py carries both and fails at import, scripts/pr_review.py carries only the import and fails the same way, and scripts/prose_lint.py carries only the call and therefore starts, runs, and fails partway through. The name rather than the version is what differs per platform, which the second probe covers.", + "why": "Every script here is standard library only, so a bare interpreter is enough and no package floor exists. The floor is the toolchain target rather than a measured breakage one version below it, which is the one entry here that reads that way and says so rather than implying a defect nobody found. pyproject.toml sets ruff target-version to py313 and mypy python_version to 3.13, so what those tools report describes 3.13 and describes no other interpreter, and a run below the floor is unverified rather than known broken. Both tools run in CI through uvx at their latest releases, beside the doc linters, the registry and spec validation, the script self-tests under coverage, and the repo and prose gates. That run judges the code rather than any host, so this floor is still a configuration choice rather than an enforced result, and a host failing it has no CI failure to point at. Two hard requirements are measured, both sit lower, and they fail differently. str.removeprefix and str.removesuffix need 3.9, and each is called where the tree actually calls it: removeprefix in scripts/prose_lint.py and spec/audit.py, removesuffix in spec/audit.py alone. An older interpreter starts, runs, and raises AttributeError when it reaches one. datetime.UTC needs 3.11 and arrives through a module-level from datetime import UTC in spec/audit.py, scripts/pr_review.py and its tests, so an older interpreter raises ImportError before any of those modules run at all. Which mode a host sees is decided by the script it runs rather than by the interpreter alone: spec/audit.py carries both and fails at import, scripts/pr_review.py carries only the import and fails the same way, and scripts/prose_lint.py carries only the call and therefore starts, runs, and fails partway through. The name rather than the version is what differs per platform, which the second probe covers. Two scripts outside this gate probe a lower number on purpose rather than by drift: repo-config/configure.sh accepts 3.7+ because that is the oldest interpreter resolve_description.py happens to run on (PEP 563), not a second floor for this repo, and scripts/skills_install.sh / host-setup/*/install-skills.* accept 3.7+ because they must run on whatever a host already has before this floor's own toolchain exists to install one; neither is evidence that spec/ or scripts/ code should hedge for anything older than 3.13.", "source": { "linux": "Whatever the platform provides at or above the floor, since the scripts need an interpreter and no packages, so no distribution or build is pinned here.", "macos": "Whatever the platform provides at or above the floor, on the same reasoning as Linux.", From ee719d4977f6840cf26954b9e48e967bb99802d3 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Sat, 22 Aug 2026 15:06:39 -0700 Subject: [PATCH 2/2] Recast Two Agent-Authored Prose Semicolons as Two Sentences (#922) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to #920: qodo-code-review caught, on the develop-to-main promotion PR (#921), that the no-semicolon-in-agent-authored-prose rule applies to shell comments and JSON `why` strings too, not just Markdown, and #920 left two semicolons behind that `scripts/prose_lint.py`'s semicolon check doesn't scan for (Markdown-only, the same known gap as its dash/comment-wrap checks). - `repo-config/configure.sh`: recast the comment and the failure message as two sentences each. - `spec/host-tools.json`: recast the `python3` tool's `why` the same way. Verified locally: `scripts/prose_lint.py` clean, `bash -n` clean, JSON valid, `pytest scripts/tests/test_host_gate.py scripts/tests/test_prose_lint.py scripts/tests/test_repo_gate.py` all pass (392 passed). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Sonnet 5 --- repo-config/configure.sh | 5 +++-- spec/host-tools.json | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/repo-config/configure.sh b/repo-config/configure.sh index 2af0f165..d655314f 100755 --- a/repo-config/configure.sh +++ b/repo-config/configure.sh @@ -71,14 +71,15 @@ if [ -f "$registry" ]; then # Resolved here, not near the top, so a run with no registry (an explicit model, no hub checkout) never needs Python at all. # The name python3 is not universal: native Windows can register a Microsoft Store stub under that name that resolves on PATH but fails when actually run, so this runs it rather than just checking PATH (docs/host-setup.md). # The probe checks 3.7+ (PEP 563, from __future__ import annotations) because that is the oldest interpreter resolve_description.py happens to parse and run on, not because 3.7 is this repo's supported floor. - # The repository floor is 3.13 (spec/host-tools.json's python3 target, enforced on the host by scripts/host_gate.py); resolve_description.py's own from __future__ import annotations is what lets it stay parseable below that, not a license for any other spec/ code to hedge for anything older. + # The repository floor is 3.13 (spec/host-tools.json's python3 target, enforced on the host by scripts/host_gate.py). + # Its own from __future__ import annotations is what lets resolve_description.py stay parseable below that, not a license for any other spec/ code to hedge for anything older. # A too-old interpreter fails here with a clear message instead of a bare traceback from the script. if python3 -c "from __future__ import annotations" >/dev/null 2>&1; then py_cmd=(python3) elif py -3 -c "from __future__ import annotations" >/dev/null 2>&1; then py_cmd=(py -3) else - echo "No Python interpreter found (python3 or py -3) able to run resolve_description.py. This repo targets Python 3.13; see docs/host-setup.md." >&2 + echo "No Python interpreter found (python3 or py -3) able to run resolve_description.py. This repo targets Python 3.13 (see docs/host-setup.md)." >&2 exit 1 fi # Delegates to spec/resolve_description.py rather than a third hand-rolled copy of description_errors(). diff --git a/spec/host-tools.json b/spec/host-tools.json index 30356d3e..f79d2fd6 100644 --- a/spec/host-tools.json +++ b/spec/host-tools.json @@ -87,7 +87,7 @@ "probes": [["python3", "--version"], ["py", "-3", "--version"]], "pattern": "Python (\\d+(?:\\.\\d+)*)", "minimum": "3.13", - "why": "Every script here is standard library only, so a bare interpreter is enough and no package floor exists. The floor is the toolchain target rather than a measured breakage one version below it, which is the one entry here that reads that way and says so rather than implying a defect nobody found. pyproject.toml sets ruff target-version to py313 and mypy python_version to 3.13, so what those tools report describes 3.13 and describes no other interpreter, and a run below the floor is unverified rather than known broken. Both tools run in CI through uvx at their latest releases, beside the doc linters, the registry and spec validation, the script self-tests under coverage, and the repo and prose gates. That run judges the code rather than any host, so this floor is still a configuration choice rather than an enforced result, and a host failing it has no CI failure to point at. Two hard requirements are measured, both sit lower, and they fail differently. str.removeprefix and str.removesuffix need 3.9, and each is called where the tree actually calls it: removeprefix in scripts/prose_lint.py and spec/audit.py, removesuffix in spec/audit.py alone. An older interpreter starts, runs, and raises AttributeError when it reaches one. datetime.UTC needs 3.11 and arrives through a module-level from datetime import UTC in spec/audit.py, scripts/pr_review.py and its tests, so an older interpreter raises ImportError before any of those modules run at all. Which mode a host sees is decided by the script it runs rather than by the interpreter alone: spec/audit.py carries both and fails at import, scripts/pr_review.py carries only the import and fails the same way, and scripts/prose_lint.py carries only the call and therefore starts, runs, and fails partway through. The name rather than the version is what differs per platform, which the second probe covers. Two scripts outside this gate probe a lower number on purpose rather than by drift: repo-config/configure.sh accepts 3.7+ because that is the oldest interpreter resolve_description.py happens to run on (PEP 563), not a second floor for this repo, and scripts/skills_install.sh / host-setup/*/install-skills.* accept 3.7+ because they must run on whatever a host already has before this floor's own toolchain exists to install one; neither is evidence that spec/ or scripts/ code should hedge for anything older than 3.13.", + "why": "Every script here is standard library only, so a bare interpreter is enough and no package floor exists. The floor is the toolchain target rather than a measured breakage one version below it, which is the one entry here that reads that way and says so rather than implying a defect nobody found. pyproject.toml sets ruff target-version to py313 and mypy python_version to 3.13, so what those tools report describes 3.13 and describes no other interpreter, and a run below the floor is unverified rather than known broken. Both tools run in CI through uvx at their latest releases, beside the doc linters, the registry and spec validation, the script self-tests under coverage, and the repo and prose gates. That run judges the code rather than any host, so this floor is still a configuration choice rather than an enforced result, and a host failing it has no CI failure to point at. Two hard requirements are measured, both sit lower, and they fail differently. str.removeprefix and str.removesuffix need 3.9, and each is called where the tree actually calls it: removeprefix in scripts/prose_lint.py and spec/audit.py, removesuffix in spec/audit.py alone. An older interpreter starts, runs, and raises AttributeError when it reaches one. datetime.UTC needs 3.11 and arrives through a module-level from datetime import UTC in spec/audit.py, scripts/pr_review.py and its tests, so an older interpreter raises ImportError before any of those modules run at all. Which mode a host sees is decided by the script it runs rather than by the interpreter alone: spec/audit.py carries both and fails at import, scripts/pr_review.py carries only the import and fails the same way, and scripts/prose_lint.py carries only the call and therefore starts, runs, and fails partway through. The name rather than the version is what differs per platform, which the second probe covers. Two scripts outside this gate probe a lower number on purpose rather than by drift: repo-config/configure.sh accepts 3.7+ because that is the oldest interpreter resolve_description.py happens to run on (PEP 563), not a second floor for this repo, and scripts/skills_install.sh / host-setup/*/install-skills.* accept 3.7+ because they must run on whatever a host already has before this floor's own toolchain exists to install one. Neither is evidence that spec/ or scripts/ code should hedge for anything older than 3.13.", "source": { "linux": "Whatever the platform provides at or above the floor, since the scripts need an interpreter and no packages, so no distribution or build is pinned here.", "macos": "Whatever the platform provides at or above the floor, on the same reasoning as Linux.",