Fix: refuse a _task_interface built from a different revision of the tree - #1523
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThe native ChangesBinding Commit Validation
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant simpler_task_interface
participant task_interface
participant Git
simpler_task_interface->>task_interface: Read __build_commit__
simpler_task_interface->>Git: Query current HEAD
Git-->>simpler_task_interface: Return commit hash
simpler_task_interface->>simpler_task_interface: Compare revisions
simpler_task_interface-->>task_interface: Continue or raise ImportError
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/developer-guide.md`:
- Around line 186-198: The Python-only row in the rebuild guidance conflicts
with the full-HEAD validation described below. Update that row to state that no
rebuild is needed only when HEAD remains unchanged; otherwise instruct users to
rerun pip install. Keep the existing explanation of full-HEAD stamp validation
intact.
In `@python/bindings/CMakeLists.txt`:
- Around line 33-50: Move SIMPLER_BUILD_COMMIT generation from CMake configure
time into the build for _task_interface, ensuring each recompilation obtains the
current git HEAD rather than a cached value. Use a build-time dependency or
generator expression tied to the target’s compile definitions, and add coverage
for an incremental rebuild after switching branches.
In `@python/simpler/task_interface.py`:
- Around line 80-98: Update the build metadata check around __build_commit__ to
use a distinct sentinel for a missing attribute, while still rejecting an
explicitly empty stamp. Only compare and trust __build_commit__ when the
attribute exists, is non-empty, and git successfully provides HEAD; preserve the
existing early returns for unavailable repository metadata or git failures.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 6cd8a2b4-5ef9-448f-8226-01dbe5c83641
📒 Files selected for processing (4)
docs/developer-guide.mdpython/bindings/CMakeLists.txtpython/bindings/task_interface.cpppython/simpler/task_interface.py
ad1ce8d to
eb2c8fd
Compare
…tree An editable install pins the compiled extension at install time (`editable.rebuild = false`) while `python/simpler/*.py` is read live. Switching branches or rebasing therefore moves the Python out from under a fixed binary, nothing rebuilds, and a changed struct layout makes attributes read as 0 with no error anywhere. That is not hypothetical. A worktree installed before hw-native-sys#1309 (which dropped `block_dim` from `CallConfig`) and then branched onto a main containing it read `aicpu_thread_num` as 0, and the whole onboard L2 suite failed with `launch_aicpu_num (0) must be in range [1, 4]` — a plausible-looking runtime rejection that reads as a product bug. It cost a full bisect, and an A/B against main "reproduced" it because both arms shared the same stale binary. The same skew hit again a few hours later as `AttributeError: run_stream_set_create_count` after a rebase across hw-native-sys#1464. `_task_interface` now records the commit it was built from, and `simpler.task_interface` compares it against the working tree at import, raising with the one command that fixes it. Loud beats silent here: the alternative is not an error, it is wrong values. A warning would also have been the wrong choice — pytest relegates import-time warnings to its end-of-run summary, and in the failure above the same warning would have appeared in *both* arms of the A/B and been dismissed as noise. A *missing* stamp raises too, rather than being treated as "cannot tell". The attribute is absent only on an extension compiled before it existed, which in a checkout new enough to run the check is by definition a different revision — and is the state of every already-installed worktree the day this lands. Keyed on git HEAD, matching `RuntimeBuilder._build_cache_stamp` — mtimes do not survive a branch switch, which is why that stamp is git-based too. Deliberately the whole HEAD rather than a subset: excluding paths means maintaining a second "what cannot affect the build" list beside the one CI already keeps, and the guard exists precisely so correctness does not rest on such judgments. It costs little — 120 of the last 200 commits touch the ABI surface anyway, so most of the reinstalls it forces were owed regardless and merely became visible; the rest cost one ~20 s reinstall. Inert outside a source tree: a wheel has no `.git` to compare against, and a build made without git carries an empty stamp. The rebuild table gained the trigger it was missing and no longer contradicts itself. It was framed as "what did you change", so it had no row for the case where you changed nothing and the tree moved — the one that bites, and the one where verifying that `import simpler` resolves into your worktree gives false confidence. Its "no rebuild needed" rows now say what is actually true: nothing to recompile, but the guard keys on HEAD, so a commit still needs a reinstall before the next import. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
@coderabbitai All three answered — two fixed, one skipped with a measurement:
Also: the ast-grep |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
git diff -- python/bindings/CMakeLists.txt python/bindings/task_interface.cpp python/simpler/task_interface.py docs/developer-guide.md
printf '\n--- task_interface.py outline ---\n'
ast-grep outline python/simpler/task_interface.py --items all 2>/dev/null || true
printf '\n--- focused subprocess / build-stamp references ---\n'
rg -n -C 4 --glob 'python/simpler/task_interface.py' --glob 'python/bindings/CMakeLists.txt' \
'(__build_commit__|SIMPLER_BUILD_COMMIT|subprocess|rev-parse|execute_process)' .Length of output: 8370 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- import guard ---'
sed -n '55,125p' python/simpler/task_interface.py
printf '%s\n' '--- rebuild guidance references ---'
rg -n -C 8 -i 'rebuild|branch|rebase|build stamp|build_commit|_task_interface|git head' \
docs/developer-guide.mdLength of output: 10392
🐇🔍 ✏️ Learnings added
|
The failure this prevents
An editable install pins the compiled extension at install time
(
editable.rebuild = false) whilepython/simpler/*.pyis read live. Switchbranches or rebase and the Python moves out from under a fixed binary. Nothing
rebuilds, and a changed struct layout makes attributes read as 0 with no error
anywhere.
Observed twice in one session:
block_dimfromCallConfig), then branched onto a main containing it, readaicpu_thread_numas 0 — even thoughCallConfig().aicpu_thread_numstillprinted
3in Python. The entire onboard L2 suite failed withlaunch_aicpu_num (0) must be in range [1, 4], deep in the device runner. Itreads as a product bug; it cost a full bisect. Worse, an A/B against
main"reproduced" it — both arms shared the same stale binary — which produced a
confidently wrong "this is pre-existing on main" conclusion.
AttributeError: run_stream_set_create_count.Verifying that
import simplerresolves into your worktree does not catchthis. Under an editable install that only proves the Python is live.
What this does
_task_interfacerecords the commit it was compiled from;simpler.task_interfacecompares it against the working tree at import and raises:
Raising rather than warning, because the alternative is not an error — it is
wrong values.
Design notes
Why git HEAD, not a content hash. Matches
RuntimeBuilder._build_cache_stamp,which is git-based for the same stated reason: cmake/mtime "cannot detect that
source files changed" across a branch switch. Reusing the existing notion beats
inventing a parallel one.
Why the whole HEAD, not just
python/bindings/+src/common/task_interface/.Path-scoping under-detects: a root
CMakeLists.txt,pyproject.tomlornanobind change alters the extension without touching those paths, and
under-detection is precisely the silent failure this exists to stop. It would
also have bought little — 120 of the last 200 commits touch the ABI surface
anyway. Over-detecting costs one reinstall, measured at 19.5 s no-op.
Inert outside a source tree. A wheel has no
.gitto compare against(verified against a replica of the wheel layout), and a build made without git
carries an empty stamp.
Verification
raises with the message above. Reinstall → clean.
tests/ut/py: 829 passed, 2 skipped.parents[2]has no.git→ check skipped.pip installand the test run happen at the same HEAD.Also removes the corresponding entry from my local
KNOWN_ISSUES.md, and addsthe trigger the rebuild table was missing — it was framed as "what did you
change", so it had no row for "you changed nothing and the tree moved", which is
the case that actually bites.
🤖 Generated with Claude Code