Keel
Grounding report.
Every verification edge in requests, classified by who produces the signal and whether the actor being verified can write to that producer.
| target | requests |
|---|---|
| revision | 69f84847045bef7a849cc994a26fe7ba8a169e95 |
| generated | 2026-07-25T03:13:36.804Z |
anchored / (anchored + self_referential + unknown) = 11 / 12
Coverage (judged)
| node kind | judged |
|---|---|
| ci_step | 10 |
| script | 10 |
| test_target | 3 |
| review_gate | 2 |
ε-audit — not run
No verdict in this report carries a readable audit block, so nothing was re-decided and there is no agreement rate to report. This is not agreement, and it is not full agreement either: a rate over zero comparisons would state a measurement that was never taken. (“Readable” is load-bearing: a block can be present and state no comparison, and this state covers that too — see below if so.)
This run judged 25 of 106 gathered edges. Every number on this page describes that sample, not the whole surface — 81 gathered edges are absent from all of it. A cap is disclosed rather than smoothed: an undisclosed sample is how a ratio gets shopped.
Scope. Keel measures the shape of verification, not its quality. A repo can be 100% anchored with terrible tests. Anchoring says the signal comes from outside; it does not say the signal is sufficient.
Node graph — 25 node(s), in gather order
anchored — 11
This hook list is not inert declaration: .github/workflows/lint.yml:21-24 installs a pinned pre-commit==4.6.0 and runs `pre-commit run --show-diff-on-failure --all-files` on every push and pull_request, so each hook here executes against the checked-out source and a non-zero hook exit fails the job. ruff-check and ruff-format carry `--fix`/rewrite semantics, but under pre-commit a hook that MODIFIES a file is reported as a failure (file hashes change), so an unformatted or lint-violating file cannot report clean without being changed. The hook implementations are fetched from external repos at pinned revs, so an author cannot alter what check-yaml or ruff decides without moving the pin. Limit: the rev pins and the exclude regex ('docs/|ext/') live in this file inside the write boundary, so the SCOPE of enforcement is author-controlled even though each verdict is computed outside it.
producer the `pre-commit` process exit code in the Lint workflow, which is the aggregate of pinned third-party hook binaries (pre-commit-hooks v6.0.0, ruff v0.15.21) run over the committed tree — actor can write to it: no
- .pre-commit-config.yaml:5
- .pre-commit-config.yaml:16-21
- .github/workflows/lint.yml:21-24
decided by agent · confidence 0.82
The recipe body is a direct `python -m pytest tests` invocation, so the target's exit status IS the interpreter's verdict on running the committed test modules against src/requests; a failing assertion or an import error cannot be talked into green without editing the code or the tests. Two limits that must travel with this: (1) the ORACLE is inside the write boundary — the same maintainers author src/requests and tests/, so a green run proves the code does what these tests say, not that the tests say the right thing; (2) this particular target is NOT the one CI invokes — .github/workflows/run-tests.yml:37-39 runs `make ci`, so `make test` gates no merge on its own and its value is developer-local execution.
producer the pytest process exit code — CPython executing tests/ and returning non-zero on a failed assertion, error or collection failure — actor can write to it: no
- Makefile:4-5
- .github/workflows/run-tests.yml:37-39
decided by agent · confidence 0.78
`make ci` is `python -m pytest tests --junitxml=report.xml`, and it is the command every test job in run-tests.yml actually runs (lines 37-39, 59-61, 81-83) across a 3-OS x 8-interpreter matrix plus the no-chardet and urllib3-1.x jobs, on push and pull_request. The pass/fail signal is the interpreter's exit status while executing real code — requests' suite drives a live pytest-httpbin server and real TLS sockets, so failures come from actual socket/HTTP behaviour, not from a recorded claim. The `--junitxml` file is only a report artefact; nothing gates on its contents. Limit: execution is anchored, the oracle is not — implementation and assertions are co-authored in this repo, so drift in both directions stays green.
producer the pytest process exit code produced by CPython executing the tests/ suite inside the GitHub Actions runner — actor can write to it: no
- Makefile:7-8
- .github/workflows/run-tests.yml:37-39
- .github/workflows/run-tests.yml:59-61
- .github/workflows/run-tests.yml:81-83
- requirements-dev.txt (pytest-httpbin==2.1.0, trustme)
decided by agent · confidence 0.9
The recipe runs the real suite under coverage; the target's exit status is pytest's verdict on executing the committed tests, and the coverage numbers themselves are derived from the interpreter's line-execution trace, which no one in the repo authors. Limits, all load-bearing here: (1) .coveragerc declares only `omit`, no `fail_under`, so the coverage measurement itself gates NOTHING — it prints a table and writes coverage.xml and the only failure path is a test failing; (2) coverage measures reach, not correctness; (3) no workflow invokes `make coverage`, so this edge blocks no merge — CI runs `make ci` instead.
producer the pytest process exit code (with coverage.py tracing attached) — CPython executing tests/ and coverage.py recording which lines the interpreter actually reached — actor can write to it: no
- Makefile:13-14
- .coveragerc:1-2
- .github/workflows/run-tests.yml:37-39
decided by agent · confidence 0.7
This table is not inert: ruff-check and ruff-format read it on every `pre-commit run --all-files` in .github/workflows/lint.yml:21-24, and target-version/src/exclude change which files are analysed and which pyupgrade rewrites apply — so a py310-incompatible construct in src/requests produces a non-zero ruff exit that an author cannot argue away without changing the file. Limit, and it is the sharp one for a config node: the analysis is executed outside the write boundary but the SCOPE is declared inside it — widening `exclude` (already 'docs/', 'ext/') silently shrinks what the gate can ever see, and this table is one of the paths that does that.
producer the ruff binary (pinned at ruff-pre-commit v0.15.21) parsing the committed source under this configuration, and its exit status inside the Lint job — actor can write to it: no
- pyproject.toml:86-90
- .pre-commit-config.yaml:16-21
- .github/workflows/lint.yml:21-24
decided by agent · confidence 0.55 · confidence below 0.6 on an anchored verdict
The `select` list here is what ruff-check enforces when pre-commit runs it over --all-files in lint.yml on every push and PR; pyflakes F catches undefined names and unused imports, T10 catches a forgotten breakpoint()/pdb.set_trace(). The verdict for a given file is computed by the pinned ruff binary from the committed bytes, so a violating file cannot report clean without being edited. Limit: `ignore` and `per-file-ignores` sit in this same table inside the write boundary — the author chooses which rules exist, and only then does an outside process decide whether they hold.
producer ruff's exit code — a pinned external analyser deciding, from the source text, whether a selected rule (E/W/F/I/UP/T10) is violated — actor can write to it: no
- pyproject.toml:91-102
- .pre-commit-config.yaml:18-19
- .github/workflows/lint.yml:21-24
decided by agent · confidence 0.78
`known-first-party = ["requests"]` parameterises the I rules that .github/workflows/lint.yml runs through ruff-check; with it, an import block ordered wrongly relative to first-party/third-party grouping is rewritten by `--fix`, pre-commit sees a modified file and fails the job. The decision about whether a given file's imports are correctly grouped is made by ruff executing over the file, not by any claim in the repo. Limit: this is a one-line scoping declaration for a rule enforced elsewhere, so it contributes almost nothing on its own — its anchoring is entirely borrowed from the ruff-check invocation, and I have kept confidence low to say so.
producer ruff's isort (I) rule evaluation inside the pinned ruff binary, surfaced as a pre-commit hook failure — actor can write to it: no
- pyproject.toml:103-104
- .pre-commit-config.yaml:18-19
- .github/workflows/lint.yml:21-24
decided by agent · confidence 0.55 · confidence below 0.6 on an anchored verdict
Every `make ci` run in the CI matrix loads this table, so it directly determines what the interpreter executes and therefore what the exit code reflects. `addopts = "--doctest-modules"` is a genuine widening of the executed oracle: examples written in docstrings are run and their printed output compared to the literal transcript, so a documented example that stops matching real behaviour fails the suite. Limits: `testpaths = ["tests"]` is author-controlled scope — narrowing it silently shrinks what the anchored runner can ever see — and the doctest transcripts are still authored in-repo alongside the code they document.
producer the pytest collector/runner inside CPython, which this table configures — testpaths decides what is collected and --doctest-modules makes docstring examples executable assertions — actor can write to it: no
- pyproject.toml:111-116
- Makefile:7-8
- .github/workflows/run-tests.yml:37-39
decided by agent · confidence 0.72
This table is the operative configuration for `python -m pyright src/requests/` at .github/workflows/typecheck.yml:31-32, which runs on every push and pull_request against Python 3.10 and 3.14. Strict mode makes pyright report unknown/partially-unknown types, so an unannotated or type-incoherent change produces a non-zero exit computed by the checker from the source and the installed stubs — nothing an author can assert their way past without changing the code or loosening this table. Limit: the four `report*: false` suppressions and the `include` list are declared here, inside the write boundary, so the strictness that gets enforced is author-chosen; the verdict under that choice is not.
producer the pyright type checker's process exit code — deterministic static analysis of src/requests under `typeCheckingMode = "strict"` — actor can write to it: no
- pyproject.toml:118-125
- .github/workflows/typecheck.yml:31-32
- .github/workflows/typecheck.yml:3
decided by agent · confidence 0.85
The step shells out to a build or packaging command (python -m build, poetry/hatch/flit build, cargo build, go build, dotnet build, mvn package) which compiles or assembles the committed source. The verdict is computed by that toolchain from the tree: malformed metadata, an unresolvable dependency specifier, a type/compile error, or a layout that does not resolve all produce a non-zero exit, and an author cannot make an unbuildable tree report success without changing the tree. Documented limit: a green build proves the artefact compiles and packages, not that it behaves correctly — it says nothing about runtime semantics.
producer the language's build/packaging backend executing over the checked-out tree, and its process exit code — actor can write to it: no
- .github/workflows/publish.yml:47-49
decided by probe (packaging-build-invocation) · confidence 0.82
`commands = pytest {posargs:tests}` is a direct runner invocation, and tox additionally builds and installs the package into an isolated env before running it, so packaging mistakes (a module missing from the sdist, an unsatisfiable extra) surface as failures too. The verdict is the interpreter's, not a claim the repo makes about itself. Limits: the oracle is co-authored with the implementation as always; and nothing in .github/workflows invokes tox — every CI job shells `make` then `make ci` — so this envlist gates no merge and its 3.10-3.14 matrix is duplicated, not enforced, by run-tests.yml.
producer the pytest process exit code inside each tox-managed virtualenv — CPython 3.10-3.14 executing tests/ against a freshly installed requests plus its security/socks extras — actor can write to it: no
- tox.ini:1-9
- .github/workflows/run-tests.yml:33-39
decided by agent · confidence 0.75
unknown — 1
The file declares that changes to .github/workflows/, CODEOWNERS, __version__.py, HISTORY.md and pyproject.toml belong to two named maintainers, but a CODEOWNERS file only blocks a merge when branch protection has 'Require review from Code Owners' switched on, and that setting lives in GitHub repository configuration that is not in the tree and cannot be read from a clone. So the fork point is untraceable: if protection is on, the producer is a second human's approval outside the PR author's write boundary; if it is off, the file is documentation and produces no signal at all. Failing closed rather than guessing anchored, per the rule for checks whose implementation lives outside the repo.
producer cannot be established from the repository — either a second maintainer's review approval, or nothing at all — actor can write to it: not established
- .github/CODEOWNERS:4-8
decided by agent · confidence 0.7
not_a_check — 13
The command installs declared dependencies so that later steps can run. Without a lockfile-freezing flag the resolver will refresh a stale lock rather than error, so the step cannot report a discrepancy between the manifest and the lock; its non-zero exits mean the network or the index failed, not that the repository is wrong. The assertion in such a job lives in whatever executes against the installed environment afterwards.
producer a package manager's dependency resolver populating an environment — actor can write to it: not established
- the run command is a bare dependency install with no --locked/--frozen equivalent
decided by probe (unfrozen-dependency-install) · confidence 0.8
The body is `python setup.py check --restructuredtext --strict && ([ $$? -eq 0 ] && echo ok) || echo "Invalid markup ..."`. If setup.py check exits non-zero the `&&` short-circuits and control falls to `|| echo "Invalid markup..."`, which succeeds — so the recipe's exit status is 0 whether the markup is valid or broken, and the only evidence of failure is a line of stdout nobody reads. A signal that cannot vary carries no information, which is what disqualifies it as a check rather than anything about the producer. Compounding it: the message names README.rst/HISTORY.rst, which no longer exist (README.md, HISTORY.md), and no workflow invokes this target.
producer `echo` — the recipe's last command in every branch, so the shell always hands make a zero exit status — actor can write to it: no
- Makefile:10-11
- README.md
- HISTORY.md
decided by agent · confidence 0.85
The step's entire effect is promotion: it takes distributions an earlier step produced and uploads them to an index (PyPI, npm, crates.io, a container registry, a GitHub release). It evaluates nothing about the artefact — a failure means the upload was rejected (duplicate version, bad credentials, registry outage), not that the code is wrong. Whatever verification exists lives in the job's `needs:` / environment condition, which is a different edge, so this one must not enter the ratio in either direction.
producer a release/upload command handing a already-built artefact to a package registry — actor can write to it: not established
- .github/workflows/publish.yml:50-51
decided by probe (artifact-publish-upload) · confidence 0.8
This target generates a documentation artefact; it asserts nothing about whether requests behaves correctly. SPHINXOPTS is empty in docs/Makefile:5, so sphinx runs without `-W` and its usual findings — broken cross-references, stale autodoc targets, missing toctree entries — are warnings that still exit 0. Nothing downstream consumes the output either: Read the Docs builds from .readthedocs.yaml with its own sphinx invocation rather than this recipe, and no workflow calls `make docs`. Filed here as generation/provisioning rather than `unknown` because the code path is fully readable, not because it was hard to judge.
producer sphinx-build, invoked through docs/Makefile's html target, writing HTML into docs/_build — actor can write to it: no
- Makefile:25-27
- docs/Makefile:5
- .readthedocs.yaml:14-17
decided by agent · confidence 0.62
This table selects a style dialect (quote-style, indent-style) for a REWRITER. Nothing about correctness is asserted by either value: any program is equally valid with either quote character, and swapping them changes no behaviour the interpreter can observe. The pass/fail edge that exists in this repo lives in the pre-commit invocation (a formatter rewrite becomes a hook failure) and is credited there under .pre-commit-config.yaml — counting this table again would credit the same gate twice while the table itself asserts nothing.
producer ruff format's rewriter, which restyles source text to double quotes and space indentation — actor can write to it: no
- pyproject.toml:106-109
- .pre-commit-config.yaml:20-21
decided by agent · confidence 0.7
There is not one assertion in this file: every top-level definition is a fixture that PROVISIONS the environment the real tests run in (a live local HTTP server, a no-SAN certificate, a clean proxy environment). It cannot pass or fail a claim about requests' behaviour; a broken fixture surfaces as an error in whichever test consumes it, and that test's edge is the one already credited under `make ci`. Worth recording rather than dismissing: this provisioning is exactly what makes the pytest edge strong here — a genuine TLS handshake against a real socket is what the runtime gets to decide — but the assertion lives in tests/*.py, not here.
producer pytest fixture functions — an httpbin URL helper, a monkeypatch that clears proxy env vars, and a threaded TLS HTTPServer built from a trustme CA — actor can write to it: no
- tests/conftest.py
- requirements-dev.txt (pytest-httpbin, trustme)
decided by agent · confidence 0.68
The recipe creates a test fixture — a key file — and evaluates no property of requests. Key generation succeeds for any tree state, so its exit code carries no information about the code under test; it is provisioning for the TLS fixtures the suite consumes, and the assertions that use those fixtures live in tests/ under the pytest edge.
producer `openssl genrsa`, generating a 2048-bit RSA private key file — actor can write to it: no
- tests/certs/valid/server/Makefile:3-4
decided by agent · confidence 0.9
A CSR-generation step in a test-fixture directory. It transforms one fixture file into another and asserts nothing about requests' behaviour; a non-zero exit here would mean openssl or cert.cnf is broken, not that the library is wrong.
producer `openssl req`, emitting a certificate signing request from the generated key and cert.cnf — actor can write to it: no
- tests/certs/valid/server/Makefile:6-7
decided by agent · confidence 0.9
This mints the server certificate the TLS tests present; it is fixture construction, not verification. The signing operation succeeds regardless of anything in src/requests, so the recipe's exit code says nothing about the library. Note it is fixture MATERIAL for an anchored edge — the actual verification is a real TLS handshake performed inside the pytest run — but the material itself asserts nothing.
producer `openssl x509 -req`, signing the CSR with the checked-in test CA key and concatenating the CA cert into the bundle — actor can write to it: no
- tests/certs/valid/server/Makefile:9-11
decided by agent · confidence 0.88
The step's whole effect is to put files or a toolchain on the runner so later steps have something to act on. It reads nothing about the repository's correctness and emits no pass/fail verdict about the artefact — a failure here means the runner could not fetch or install, not that the code is wrong. The check in such a job is whatever executes afterwards, so this edge must not enter the ratio in either direction.
producer a GitHub Actions provisioning action (repository checkout, toolchain setup, or dependency cache restore) — actor can write to it: not established
- the step body is a bare `uses:` of a checkout/setup/cache action with no command of its own
decided by probe (ci-provisioning-action) · confidence 0.9
The step's whole effect is to put files or a toolchain on the runner so later steps have something to act on. It reads nothing about the repository's correctness and emits no pass/fail verdict about the artefact — a failure here means the runner could not fetch or install, not that the code is wrong. The check in such a job is whatever executes afterwards, so this edge must not enter the ratio in either direction.
producer a GitHub Actions provisioning action (repository checkout, toolchain setup, or dependency cache restore) — actor can write to it: not established
- the step body is a bare `uses:` of a checkout/setup/cache action with no command of its own
decided by probe (ci-provisioning-action) · confidence 0.9
The command installs declared dependencies so that later steps can run. Without a lockfile-freezing flag the resolver will refresh a stale lock rather than error, so the step cannot report a discrepancy between the manifest and the lock; its non-zero exits mean the network or the index failed, not that the repository is wrong. The assertion in such a job lives in whatever executes against the installed environment afterwards.
producer a package manager's dependency resolver populating an environment — actor can write to it: not established
- the run command is a bare dependency install with no --locked/--frozen equivalent
decided by probe (unfrozen-dependency-install) · confidence 0.8
The step's whole effect is to move a file the pipeline itself produced from one job to another. Nothing about the repository is evaluated: with `if-no-files-found: error` the worst it can report is that an earlier step in the same run did not leave the file it claimed to leave, which is a fact about the plumbing, not about whether the code is correct. Whatever assertion exists in such a job is made by the step that produced the artifact or the step that consumes it, so this edge must not enter the ratio in either direction.
producer a GitHub Actions artifact transport action (upload-artifact / download-artifact) moving files between jobs — actor can write to it: not established
- the step body is a bare `uses:` of an artifact upload/download action with no command of its own
decided by probe (ci-artifact-transport) · confidence 0.7
Crystallization curve
Run economics