Repository navigation
PR C: multi-platform and Python 3.15/3.15t ABI compatibility lanes - #19
Merged
Merged
Conversation
On macOS the collector reads sysctl values for the VM marker, the model, the OS release, the machine architecture and Rosetta translation. On Windows it reads the native machine from IsWow64Process2, the BIOS manufacturer and product name, and whether the process token is elevated, holds the Administrators group, or is LocalSystem. Both mark the Linux isolation interfaces not-applicable. An administrator token is recorded as the superuser, so a Windows runner's record cannot pass app-non-root. --amend-container runs podman info and podman container inspect on the host and adds the engine's rootless mode, the privileged flag and the devices to a record taken inside that container. --extension adds a JSON object file to the record's extensions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGH7FAsq1H9HmCR27Cj2Ao
scripts/ci/probe_python_abi.py reads the interpreter's wheel tags with packaging and PyPI's JSON metadata for each locked version, and reports which lock packages have a wheel this interpreter accepts, which ABI each wheel serves (cp315, cp315t, abi3, abi3t, abi3.abi3t), and whether the hash is in the lock. Its packages command installs each native package binary-only with the lock's hashes and no dependencies, imports and calls it once in a fresh process, and records the GIL state before and after. Thread safety is not tested. install-stage decides a lane's install stage from uv's exit statuses and the reports: blocked only when the installer names a package that has no wheel for the interpreter, or uv pip check finds only requirements the lock does not pin; any other failure is failed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGH7FAsq1H9HmCR27Cj2Ao
compatibility.yml runs the backend lock install, Django check, the Django tests and pytest, and the frontend checks, on GitHub-hosted Linux, Windows and macOS runners on x86-64 and Arm64, plus an x64 Python emulated on Windows on Arm, and records each runner against its profile. Its ubi job runs the backend checks in UBI 9 and 10 containers pinned by digest, in rootless Podman as the runner's user with every capability dropped, and adds the engine's facts from the host. python-abi.yml probes CPython 3.15.0 and 3.15t from uv 0.13.0 on the same six runners. Both workflows run on pull requests that touch their paths and on manual dispatch, are not required checks, and change no support status. The 23 profiles copy their lane's status or record not-assessed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGH7FAsq1H9HmCR27Cj2Ao
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f7ccd3da0b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
CI-MATRIX.md and TESTING.md describe the platform, UBI and Python ABI jobs, the collector's macOS, Windows and Podman sources, and the probe's tests. DEPENDENCY-STRATEGY.md records the third freshness batch (uv 0.13.0 for the ABI workflow, the CPython 3.15.0 builds, and the UBI 9.8 and 10.2 image digests) and the results of runs 38210201779 and 38210201628, without changing any lane status. SECURITY-ARCHITECTURE.md records the new workflows' triggers, permissions, downloads, PyPI metadata reads, Windows administrator token, and rootless container options; SECURITY-REVIEW.md and CODE-REVIEW.md point to them. CONTEXT.md, ARCHITECTURE.md and the READMEs no longer say other lanes were never run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGH7FAsq1H9HmCR27Cj2Ao
- probe_python_abi: a package that enables the GIL in a build that started without it fails execute and the job; wheels whose Requires-Python excludes the interpreter are not counted as installable; unmet requirements include installed versions a requirement excludes; the install stage is blocked on missing requirements only when every problem uv pip check prints, and its count, says so; a uv line names a missing wheel only when it says the package has none; recorded warning text loses control characters. - collect_environment: Windows capabilities are unknown, not not-applicable, because token privileges are not read, so a Windows record cannot pass minimal-capabilities unmeasured. - compatibility.yml: the frontend steps' outcomes go in the record as x-frontend. - Tests for these paths, probe_package and the packages command; docs updated to match, with corrected line citations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGH7FAsq1H9HmCR27Cj2Ao
Path.write_text translates newlines on Windows, so the lock file on disk had CRLF line endings and its sha256 no longer matched the hash the test computes from the text. The four Windows python-abi jobs failed on this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGH7FAsq1H9HmCR27Cj2Ao
import_probe read the result line the fresh python -I process printed and ignored how the process then ended, so a package that printed a passing import and call and crashed at shutdown, exited nonzero or was killed by a signal still passed. The probe now records the process's exit under "exit" (status, returncode, and on POSIX the signal name) beside the result it printed, and overall() counts a failed exit as a broken package, so execute fails and the packages command exits 1. A process that prints no result is a failed import with "no result" and its exit recorded. The summary table gains an Exit column. Tests: the exit after a printed result with subprocess.run replaced (0, 3, -11/SIGSEGV, an unknown signal, 0xC0000005), and in real processes an atexit os._exit(3), an atexit SIGKILL (POSIX only), and an exit before any result; overall() and the packages command with a failed exit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGH7FAsq1H9HmCR27Cj2Ao
GitHub-hosted Windows runners give the job's user an elevated administrator token only. The owner approved that on 2026-10-12 as a temporary exception limited to PR C's functional and ABI checks. - The Windows lanes move out of the platform and abi matrices into platform-windows and abi-windows, which run the same steps and env through YAML aliases. Their job-level if reads only the github context, since a job's if is evaluated before its matrix: a pull request whose head repository is this one, or a workflow_dispatch on refs/heads/main. A fork's pull request skips them (an intentional not-run). Linux, macOS and UBI jobs are unchanged. - --no-policy is passed only in those two jobs on a Windows runner. Their records hold x-policy-exception (policy skipped, security not-verified, compatibility incomplete, non-administrator run not-run), the security stage's reason says the same, and the step summary and a warning annotation repeat it. A Windows runner in any other job fails at once. - Stage evidence names the running job (GITHUB_JOB); the seven Windows profiles name the new jobs as their evidence sources. - SECURITY-ARCHITECTURE.md gains "Temporary exception: Windows administrator token" (scope, risks including a fork editing the condition, approval basis, end conditions, and the non-administrator Windows follow-up); CI-MATRIX, TESTING, DEPENDENCY-STRATEGY and CODE-REVIEW describe the jobs, the not-run, and the probe's exit check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGH7FAsq1H9HmCR27Cj2Ao
Doc-only change to the "Temporary exception: Windows administrator token" section of docs/SECURITY-ARCHITECTURE.md and the Triggers sentence that points to it. No workflow, script, test, or profile changes. - Scope: the exception covers every run of platform-windows and abi-windows that meets their condition, before and after pull request #19 is merged, until an end condition is met, instead of saying it is for that pull request only. - overall: a record from these jobs is never passed; it is incomplete, or failed when a stage failed. - End conditions: the records must pass the default policy without --no-policy, which needs at least a Windows run without an administrator token (app-non-root) and a collector that reads Windows token privileges (minimal-capabilities, recorded as unknown today). Nothing in the workflows reads the review date. - Fork approval: the job condition is not a control against a malicious fork, because a pull_request run uses the workflow file of the merge commit. Whether an edited fork run starts without approval depends on the repository's GitHub Actions fork approval setting, which the owner decides; the level in use was not checked. - Manual runs: a workflow_dispatch run needs write access and nothing else gates it. A setting alone cannot add a gate, because environment protection rules apply only to jobs that name an environment. - Review date 2026-11-08, confirmed by the owner on 2026-10-12. - The Codex finding on the Windows administrator token is recorded as accepted as this owner-approved, time-limited exception, not fixed in code. Related: #19 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGH7FAsq1H9HmCR27Cj2Ao
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by 김제석 · project thread
Summary
Before: only the checked lane (GitHub-hosted
ubuntu-24.04, x86-64, CPython 3.12.15) ran in CI, and the compatibility collector could describe Linux only.After: two new workflows install the hashed backend lock and run its checks on GitHub-hosted Linux, macOS and Windows runners on x86-64 and Arm64, in UBI 9 and UBI 10 containers under rootless Podman, and on CPython 3.15.0 and 3.15t. Each job writes a record of the runner and the stage outcomes, validates it against a committed profile, and uploads it as an artifact. No run changes a support status in
docs/DEPENDENCY-STRATEGY.md.The Windows lanes run in their own jobs,
platform-windowsandabi-windows, because GitHub-hosted Windows runners give the job an administrator token. The owner approved that as a temporary security exception limited to the functional and ABI checks this PR adds. It covers those jobs' runs after merge too, until its end conditions are met, and the owner reviews it by 2026-11-08. The jobs run only for a pull request from a branch of this repository and for a manual run onmain. A fork's pull request that leaves the job condition unchanged skips them: that is an intentional not-run, not a pass. The condition is not a control against a malicious fork, which can edit it (see Security Impact). Their records are validated with--no-policyand say that the policy was skipped, security is not verified, and compatibility is incomplete. The default privilege policy is unchanged.When a lane cannot install the lock, its record says why:
blocked, naming the missing wheel or the requirement the lock does not pin. The later stages are thenblocked, and the job stays green unless the lane's profile requires those stages. An install failure the probe cannot explain from the installer output is recorded asfailedand fails the job.Changes / Scope
Requested in the project thread linked above.
Workflows
.github/workflows/compatibility.yml(new)platform, 4 lanes: linux-x64, linux-arm64, macos-arm64, macos-x64.platform-windows, 3 lanes: windows-x64, windows-arm64, and windows-arm64-x64 (x64 Python and Node emulated on Windows on Arm). It runs the same steps and env asplatformthrough YAML aliases.ubi, 4 lanes: UBI 9.8 and UBI 10.2 on x86-64 and Arm64. Images are pinned by digest. Podman must report rootless, or the lane recordsblockedand fails, with no Docker, rootful orsudofallback. Containers run with--userns=keep-id,--cap-drop=allandno-new-privileges, no--privilegedand no devices. The image's system Python is recorded but not used; every lane uses uv's CPython 3.12.15..github/workflows/python-abi.yml(new):abion the 4 Linux and macOS runners andabi-windowson the 2 Windows runners, each for bothcp315andcp315t(8 + 4 jobs). Only these jobs use uv 0.13.0. The backend jobs keep uv 0.12.24.platform-windows,abi-windows):github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository, orgithub.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main'. It reads only thegithubcontext, because a job'sifis evaluated before its matrix is expanded;--no-policy. The record getsx-policy-exception(policyskipped, securitynot-verified, compatibilityincomplete, non-administrator runnot-run), and thesecuritystage reason says the same. A warning annotation and the step summary repeat it;platformorabifails the job at its first run step;platform-windowsorabi-windowsas their evidence source.pull_requesttomainwith a path filter, and onworkflow_dispatch;contents: read, usepersist-credentials: false, and pin every action to a full commit SHA;continue-on-error;ci.ymlis unchanged.Scripts
scripts/ci/collect_environment.pyIsWow64Process2, the BIOS registry values and the token (elevated, Administrators, LocalSystem);unknown, because token privileges are not read;--amend-container, which adds the engine's view of a container (rootless mode, privileged flag, devices);--extension, which attaches JSON reports such asx-python-abi,x-frontendandx-policy-exception.scripts/ci/probe_python_abi.py(new):wheels: for each locked package, records which of thecp315-cp315,cp315-cp315t,cp315-abi3,cp315-abi3tandcp315-abi3.abi3ttags are published, and whether the interpreter accepts them (yanked files and files whose Requires-Python excludes the interpreter are not counted);packages: installs each lock-hashed binary wheel with--no-deps, then imports and calls it in a freshpython -Iprocess, recording the GIL state before and after. A package fails the job when it re-enables the GIL, or when its process exits with a nonzero status or a signal, even after printing a passing result. The report records the exit (exit: status, return code, signal name) and the step summary shows it in an Exit column;install-stage: decides betweenblockedandfailedfrom the uv log, theuv pip checkoutput and the requirement report.Tests and profiles
scripts/ci/tests/test_environment_probe.py(extended) andscripts/ci/tests/test_python_abi_probe.py(new, 35 tests). The exit tests cover a clean exit, a nonzero exit after the result (os._exit(3)from anatexithook), a signal after the result (SIGKILL, skipped on Windows), a process that prints no result, and mocked return codes 0, 3, -11, -200 and0xC0000005.tests/compatibility/profiles/gha-*.json: 23 new real profiles, 24 with the existinglinux-x64-gha.json.Docs
465bea6changes only this section and the Triggers sentence that points to it (see Notes).Out of scope, unchanged
backend/locks/*andfrontend/package-lock.jsonhave no diff. No new production package, no lock refresh, no lowered security floor, no source builds, and no renamed wheels or tags.scripts/ci/validate_compatibility.py,tests/compatibility/schema.json) have no diff.Implementation Status
SEC-*status changes. The diff cites noSEC-*ID.docs/DEPENDENCY-STRATEGY.md, or recorded asnot-assessed.Validation
CI on the final head
Head
465bea6(one doc-only commit on top ofc2e2f59:docs/SECURITY-ARCHITECTURE.md, 9 insertions and 8 deletions): 30/30 checks passed.ci.yml, unchanged)platform4,platform-windows3,ubi4)abi8,abi-windows4)The Windows jobs ran because this pull request's head branch is in this repository. All 11 Compatibility records of
465bea6have the same stage results,overallandsuperuseras those ofc2e2f59, and the threeplatform-windowsrecords still holdx-policy-exception.Checked in the artifacts and logs of
c2e2f59(CI 38225035053, Compatibility 38225034884, Python ABI 38225034916, also 30/30), whose code465bea6keeps unchanged:286dff0(table below). Each record names the job that wrote it (platform,platform-windowsorubi).platform-windowsrecords and the Python ABI records of windows-x64 cp315 and windows-arm64 cp315t holdx-policy-exceptionand the "not verified" security reason. The linux-x64 records have neither.valid result (0 errors, 1 warnings)withpolicy-skipped.exitpassedwith return code 0. Thepackagesstep fails on any failed exit, and it passed in all 12 jobs.Lane results (CPython 3.12.15, hashed lock)
tzdata, which django and psycopg require on win32incompleteeverywhere: CI runs no security or performance stage.superuser: true, validated with--no-policy, and reported aspolicy-skipped, under the exception below.CPython 3.15.0 and 3.15t
blockedon every job, because some locked packages publish no usable wheel. Every package that installed was imported and called successfully, and its process exited 0.cp315-abi3.abi3twheel where one exists.cp315torabi3twheel.Earlier heads
c2e2f59: 30/30 passed (runs above).286dff0: 30/30 passed (CI 38215048118, Compatibility 38215048102, Python ABI 38215048106).2a45b6f: the four Windows Python ABI jobs failed in a new test that wrote the lock withwrite_text, which gives CRLF line endings on Windows.286dff0writes it as bytes.Local runs (author, self-assessment)
scripts/ci/tests/test_python_abi_probe.py: 35 OK on CPython 3.12.15 (2 skipped) and on 3.15.0 and 3.15t (1 skipped each). The new exit tests fail against the probe of286dff0.scripts/ci/tests/test_compatibility_schema.py: 95 OK.scripts/ci/tests/test_environment_probe.py: 84 OK.scripts/ci/tests/test_check_test_lock.py: 49 OK.validate_compatibility.py --require-real --dependency-strategy docs/DEPENDENCY-STRATEGY.md tests/compatibility/profiles/*.json: 24 valid profiles, 0 errors.GITHUB_JOBandRUNNER_OS:--no-policy, the warning, the summary andx-policy-exceptionappear only forplatform-windowsorabi-windowson Windows; the guard fails only a Windows runner inplatformorabi.git diff --check: clean.465bea6:git diff --checkclean, no em or en dash in the changed file, the section anchor unchanged, andgit show --statlists onlydocs/SECURITY-ARCHITECTURE.md.Not run
main:workflow_dispatchneeds the workflow on the default branch, so it cannot run before merge.Reviews
c2e2f59for code and security once, along five lines: the probe's exit handling, the Actions job gating, security per SECURITY-REVIEW.md (including the step 7 secret-keyword check, whose hits were all prose about tokens or the secret-shape checker), documentation accuracy, and records and validation. It found no P0, P1 or P2 issue. It recorded 15 P3 findings (11 distinct), left unfixed as the owner asked; the owner then asked for the four about the exception section's wording, which465bea6fixes. See Notes.465bea6doc change before it was committed, along three lines: accuracy against the workflows and the validator, the owner's instructions and scope, and security claims. Each P0-P2 finding went to a separate agent that tried to refute it. One P2 survived, reported three times: the text said a repository setting could add an approval gate to manual runs, but environment protection rules apply only to jobs that name an environment, and these jobs name none. It is fixed in465bea6. Of its P3 findings, four small ones about the same section's wording are applied (the approval is "for", not "in", pull request PR C: multi-platform and Python 3.15/3.15t ABI compatibility lanes #19; the reviewer is named as Codex; "at least" two things for the end condition; nothing reads the review date), one sentence it judged an unrequested new rule is dropped, and the rest are not applied; see Notes.9f0a8d9(no P0 or P1; its 3 P2 and 5 of 7 P3 fixed in2a45b6f), and Codex automated reviews off7ccd3d(5 findings, 4 fixed and resolved, 1 open; see Notes).Security Impact
scripts/ci/*/tests/compatibility/*. SECURITY-REVIEW.md maps that trigger toSEC-SECRET-001as the primary area. This PR claims no status for it.pull_requestandworkflow_dispatchonly, with nopull_request_targetorworkflow_run;contents: read, and no secret or OIDC token is referenced;runs-onlabels are GitHub-hosted (ubuntu-24.04,ubuntu-24.04-arm,windows-2025,windows-11-arm,macos-15,macos-15-intel).docs/SECURITY-ARCHITECTURE.md"Temporary exception: Windows administrator token"):main. A manual run needs write access to the repository, and nothing else gates it: no job names an environment, so a repository setting alone cannot add a reviewer;pull_requestrun uses the workflow file of the merge commit, so a fork's pull request that edits the condition runs the jobs as edited. Whether it starts without a maintainer's approval depends on the repository's GitHub Actions setting for fork pull request workflows. By GitHub's default only first-time contributors need approval; the level this repository uses was not checked, and changing it is the owner's decision. Beyond that, the read-only token is the limit;no-new-privileges, no--privilegedor devices. The writable mounts are the checkout and the job's records directory. uv and the event file are mounted read-only.c2e2f59, and of the465bea6doc change, which adds no code (above). The Codex security review off7ccd3dis older.Notes / Risks
Owner decision (approval basis of the exception). On 2026-10-12 08:20Z, in the project thread linked above, the repository owner (김제석) decided:
In English: the GitHub-hosted Windows administrator token is approved as a temporary security exception, limited to PR C's functional and ABI checks; it does not mean a non-administrator run was verified. The same decision limits Windows runs to this repository's pull requests and manual runs on
main(started by a user with write access; nothing else gates them), keeps--no-policyto those Windows jobs, keeps the Rootless First default policy, and moves a non-administrator Windows environment to a follow-up.Exception review date: 2026-11-08, confirmed by the owner on 2026-10-12. It is also the recheck date of the dependency exceptions in DEPENDENCY-STRATEGY.md. Nothing in the workflows reads the date: the jobs keep running with
--no-policyuntil a change removes it.Codex finding on the Windows administrator token: an owner-approved, time-limited security exception, not a code fix. The finding asked for the Windows lanes to run under a non-administrator token. No code change does that: GitHub-hosted Windows runners offer no such token. The owner accepted it as the temporary exception above, and the exception section records it so (
465bea6). Its review thread is left open and unanswered, because replying to it or resolving it is not part of what this session was asked to do; that is the owner's step.P3 findings of the review of
c2e2f59. At the owner's request,465bea6fixes the four about the exception section's wording:overall"staysincomplete"; it now saysoverallis neverpassed:incomplete, orfailedwhen a stage failed;app-non-root) and a collector that reads Windows token privileges (minimal-capabilities), since Windows capabilities are recorded asunknowntoday;The other P3 findings are left as they are, as the owner asked:
--stage security=not-runargument (compatibility.yml 221, python-abi.yml 145);frontend.jsonandpolicy-exception.json;importandcallstops thepackagescommand with a traceback (fails closed);Fork wording outside the exception section, not changed here. These still say that a fork's pull request skips the Windows jobs, without the caveat that one which edits the condition runs them: CI-MATRIX.md line 127, TESTING.md lines 165 and 170, CODE-REVIEW.md line 10, and the job comments at compatibility.yml line 277 and python-abi.yml line 180. None of them claims the condition stops a malicious fork, and the documents link to the exception section. The owner limited this round to that section, so they are left for a follow-up if wanted.
install-stagerelies on the format of uv 0.12.24'suv pip checkoutput. If that format changes, a lane that would beblockedreads asfailedinstead and its job fails.tzdataon Windows: whether a platform lock is wanted is left as a separate decision. This PR only records the evidence.Missing wheels for macOS Intel and Windows Arm64, and missing 3.15/3.15t wheels, are upstream gaps. Re-locking does not fix them.
Not merged here; the owner merges. PR D is not started.
🤖 Generated with Claude Code
https://claude.ai/code/session_01CGH7FAsq1H9HmCR27Cj2Ao