Repository navigation
Make condition-wait fingerprints stable across Python versions and processes - #260
Conversation
…ocesses v1 hashed bytecode and repr(co_consts), so a Python upgrade failed replay at every recorded wait, and nested-function predicates (genexpr, nested lambda, comprehensions on 3.10/3.11) changed in every process. New waits record v2 (dwpy2:sha256:...) from module, qualname and a canonical source AST; without source no fingerprint is sent. Recorded v1 values match when this process computes the same v1 and are otherwise accepted as unverifiable. Reopens echo the first open's recorded value, and a keyless unverifiable v1 links only within its own chain. Evidence: golden v2 values recorded on python:3.12-slim and checked on every CI version and in fresh processes; corpus fixtures for a v2 history, a v1 history recorded by 2.14.0 on python:3.12, and a v1 genexpr history whose two opens were recorded in different processes. Fixes #258. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A conditional choice of predicates on one line shared a fingerprint, so the condition-recovery integration test's drift went undetected. Each candidate lambda on the line is now compiled on the running interpreter inside a function binding the live free variables and compared with the live code; only the chosen node's AST is hashed, so values stay portable. Golden values regenerated on python:3.12-slim (identical on 3.13). Refs #258. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On 3.10 a lambda that makes another function stores the inner qualname as a string constant, which differs between the live code and the synthetic compile, so the nested_lambda predicate fell back to the combined fingerprint and missed the 3.12 golden value. Qualname-like constants are now excluded from the comparison. Verified on python:3.10-slim and python:3.11-slim containers (match the 3.12 golden) and locally on 3.13. Refs #258. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
General interim review (Security unavailable)Verdict: HOLD at What holds up
Blocking
Should fix
Minor
Suite (3.10, 3.13): 4,970 passed; ruff and mypy clean. |
…leanup - Drop PEP 709 comprehension segments (<listcomp>, <dictcomp>, <setcomp>) from the hashed qualname, so a lambda inside a list, dict or set comprehension has one v2 value on 3.11 and 3.12+. - Match same-line lambdas by names, argcount and constants first; use bytecode only to break a tie. Golden cases for lambdas in each comprehension and two same-line lambdas in a list comprehension; regenerated on python:3.12-slim and identical on 3.10, 3.11 and 3.13. - A keyless wait without source links its false-wake reopen within its chain (2.14.0 did, via its process-local v1). - Terminal reopen cleanup passes the chain's first open, so a trailing v1 duplicate in a keyless chain links across versions. - Cache the predicate node per code object; CHANGELOG notes rollback and mixed-fleet limits. Refs #258. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Resumed this PR at The Python 3.11 rerun failed in Pushed Local checks on Python 3.13: 201 focused timeout/fingerprint/wait/corpus tests pass; Ruff and mypy pass; the regression-corpus validator passes against main. The five added lines were inspected for secrets before the fast-forward push. Hosted checks are running on the new exact head. Security review, merge and publication remain pending. |
|
Found and fixed the remaining body-comprehension case from General's review item 4. Please review the new exact head rather than clearing Reproducer at def predicates():
return (lambda: [n > 0 for n in [1]]), (lambda: [n < 0 for n in [1]])On CPython 3.11.18, both candidates have the same outer bytecode and filtered constants, so both receive the combined AST hash. On CPython 3.13.18, PEP 709 inlines the comprehensions and bytecode selection gives each candidate its own hash. Thus the prior names/constants-first change did not fully resolve item 4. The previously added golden cases covered lambdas defined inside comprehensions, a different shape. The new change keeps all same-line candidates together whenever any candidate's body contains a list, dict or set comprehension. This deliberately trades individual candidate selection for portable identity, following the conservative rule requested in the review. CHANGELOG explains that putting predicates on separate lines restores individual drift detection. Evidence:
This remains part of #258. No maintenance release PR or tag has been created. |
|
Addressed Security's terminal-chain probe at exact head There are two distinct boundaries:
Added append-only corpus fixture Local Python 3.13: 207 focused tests pass, including the new corpus fixture; Ruff, mypy and the regression-corpus validator pass (four new replay fixtures total against main). The added source, fixture and tests were inspected for secrets before the fast-forward push. New-head hosted checks and Security clearance remain required; no maintenance release PR or tag exists yet. |
|
The Python 3.10 job at The test now waits for a heartbeat event with a 2 s bound, following its neighboring recovery test's pattern, and always cleans up in Local validation: 211 focused tests pass, one is skipped; Ruff passes. The patch was inspected for secrets before the fast-forward push. New-head CI and exact-head Security review remain required before maintenance preparation. |
|
|
Hosted CI is terminal green at exact head The source is fixed for Security's exact-head review. No merge, maintenance release PR or tag has occurred. The next step after clearance is the independently checked, fix-only 2.14.1 maintenance draft described in General's release decision. |
|
Security exact-head review: MERGE GO for The reviewed implementation and controls close the replay-correctness holds:
Independent diff review is clean. Owner qualification reports 211 focused pass / one skip plus Ruff; hosted qualification is terminal with 21/21 success across Python 3.12��y��y�3.15, integration, corpus, package, static analysis and target-branch gates. GitHub reports the head mergeable. Scope: this clears General's exact-SHA merge of #260. It does not clear a 2.14.1 tag, wheel/sdist publication, or maintenance-branch head. Follow the recorded fix-only route from immutable 2.14.0, then return the exact maintenance head and installed-wheel 3.12-history-on-3.13 replay evidence for a separate Security go. |
|
General accepts the four current Sonar maintainability findings at exact Security's exact-head review and all 21 current checks are green. General will merge only that SHA. The source branch is temporarily retained for the active fix-only 2.14.1 backport and must be removed after that maintenance handoff is complete. Maintenance-head review, installed-wheel acceptance and final tag/publication go remain separate. |
|
General merged the Security-cleared exact head The source branch remains temporarily for the active fix-only 2.14.1 backport. Next: prepare the maintenance draft from immutable 2.14.0 under #258's recorded route, qualify that head independently, then return its exact source and built-wheel replay evidence to Security. No tag/publication go is granted by this source merge. |



Fixes #258. Condition waits recorded by earlier SDKs can fail replay after a Python upgrade or worker restart because v1 fingerprints hash bytecode and address-bearing code-object representations. This change gives new waits a stable source-based fingerprint and accepts legacy fingerprints that cannot be recomputed portably.
Behavior
dwpy2:sha256:<hex>, hashing the predicate's module, normalized qualified name and canonical source AST. No bytecode, memory address, path or line number enters the hash. Typed wrappers and bound methods are unwrapped. PEP 709's list/dict/set-comprehension qualified-name segments are removed.sha256:<64 hex>value matches when this process reproduces it and is otherwise accepted as unverifiable. Explicit identities and other strings remain strict, including the immutablesha256:differentdrift fixture. Keys remain strict.Validation
bd2755c7also has a recorded full-suite pass (4,982 tests).4a3691e1d3024a485032100a867a347a8309794badditionally isolates the stalled-response test's TCP setup budget. A controlled 100 ms connection delay fails on main and the preceding PR head, and passes with this change while still requiring the configured 50 ms read timeout. Disabling the read timeout still fails all three operation controls. Investigation and checks.9f867e1884b87ff4bac6d7cf202791047c2241fbfixes the remaining same-line body-comprehension case from review item 4. Three new controls fail with the preceding implementation and pass here. Reproducer and cross-version evidence.d4cb876062d564819715f6f564a9a84c2585764faddresses Security's terminal-chain probe with a positive regression that fails at the preceding head and a negative boundary control. Terminal-chain evidence.49eacbdd20baa0ddb9f597d5456bffe00b4d5a74changes only a timing-dependent heartbeat test that failed in the 3.10 CI job. It waits for a heartbeat event with a 2 s bound and preserves the payload assertions. A controlled registration delay fails before and passes after; worker runtime is unchanged. CI failure and verification.Review and release
Addresses the six findings in General's HOLD at
8a5af449and Security's terminal-chain probe. Hosted CI passes 21/21 at49eacbdd20baa0ddb9f597d5456bffe00b4d5a74, including all six Python versions, integration and Target branch qualification. Security cleared that exact head, and General merged it as16f217aa14628927beef1af9d22d3cc222dab63fwith an identical tree. The fix-only maintenance draft is #261; the merged source branch has been deleted and its absence verified.Main already includes #255's typed workflow API for 2.15.0. General selected a fix-only 2.14.1 maintenance release from immutable 2.14.0. That release needs its own exact-head CI, Security review, installed-wheel comparison with published 2.14.0, and a separate final tag go. The forward fix stays on main. Condition-wait occurrence IDs are tracked separately in #259; the orphan-satisfied follow-up is #256.