Skip to content

Probe for the Interpreter in the Order the Spec Declares, and Enforce Its Floor - #1167

Merged
ptr727 merged 3 commits into
developfrom
feature/1163-pre-push-python-probe
Sep 1, 2026
Merged

Probe for the Interpreter in the Order the Spec Declares, and Enforce Its Floor#1167
ptr727 merged 3 commits into
developfrom
feature/1163-pre-push-python-probe

Conversation

@ptr727

@ptr727 ptr727 commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Unblocks the develop -> main promotion #1163, whose last two open threads are against .husky/pre-push from #1166.

What was wrong

Both hooks said the interpreter is chosen "by running the probes spec/host-tools.json declares, in its order", then probed python3 before py -3. The spec declares the opposite:

"probes": [["py", "-3", "--version"], ["python3", "--version"]]

#1166 changed that order deliberately and wrote the reason into the spec's own why: a bare python3 is reached through PATH, so on Windows an activated virtual environment or an interpreter from MSYS2, Cygwin or Scoop answers ahead of the managed one and would be graded in its place, while the py launcher reaches a registered interpreter whatever is active and exists on Windows alone. It changed the spec and left both hooks behind, and the hooks' own comments then asserted an order they did not follow.

Two reviewers found this independently on #1163, one of them twice.

The pre-commit comment also still said native Windows "registers py and not python3", which #1166 made untrue: host-setup/windows/install-tools.ps1 now supplies a real python3 there.

Separately, neither hook enforced the 3.13 floor the same spec entry declares. The engines import datetime.UTC at module level, so an older interpreter fails at import and exits 1, which reads as a gate refusal rather than as the gate never running. That is the one distinction local-strict-review's refusal table exists to keep, and it was silently collapsed.

What changed

Both hooks probe in the spec's order and state why. The pre-push hook reads the version out of the probe it already ran and refuses below the floor, naming the interpreter and the version it found, and refuses an unparseable version for the same reason.

Verification

The comparison is sort -V rather than a string test, since 3.9.6 sorts above 3.13 as a string. Checked across the cases that separate the two:

Input Result
Python 3.13.5, 3.13.0, 3.13, 3.14.1, 4.0 pass
Python 3.12.9, 3.9.6, 3.2 refuse, below floor
empty, bash: py: command not found refuse, no recognizable version

The push that opened this pull request ran through the modified hook itself, on a host where py -3 is absent, so the fallback to python3 and the floor check are exercised live. shellcheck and shfmt pass on both files, as do prose_lint, spec/validate.py, host_gate.py and the full test suite.

Note for #1161

The probe order here follows spec/host-tools.json because that is the declared ground truth and the hooks were the stragglers. If #1161 lands python3 everywhere on Windows and decides the order should flip, flipping the spec and these six lines together is the whole change. The floor check is independent of that question.

Summary by CodeRabbit

  • Bug Fixes
    • Improved Python interpreter detection in commit and push checks, prioritizing the Windows py -3 launcher with python3 as a fallback.
    • Added clearer validation against the project’s minimum supported Python version.
    • Pushes are now blocked with an informative error when Python requirements cannot be verified or are not met.

… Its Floor

Both hooks said the interpreter is chosen "by running the probes
spec/host-tools.json declares, in its order", then probed python3 before py -3
while the spec declares py -3 first. The spec's order is deliberate and carries
its reason: a bare python3 is reached through PATH, so on Windows an activated
virtual environment or an interpreter from MSYS2, Cygwin or Scoop answers ahead
of the managed one and would be graded in its place, while the py launcher
reaches a registered interpreter whatever is active and exists on Windows alone.
The hooks now probe in that order and say why, and the pre-commit comment loses
the claim that Windows registers no python3, which is no longer true.

The pre-push hook also enforces the 3.13 floor the spec declares rather than
accepting any interpreter whose --version runs. The engines import datetime.UTC
at module level, so an older interpreter fails at import and exits 1, which reads
as a gate refusal rather than as the gate never running, the one distinction the
refusal table exists to keep. An unparseable version refuses for the same reason.

The comparison is sort -V rather than a string test, checked across the cases
that separate the two: 3.13.5, 3.13.0, 3.13, 3.14.1 and 4.0 pass, 3.12.9, 3.9.6
and 3.2 refuse, and an empty or unrecognized line refuses.
Copilot AI lite review requested due to automatic review settings September 1, 2026 18:22
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Align Python Probes and Enforce the Pre-Push Version Floor

🐞 Bug fix ✨ Enhancement 🕐 10-20 Minutes

Grey Divider

AI Description

• Aligns Python interpreter probes with the host-tools specification across both Husky hooks.
• Enforces Python 3.13 before pre-push review engines run.
• Distinguishes unavailable, unparseable, and below-floor interpreters with actionable failures.
Diagram

graph TD
    A["Husky Hooks"] --> B{"py -3 works?"} -->|Yes| D["Selected Python"] -->|Pre-commit| G["Documentation Gates"]
    B -->|No| C{"python3 works?"} -->|Yes| D
    C -->|No| F["Fail Closed"]
    D -->|Pre-push| E{"Python 3.13+?"} -->|Yes| H["Review Gates"]
    E -->|No or invalid| F
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Shared hook bootstrap helper
  • ➕ Prevents probe-order drift between pre-commit and pre-push.
  • ➕ Centralizes interpreter naming, version parsing, and diagnostics.
  • ➖ Adds sourcing and shell-library indirection to otherwise self-contained hooks.
  • ➖ Pre-commit and pre-push currently require different validation behavior.
  • ➖ Broadens a focused promotion-unblocking fix.

Recommendation: Keep the PR’s direct hook changes for this focused fix: they avoid relying on Python or another parser before Python itself is validated. If interpreter policy expands further, extract the duplicated probe selection into a sourced shell helper while preserving pre-push-specific floor enforcement.

Files changed (2) +29 / -14

Bug fix (2) +29 / -14
pre-commitFollow the declared Python probe order +7/-8

Follow the declared Python probe order

• Tries 'py -3' before 'python3', matching 'spec/host-tools.json' and avoiding PATH-selected Windows interpreters. Updates comments and failure output to explain the ordering and fallback behavior accurately.

.husky/pre-commit

pre-pushValidate the selected Python before review gates +22/-6

Validate the selected Python before review gates

• Reorders interpreter probes to prefer 'py -3', then falls back to 'python3'. Parses the selected interpreter’s version, rejects unrecognizable output or versions below 3.13 using version-aware sorting, and reports gate-not-run failures explicitly.

.husky/pre-push

@qodo-code-review

qodo-code-review Bot commented Sep 1, 2026

Copy link
Copy Markdown

Code Review by Qodo

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

Grey Divider


Action required

1. head Reads Pipeline Early ✓ Resolved 📜 Skill insight ≡ Correctness
Description
The version comparison pipes sort directly into head -n 1 while the script runs under `set -Eeuo
pipefail`. This prohibited early-exit reader pattern can make the producer fail with SIGPIPE and
abort the hook.
Code

.husky/pre-push[90]

+if [ "$(printf '%s\n%s\n' "$py_floor" "$py_version" | sort -V | head -n 1)" != "$py_floor" ]; then
Relevance

●●● Strong

This is a deterministic pipefail correctness bug in the newly added shell pipeline.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Rule 2827161 explicitly prohibits cmd | head patterns under pipefail. The new condition uses
printf | sort -V | head -n 1, and .husky/pre-push enables set -Eeuo pipefail at line 4.

.husky/pre-push[4-4]
.husky/pre-push[90-90]
Skill: shell-codestyle

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

## Issue description
The version comparison pipes producer output directly into the early-exit reader `head` under `pipefail`.

## Issue Context
Capture the complete sorted output first, then select its first line without allowing a downstream reader to terminate the producer early. Preserve the existing version-floor semantics.

## Fix Focus Areas
- .husky/pre-push[90-90]

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


2. Floor check breaks macOS ✓ Resolved 🐞 Bug ≡ Correctness
Description
The new floor check invokes sort -V, which macOS's default sort does not support. Its failed
pipeline yields no matching floor value, so the hook enters the below-floor branch and rejects
pushes even when Python 3.13 or newer is installed.
Code

.husky/pre-push[90]

+if [ "$(printf '%s\n%s\n' "$py_floor" "$py_version" | sort -V | head -n 1)" != "$py_floor" ]; then
Relevance

●●● Strong

The comparison uses nonportable GNU sort behavior despite documented macOS support.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The repository explicitly supports macOS, while the host contract neither requires nor installs GNU
coreutils. The macOS sort documentation lists its supported ordering flags without -V; therefore
line 90 fails on a stock supported macOS host, and lines 90-93 turn the empty result into a refusal.

docs/host-setup.md[5-25]
.husky/pre-push[84-93]
🌐 The documented macOS sort options do not include GNU version-sort option -V.

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

## Issue description
The pre-push Python floor check uses GNU-specific `sort -V`. Supported macOS hosts use a `sort` implementation without this option, causing valid pushes to be rejected.

## Issue Context
The selected Python interpreter is already available at this point. Compare dot-separated numeric components portably, for example through that interpreter, while preserving equivalence such as `3.13 == 3.13.0` and rejecting malformed versions.

## Fix Focus Areas
- .husky/pre-push[84-90]

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



Remediation recommended

3. pre-push Probe Comments Overlong ✓ Resolved 📜 Skill insight ⚙ Maintainability
Description
The new interpreter explanation spans three consecutive comment lines and elaborates on probe
selection and platform fallback behavior already represented by the probe order. Condense it to the
essential rationale, using the concise explanation in pre-commit as a reference.
Code

.husky/pre-push[R67-69]

+# The pre-commit hook states why running a probe rather than testing for a name is the point.
+# The py launcher answers first because it reaches a registered interpreter whatever is active, where a bare python3 is reached through PATH and an activated virtual environment answers there instead.
+# That launcher exists on Windows alone, so the second probe is what answers on Linux and macOS.
Relevance

●●● Strong

Concise comment fixes are consistent with accepted hook-comment feedback in the repository.

PR-#643

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Rule 2826677 establishes that comments should be one line by default, with a second line only for a
genuine constraint; the cited additions instead form three-line prose blocks explaining the probe
mechanism, interpreter selection, and platform fallback behavior.

.husky/pre-push[67-69]
.husky/pre-commit[28-30]
Skill: comment-and-doc-style

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

## Issue description
The interpreter probe rationale is expressed as unnecessarily long three-line comment blocks that should be condensed to one concise comment.

## Issue Context
Compliance rule 2826677 requires comments to use one line by default, with a second line only for a genuine constraint. The probe order already represents the relevant behavior, and the concise explanation in `pre-commit` can serve as a reference.

## Fix Focus Areas
- .husky/pre-push[67-69]
- .husky/pre-commit[28-30]

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


Grey Divider

Context sources
✅ Compliance rules (platform): 72 rules
✅ Skills: 5 invoked
  comment-and-doc-style
  dotnet-codestyle
  python-codestyle
  shell-codestyle
  workflow-ci-contract
✅ Web pages:
  +7 more
Review mode: ⚖️ Balanced

Grey Divider

Tip of the day
💡 Did you know, you can describe a rule in plain language on the Rules page and Qodo drafts it for you

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread .husky/pre-push Outdated
Comment thread .husky/pre-push Outdated
Comment thread .husky/pre-push Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The new version-floor comparison uses sort -V, which is not portable on macOS/BSD and can cause incorrect refusals.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Aligns the Husky hooks’ Python interpreter probing with spec/host-tools.json (try py -3 before python3) and adds an explicit Python version-floor refusal in pre-push so an interpreter that is too old is reported as “gate did not run” rather than failing inside the engines.

Changes:

  • Reordered interpreter probing in .husky/pre-push and .husky/pre-commit to match the spec-declared probe order.
  • Added a Python version-floor check to .husky/pre-push with clearer refusal messaging when the gate cannot run.
File summaries
File Description
.husky/pre-push Probe py -3 before python3 and enforce the Python version floor before running review engines.
.husky/pre-commit Probe py -3 before python3 and update comments/messages to match the spec rationale.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .husky/pre-push
The floor check compared version strings through sort -V piped into head, which
carried two defects the reviewers caught in my own new code.

sort -V is a GNU spelling. This repository uses it in host-setup/linux scripts
alone, and macOS is a first-class platform here, so a hook is the wrong place to
depend on it. A failed pipeline would also have yielded no matching floor, which
takes the below-floor branch and refuses a push on a host that satisfies the
floor.

Piping sort into head -n 1 under set -Eeuo pipefail is the early-reader pattern
the shell rules name. Measured here: harmless at two lines, and exit 141 once the
producer cannot finish writing before the reader leaves.

Both go away by asking the interpreter about itself, which needs no text
comparison and no pipeline. Verified across the version_info values that separate
the two arms: 3.13, 3.13.0, 3.13.5, 3.14.1 and 4.0.0 pass, and 3.12.9, 3.9.6,
3.2.0 and 2.7.18 refuse while still reporting the version they found.

The probe comments come down to the one line each that carries a reason the code
does not.
Copilot AI review requested due to automatic review settings September 1, 2026 18:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The pre-push hook hardcodes the Python floor instead of reading it from spec/host-tools.json, risking spec drift and reintroducing the same class of mismatch this PR is fixing.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread .husky/pre-push Outdated
The floor was a literal in the hook, which is the shape that produced the defect
this branch exists to fix: the probe order lived in two places, the spec moved,
and the hook did not. A floor written twice drifts the same way.

The hook reads spec/host-tools.json for the python3 minimum with the interpreter
it just selected. A spec it cannot read refuses the push and says so, rather than
falling through to an unchecked interpreter, since a floor that cannot be read is
the gate not running rather than a version verdict.
Copilot AI review requested due to automatic review settings September 1, 2026 18:40
@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: 0280b524-66bc-4691-9237-a3a2b955e8d3

📥 Commits

Reviewing files that changed from the base of the PR and between d8d6a67 and 8d54bd5.

📒 Files selected for processing (2)
  • .husky/pre-commit
  • .husky/pre-push

📝 Walkthrough

Walkthrough

The pre-commit and pre-push hooks now prefer py -3 over python3. The pre-push hook reads the configured minimum Python version and rejects unavailable, unreadable, or outdated interpreters.

Changes

Python hook validation

Layer / File(s) Summary
Interpreter selection and version gate
.husky/pre-commit, .husky/pre-push
Both hooks probe py -3 before python3. The pre-push hook validates the selected interpreter against the minimum version in spec/host-tools.json before running the review gate.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 8d54b

The hooks now follow the declared interpreter probe order and enforce the minimum supported Python version; no actionable merge-blocking risk remains after normal checks and review.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Probe for the Interpreter in the Order the Spec Declares, and Enforce Its Floor' directly reflects the main changes in the pull request. The changeset updates both .husky/pre-commit and `…
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title 'Probe for the Interpreter in the Order the Spec Declares, and Enforce Its Floor' directly reflects the main changes in the pull request. The changeset updates both .husky/pre-commit and .husky/pre-push to probe interpreters in the order declared by spec/host-tools.json and adds enforcement of the Python minimum version floor from the spec. The title captures both key aspects of the changeset without vague or misleading language.

Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0 files. (2 skipped: 2 unsupported.)

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/1163-pre-push-python-probe

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The hook changes match the spec’s declared probe order and correctly enforce the spec floor using a portable version check without introducing new failure modes in the edited regions.

Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@ptr727
ptr727 merged commit 44ae00b into develop Sep 1, 2026
8 of 9 checks passed
@ptr727
ptr727 deleted the feature/1163-pre-push-python-probe branch September 1, 2026 18:47
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.

2 participants