Keel

Grounding report.

Every verification edge in flask, classified by who produces the signal and whether the actor being verified can write to that producer.

targetflask
revision36e4a824f340fdee7ed50937ba8e7f6bc7d17f81
generated2026-07-25T03:33:58.626Z
1.00

anchored / (anchored + self_referential + unknown) = 15 / 15

anchored 15 self_referential 0 unknown 0 not_a_check 10 excluded from the denominator

Coverage (judged)

node kindjudged
ci_step12
test_target11
review_gate1
script1

Nodes by kind, over the 25 node(s) carried in this report. Surfaces the gatherer cannot read are absent here rather than counted as unknown, which makes non-coverage Keel's own shoppable class — so it is stated, not implied.

ε-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 carries 0 probe-decided verdict(s), none of them contributing a readable comparison. (“Readable” rather than “audited”, because a block can arrive and state nothing — if any did, they are counted below.) Audit coverage is itself shoppable — audit none of the library and no disagreement can ever be found — so the absence is printed here rather than left to be inferred from a missing section.

This run judged 25 of 59 gathered edges. Every number on this page describes that sample, not the whole surface — 34 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 15 not_a_check 10

2 marks are ringed in the unknown hue: an anchored verdict asserted below confidence 0.6. The ring says the claim is closer to unknown than its colour suggests. Hover a mark for its node.

anchored — 15

The producer sits outside the write boundary of the actor being verified.

pre-commit hooks (.pre-commit-config.yaml)
anchored
.pre-commit-config.yaml · review_gate

The pre-commit workflow runs `uv run --locked --no-default-groups --group pre-commit pre-commit run --show-diff-on-failure --color=always --all-files` on every pull_request and on pushes to main/stable, so this config is the live gate rather than a local convenience. Each hook is executed from an external repository pinned by full SHA (`astral-sh/ruff-pre-commit` at 5e2fb545…, `codespell-project/codespell` at 2ccb47ff…, `pre-commit/pre-commit-hooks` at 3e8a8703…), so a flask author cannot make a violating file report clean without changing the file or changing the pinned rev — the deciding programs are outside their write boundary. The rewriting hooks (ruff-format, codespell --write-changes, trailing-whitespace, end-of-file-fixer) still produce a falsifiable signal because pre-commit exits non-zero when a hook modifies a file. Documented limits: (1) this is an AGGREGATE — its exit code is only as anchored as its children, and I descended by hook id and pinned rev rather than by reading each hook's source; (2) the rule set (ruff select list, codespell ignore-words) lives in the repo, so scope is author-controlled even though each verdict is not.

producer the `pre-commit` process exit code, aggregating the exit status of hook binaries fetched from third-party repositories pinned to full commit SHAs — actor can write to it: no

  • .pre-commit-config.yaml:1-27
  • .github/workflows/pre-commit.yaml:28 (`pre-commit run --show-diff-on-failure --all-files`)
  • pyproject.toml:167-168 ([tool.codespell])

decided by agent · confidence 0.85

pytest config ([tool.pytest.ini_options])
anchored
pyproject.toml:106 · test_target

This ini block is the live configuration of the suite that the Tests workflow runs: the matrix job executes `uv run --locked --no-default-groups --group dev tox run` with TOX_ENV per interpreter, and every tox env's command is `pytest -v --tb=short --basetemp=...` (pyproject.toml:190-193). testpaths=["tests"] selects which modules the interpreter imports and `filterwarnings = ["error"]` promotes any DeprecationWarning raised at runtime into a test failure, so the block widens rather than suppresses what can fail. A failing assertion, an import error or a warning cannot be argued into a zero exit status. Documented limit — the EXECUTION is anchored, the ORACLE is not: tests/ and src/flask/ are written by the same maintainers, so a green run proves the code does what these tests say, not that the tests say the right thing.

producer the pytest process exit code — the CPython/PyPy interpreter executing the modules under tests/ against the built flask wheel — actor can write to it: no

  • pyproject.toml:106-110
  • pyproject.toml:190-193 ([tool.tox.env_run_base] commands = pytest)
  • .github/workflows/tests.yaml:40 (`tox run`)

decided by agent · confidence 0.85

linter/type-checker config ([tool.mypy])
anchored
pyproject.toml:126 · ci_step

This table is the live configuration of a gate that runs: the `typing` job in the Tests workflow executes `uv run --locked --no-default-groups --group dev tox run -e typing`, and that env's first command is bare `mypy` (pyproject.toml:242-245), which picks up exactly this table. `strict = true` over files=["src","tests/type_check"] means the verdict is computed by the checker from the source text; an author cannot make an ill-typed function report clean without changing the code or loosening this pinned config. Documented limit: the RULE SET is author-controlled — `[[tool.mypy.overrides]] ignore_missing_imports = true` for asgiref/dotenv/cryptography (pyproject.toml:133-140) removes those modules from the checked surface — so this is anchored on the execution axis while its scope sits inside the write boundary.

producer the mypy process exit code — a type checker parsing the committed source under src/ and tests/type_check and returning non-zero on a type error — actor can write to it: no

  • pyproject.toml:126-131
  • pyproject.toml:239-245 ([tool.tox.env.typing] commands = mypy, pyright)
  • .github/workflows/tests.yaml:62 (`tox run -e typing`)

decided by agent · confidence 0.88

linter/type-checker config ([tool.pyright])
anchored
pyproject.toml:142 · ci_step

The `typing` tox env runs `pyright` as its second command and the Tests workflow's typing job invokes that env, so this table configures an executing gate rather than an aspiration. Pyright is a different implementation from mypy (Microsoft's TypeScript-based checker vs. the Python one), so the two commands in the env are not correlated implementations of the same analysis — a bug in one is unlikely to be reproduced by the other. The verdict is computed from the source; changing it requires changing the code or this pinned config. Documented limit: `typeCheckingMode = "basic"` (not "strict") is an author-chosen scope, so the amount of the surface this gate can fail on is inside the write boundary even though each diagnostic is not.

producer the pyright process exit code — a second, independently-implemented type checker analysing the committed source and returning non-zero on a diagnostic — actor can write to it: no

  • pyproject.toml:142-145
  • pyproject.toml:242-245
  • .github/workflows/tests.yaml:44-62

decided by agent · confidence 0.85

linter/type-checker config ([tool.ruff])
anchored
pyproject.toml:147 · ci_step

This table configures the ruff invocation that the pre-commit workflow actually runs on every PR via the `ruff-check` / `ruff-format` hooks pinned at astral-sh/ruff-pre-commit rev 5e2fb545…. `fix = true` means ruff rewrites what it can rather than only reporting — but under `pre-commit run --all-files` a hook that modifies a file makes pre-commit exit non-zero, and unfixable violations exit non-zero directly, so the edge remains falsifiable in both branches. `src = ["src"]` fixes first-party import resolution for the isort rules. The deciding program is a pinned external binary, not a claim the repo makes about itself. Documented limit: which rules are enforced is author-controlled (see [tool.ruff.lint]).

producer the ruff binary's exit code (and pre-commit's file-modification detection wrapping it), executed from a SHA-pinned third-party hook repo against the committed tree — actor can write to it: no

  • pyproject.toml:147-151
  • .pre-commit-config.yaml:2-6 (ruff-check, ruff-format at frozen rev)
  • .github/workflows/pre-commit.yaml:28

decided by agent · confidence 0.75

linter/type-checker config ([tool.ruff.lint])
anchored
pyproject.toml:153 · ci_step

This is the rule-selection table that decides what makes ruff exit non-zero, and ruff is executed on every pull request by the pre-commit workflow through a SHA-pinned hook. F (pyflakes) and B (bugbear) are genuine defect rules — an undefined name or a mutable default argument is detected by the binary parsing the file, and no amount of assertion in the repo makes a violating file report clean without editing the file or this select list. Documented limit: the select list is itself committed, so an author can shrink the enforced surface by editing this table; the individual verdicts are outside the write boundary, the scope is not.

producer the ruff binary's exit code — a linter executing the selected rule set (bugbear, pycodestyle, pyflakes, isort, pyupgrade) over the committed source — actor can write to it: no

  • pyproject.toml:153-161
  • .pre-commit-config.yaml:2-6
  • .github/workflows/pre-commit.yaml:28

decided by agent · confidence 0.82

linter/type-checker config ([tool.ruff.lint.isort])
anchored
pyproject.toml:163 · ci_step

`I` is in the select list (pyproject.toml:159), so this sub-table parameterises rules that actually fire: ruff compares each file's import block against the ordering these settings define and exits non-zero (or, with fix=true, rewrites the file, which makes the pre-commit hook fail). The comparison is computed by the pinned external binary from the file's text, so an author cannot get a mis-ordered import block past the gate without editing the file or this config. Documented limit, and it is a large one: this asserts import ORDER, a style property — Keel measures the shape of the signal, not its value, and nothing here says anything about whether the code is correct.

producer the ruff binary's exit code for the `I` (isort) rules it was configured to enforce, run from the pinned pre-commit hook — actor can write to it: no

  • pyproject.toml:163-165
  • pyproject.toml:159 ("I", # isort selected)
  • .github/workflows/pre-commit.yaml:28

decided by agent · confidence 0.7

tox config ([tool.tox.env_run_base])
anchored
pyproject.toml:181 · test_target

This is the env every Tests matrix entry runs (`uv run --locked ... tox run` with TOX_ENV=py3.14/py3.13/… on ubuntu, windows, macos and pypy), so its exit status is the signal that gates the merge. `package = "wheel"` plus `constrain_package_deps`/`use_frozen_constraints` means the suite runs against a built artefact under versions constrained by the committed uv.lock, and the verdict is the interpreter's: a failing assertion, an import error or a segfault cannot be talked into exiting zero. Documented limit — the EXECUTION is anchored, the ORACLE is not: tests/ is authored by the same maintainers as src/flask/, so a green run proves the implementation matches these assertions, not that the assertions are right.

producer the pytest process exit code, produced by the target interpreter executing the committed test suite against a freshly built flask wheel — actor can write to it: no

  • pyproject.toml:181-193
  • .github/workflows/tests.yaml:18-42 (matrix of 11 interpreters/OSes, `tox run`)
  • uv.lock

decided by agent · confidence 0.9

tox config ([tool.tox.env.tests-min])
anchored
pyproject.toml:195 · test_target

The `Minimum Versions` matrix entry sets TOX_ENV=tests-min, so this env runs on every PR. Its first command installs exactly the floor versions declared in [project].dependencies (blinker==1.9.0, click==8.1.3, werkzeug==3.1.0, …) — already-published PyPI artefacts that cannot be rewritten in place — and the second command runs pytest against them. The signal is therefore the interpreter's verdict on running the suite under the oldest supported dependency set, which is precisely the drift this env exists to catch. Documented limits: the version floor itself is author-chosen (they could raise it to dodge a failure), and as with any suite the execution is anchored while the assertions were authored inside the write boundary.

producer the pytest process exit code, with the dependency versions under test fixed to specific immutable PyPI releases installed by `uv pip install` — actor can write to it: no

  • pyproject.toml:195-212
  • .github/workflows/tests.yaml:31 ({name: Minimum Versions, python: '3.14', tox: tests-min})
  • pyproject.toml:23-30 (declared floors match the pins)

decided by agent · confidence 0.85

tox config ([tool.tox.env.tests-dev])
anchored
pyproject.toml:214 · test_target

The `Development Versions` matrix entry sets TOX_ENV=tests-dev, so this runs on every PR. It installs blinker/click/itsdangerous/jinja/markupsafe/werkzeug from `archive/refs/heads/main.tar.gz` and then runs pytest, so the exit code is the interpreter's verdict on flask against unreleased upstream code — a canary whose whole point is that its inputs are not controlled by this repository's tree. Documented limit worth naming: those six upstreams are all Pallets-org projects, so the same maintainer group can write to the branches being tested against, and the env is non-deterministic across runs (an unpinned tarball fetched at build time); the pass/fail is still decided by executing the suite, not by any claim the repo makes.

producer the pytest process exit code, executing the suite against dependency source tarballs pulled live from upstream `main` branches — actor can write to it: no

  • pyproject.toml:214-231
  • .github/workflows/tests.yaml:32 ({name: Development Versions, python: '3.10', tox: tests-dev})

decided by agent · confidence 0.8

tox config ([tool.tox.env.style])
anchored
pyproject.toml:233 · test_target

`commands = [["pre-commit", "run", "--all-files"]]` re-runs the same hook set that .pre-commit-config.yaml pins (ruff, codespell, merge-conflict/debug-statement checks) against the whole tree, and the dedicated pre-commit workflow runs the equivalent command on every pull_request. The exit code is produced by external binaries fetched at pinned revisions parsing the committed files; a violation cannot report clean without editing the file or the pinned rev. Documented limits: (1) it is an AGGREGATE and I descended only to hook id + pinned rev, not into each hook's source; (2) this tox env is not itself selected by any CI matrix entry — the pre-commit.yaml workflow is what actually blocks the merge, and this env is the local mirror of it.

producer the `pre-commit` process exit code, aggregating SHA-pinned third-party hook binaries run over every file in the tree — actor can write to it: no

  • pyproject.toml:233-237
  • .pre-commit-config.yaml:1-27
  • .github/workflows/pre-commit.yaml:28

decided by agent · confidence 0.8

tox config ([tool.tox.env.typing])
anchored
pyproject.toml:239 · test_target

The Tests workflow's `typing` job runs `uv run --locked --no-default-groups --group dev tox run -e typing`, so this env is directly on the merge path. tox fails the env if either command returns non-zero, and both commands are static analysers computing their verdict from the source text under src/ and tests/type_check. An author cannot make an ill-typed change report clean without changing the code or relaxing the pinned [tool.mypy]/[tool.pyright] tables. Documented limit: strictness and the ignore_missing_imports overrides are author-controlled scope, so what the gate is capable of seeing lives inside the write boundary even though each diagnostic does not.

producer the mypy and pyright process exit codes — two independently implemented type checkers analysing the committed source, either of which fails the env — actor can write to it: no

  • pyproject.toml:239-245
  • .github/workflows/tests.yaml:44-62
  • pyproject.toml:126-145

decided by agent · confidence 0.9

tox config ([tool.tox.env.docs])
anchored
pyproject.toml:247 · test_target

`sphinx-build -E -W -b dirhtml docs docs/_build/dirhtml` re-parses the whole doc tree from scratch and, because of -W, fails on any warning — a genuinely falsifiable signal computed by the builder from the sources, not a claim the docs make about themselves. The equivalent command runs on Read the Docs for the hosted build (.readthedocs.yaml:8, `uv run --group docs sphinx-build -W -b dirhtml docs $READTHEDOCS_OUTPUT/html`). Documented limits: (1) no job in .github/workflows/ selects this tox env — the Tests matrix sets TOX_ENV only to test/typing envs, and tests.yaml even carries `paths-ignore: ['docs/**']` — so within this repository's own merge path the env gates nothing; whether the RTD build is a required PR check is not visible in the tree; (2) it checks that the docs BUILD, never that they are true.

producer the sphinx-build process exit code, with `-W` promoting every build warning (broken cross-reference, unknown role, missing toctree entry) into a non-zero exit — actor can write to it: no

  • pyproject.toml:247-250
  • .readthedocs.yaml:5-9
  • .github/workflows/tests.yaml:3-7 (paths-ignore docs/**), :40-42 (TOX_ENV set from matrix)

decided by agent · confidence 0.6

linter/type-checker config ([tool.ruff])
anchored
examples/tutorial/pyproject.toml:39 · ci_step

`pre-commit run --all-files` in the pre-commit workflow passes every tracked .py file to the pinned `ruff-check` hook, and .pre-commit-config.yaml sets no exclude, so example sources are linted on each PR. Ruff resolves configuration hierarchically from the nearest pyproject.toml containing a [tool.ruff] table, which makes this two-line table the live settings (src=["src"] for first-party import resolution) for those files; the rule selection is inherited from nowhere else, so the root select list does not apply here and only ruff's defaults (E/F) fire. The verdict is still computed by the pinned external binary from the file text. Confidence is held down because the block itself sets no rules — its effect on what can fail is limited to isort first-party detection, and I inferred ruff's config-discovery behaviour rather than observing it run.

producer the ruff binary's exit code, run from the SHA-pinned ruff-pre-commit hook over every Python file in the repository, including those under examples/ — actor can write to it: no

  • examples/tutorial/pyproject.toml:39-40
  • .pre-commit-config.yaml:2-6 (ruff-check, no exclude)
  • .github/workflows/pre-commit.yaml:28

decided by agent · confidence 0.55 · confidence below 0.6 on an anchored verdict

linter/type-checker config ([tool.ruff])
anchored
examples/celery/pyproject.toml:16 · ci_step

Same causal path as the other example config: the pre-commit workflow runs `pre-commit run --all-files` on every PR with no exclude list, so `examples/celery/src/task_app/*.py` goes through the pinned ruff-check hook, and ruff picks this nearest [tool.ruff] table as its configuration for those files. A syntax error or an undefined name in the example fails the job, and no assertion inside the repo can change that verdict — the deciding binary is fetched at a frozen rev from astral-sh. Confidence is low because the table declares only `src`, so its own contribution to what can fail is confined to isort first-party resolution, and ruff's hierarchical config discovery is inferred rather than executed here.

producer the ruff binary's exit code, run from the SHA-pinned ruff-pre-commit hook over the example package's Python files — actor can write to it: no

  • examples/celery/pyproject.toml:16-17
  • .pre-commit-config.yaml:2-6
  • .github/workflows/pre-commit.yaml:28

decided by agent · confidence 0.55 · confidence below 0.6 on an anchored verdict

not_a_check — 10

Asserts nothing about correctness, so it is excluded from the denominator. This is the one shoppable class: mis-filing a real check here shrinks the denominator and inflates the score, so each row carries the same burden of argument as any other verdict.

coverage config ([tool.coverage.run])
not_a_check
pyproject.toml:112 · ci_step

I grepped the whole .github/ tree, .readthedocs.yaml and every tox env command: there is no `coverage run`, no `--cov`, and `coverage` appears in no dependency-group (pyproject.toml:36-72). Nothing executes with this configuration, and even when a maintainer runs it locally the table only declares measurement SCOPE (branch=true, source=["flask","tests"]) — there is no fail_under anywhere in the file, so coverage emits a number and never a non-zero exit. A declaration that no runner consumes and that carries no threshold cannot report differently on a broken tree than on a healthy one, so it asserts nothing about correctness. Filing it here shrinks the denominator, so the burden is met explicitly: this is not a hard node, it is an inert one.

producer nothing — no process reads this table in any pipeline path; `coverage` is not installed by any dependency group and is not invoked by any tox env or workflow step — actor can write to it: not established

  • pyproject.toml:112-114
  • pyproject.toml:36-72 (no coverage in any dependency-group)
  • grep -rn 'coverage|--cov' .github/ .readthedocs.yaml -> no invocation
  • pyproject.toml:170-277 (no tox env runs coverage)

decided by agent · confidence 0.82

coverage config ([tool.coverage.paths])
not_a_check
pyproject.toml:116 · ci_step

[tool.coverage.paths] only tells coverage that `src` and `*/site-packages` are the same tree when combining data files. It computes no verdict of its own, and the tool that would consume it is never run: no `coverage`/`--cov` invocation exists in .github/workflows/, .readthedocs.yaml or any tox env command, and `coverage` is absent from every dependency-group. Nothing here can vary between a passing and a failing tree.

producer nothing — a path-remapping table for a tool that no workflow step, tox env or dependency group installs or invokes — actor can write to it: not established

  • pyproject.toml:116-117
  • pyproject.toml:170-277 (tox env commands: pytest / pre-commit / mypy / pyright / sphinx-build only)
  • .github/workflows/tests.yaml:40,62

decided by agent · confidence 0.85

coverage config ([tool.coverage.report])
not_a_check
pyproject.toml:119 · ci_step

`exclude_also` narrows which lines a coverage report counts (TYPE_CHECKING blocks, NotImplementedError, `...` bodies). It is the SCOPE knob of a measurement, and critically there is no `fail_under` in this table or anywhere else in the file, so even if a report were produced it would print a percentage and exit zero. Combined with the fact that no CI job, readthedocs command or tox env invokes coverage at all, this table produces no signal that could ever be non-green.

producer nothing — an exclusion list for a report that is never generated in any pipeline path — actor can write to it: not established

  • pyproject.toml:119-124
  • no `fail_under` anywhere in pyproject.toml
  • grep -rn 'coverage' .github/ -> no matches

decided by agent · confidence 0.85

tox config ([tool.tox])
not_a_check
pyproject.toml:170 · test_target

`env_list` only enumerates which envs a bare `tox run` would execute locally. In CI it is not even consulted: each Tests matrix entry sets `TOX_ENV` (tests.yaml:41-42) and the typing job passes `-e typing` explicitly, so the selection comes from the workflow, not this list. The list itself parses no source, runs no interpreter and has no exit code — the falsifiable edges are `[tool.tox.env_run_base]`, `[tool.tox.env.typing]`, `[tool.tox.env.style]` and friends, each judged on its own. Excluding a name list from the ratio does not hide a real gate; the gates it names are all in the denominator individually.

producer nothing — a list of environment names; every signal attributed to it is actually produced by the per-env `commands` tables, which are separate nodes — actor can write to it: not established

  • pyproject.toml:170-179
  • .github/workflows/tests.yaml:40-42 (TOX_ENV per matrix entry)
  • .github/workflows/tests.yaml:62 (`tox run -e typing`)

decided by agent · confidence 0.7

tox config ([tool.tox.env.docs-auto])
not_a_check
pyproject.toml:252 · test_target

`sphinx-autobuild --watch src docs docs/_build/dirhtml` is a developer preview loop: it serves the rendered docs on localhost and rebuilds on file save. Nothing consumes its exit status — no CI job selects this env and by construction the process runs until interrupted — so it can never report differently on a broken tree than on a healthy one. It is provisioning for a human's eyeballs, not an assertion.

producer a long-running local HTTP server (sphinx-autobuild) that watches src/ and docs/ and rebuilds on change; it never terminates, so it has no verdict to hand to anything — actor can write to it: not established

  • pyproject.toml:252-255
  • .github/workflows/ (no job selects docs-auto)

decided by agent · confidence 0.92

tox config ([tool.tox.env.update-actions])
not_a_check
pyproject.toml:257 · test_target

This env exists to mutate the repository, not to evaluate it: gha-update resolves each `uses:` to its newest tag and writes the new pin back into the workflow files. It is labelled `update` and is not in env_list, so no CI job runs it. A tool that succeeds by editing files reports the same success whether or not the tree was correct beforehand — there is no assertion to fail. (Contrast a `--check`/`--frozen` mode, which would be a real falsifiable claim that the pins are current; no such mode is used here.)

producer the `gha-update` tool, whose effect is to REWRITE the action SHAs in .github/workflows/*.yaml to the latest upstream releases — actor can write to it: not established

  • pyproject.toml:257-262
  • pyproject.toml:171-179 (env_list excludes update-*)

decided by agent · confidence 0.88

tox config ([tool.tox.env.update-pre_commit])
not_a_check
pyproject.toml:264 · test_target

`autoupdate` queries each hook repo for its newest tag and writes the resolved SHA back into the config; it runs no hook against any file. Like the gha-update env it is labelled `update`, is excluded from env_list, and is selected by no CI job. Its success is a statement that the network was reachable, not that anything in this repository is correct — nothing here can go red because of a defect in the tree.

producer the `pre-commit autoupdate --freeze` command, whose effect is to REWRITE the `rev:` pins in .pre-commit-config.yaml — actor can write to it: not established

  • pyproject.toml:264-269
  • .pre-commit-config.yaml (the `rev:` lines it rewrites)
  • pyproject.toml:171-179 (env_list excludes update-*)

decided by agent · confidence 0.9

help
not_a_check
docs/Makefile:12 · script

The recipe is `@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)"` — a usage listing. It does not parse the doc sources, produce output artefacts, or evaluate anything about the repository; its exit status depends only on sphinx being installed. It is the canonical help target: informational plumbing with no correctness claim attached.

producer sphinx-build's `-M help` mode, which prints the list of available builders to stdout and exits — actor can write to it: not established

  • docs/Makefile:12-13
  • docs/Makefile:1-10 (SPHINXBUILD ?= sphinx-build)

decided by agent · confidence 0.95

coverage config ([tool.coverage.run])
not_a_check
examples/tutorial/pyproject.toml:35 · ci_step

Two independent reasons it emits no signal. First, as with the root table it declares only branch/source scope and no `fail_under`, so coverage would print a percentage and exit zero. Second, nothing in the pipeline touches examples/ at all: the Tests matrix runs the root tox envs whose pytest is bound to testpaths=["tests"] (root), and no job installs the `flaskr` package. The one CI process that does read files under examples/ is pre-commit/ruff, which never consults a coverage table.

producer nothing — measurement-scope configuration for a tool that no workflow, tox env or readthedocs command runs, in an example package that CI never installs or tests — actor can write to it: not established

  • examples/tutorial/pyproject.toml:35-37
  • pyproject.toml:106-107 (root testpaths = tests)
  • .github/workflows/tests.yaml (no examples/ install or test step)

decided by agent · confidence 0.85

coverage config ([tool.coverage.run])
not_a_check
examples/javascript/pyproject.toml:28 · ci_step

Identical to the tutorial example: branch/source scope only, no `fail_under` in the file, no `coverage`/`--cov` invocation anywhere in .github/workflows/, .readthedocs.yaml or any tox env, and no CI job that installs `js_example` or runs its tests (the root pytest run is bound to the root tests/ directory). There is no process whose result could differ between a healthy and a broken tree.

producer nothing — a coverage scope table with no threshold, for a tool no pipeline step invokes, in an example package CI never installs — actor can write to it: not established

  • examples/javascript/pyproject.toml:28-30
  • examples/javascript/pyproject.toml (no fail_under)
  • .github/workflows/tests.yaml (no examples/ test step)

decided by agent · confidence 0.85

Crystallization curve

Keel crystallization curve Crystallization curve over 15 runs from <repo>/reports. falls — total fitted change -66.5% of the mean across 15 runs (R^2 0.28, so the line explains less than half the variance — read the raw squares) CRYSTALLIZATION CURVE 15 sequential runs · 345 nodes judged of 1838 gathered · 124 anchored Keel corpus — 15 repositories, 2026-07-24 — measured corpus (declared on the command line) estimated tokens per node estimated tokens / judged node 0 300 600 run 0 · keel · estimated tokens per node = 498 · judged 25 of 32 gathered run 1 · anthropic-sdk-python · estimated tokens per node = 369 · judged 25 of 41 gathered run 2 · openai-python · estimated tokens per node = 398 · judged 25 of 95 gathered run 3 · vercel-ai · estimated tokens per node = 410 · judged 25 of 1014 gathered run 4 · aider · estimated tokens per node = 204 · judged 25 of 62 gathered run 5 · browser-use · estimated tokens per node = 378 · judged 25 of 104 gathered run 6 · mcp-python-sdk · estimated tokens per node = 592 · judged 25 of 121 gathered run 7 · simonw-llm · estimated tokens per node = 189 · judged 25 of 38 gathered run 8 · tiktoken · estimated tokens per node = 240 run 9 · requests · estimated tokens per node = 290 · judged 25 of 106 gathered run 10 · flask · estimated tokens per node = 408 · judged 25 of 59 gathered run 11 · sinatra · estimated tokens per node = 265 · judged 25 of 59 gathered run 12 · commander-js · estimated tokens per node = 316 run 13 · anthropic-quickstarts · estimated tokens per node = 310 · judged 25 of 62 gathered run 14 · tiktoken · estimated tokens per node = 53 0* 1* 2* 3* 4* 5* 6* 7* 8 9* 10* 11* 12 13* 14 seconds per node measured s / judged node 0.0 60.0 120.0 run 0 · keel · seconds per node = 0.1 · judged 25 of 32 gathered run 1 · anthropic-sdk-python · seconds per node = 15.8 · judged 25 of 41 gathered run 2 · openai-python · seconds per node = 15.4 · judged 25 of 95 gathered run 3 · vercel-ai · seconds per node = 105.9 · judged 25 of 1014 gathered run 4 · aider · seconds per node = 21.3 · judged 25 of 62 gathered run 5 · browser-use · seconds per node = 21.7 · judged 25 of 104 gathered run 6 · mcp-python-sdk · seconds per node = 20.1 · judged 25 of 121 gathered run 7 · simonw-llm · seconds per node = 11.8 · judged 25 of 38 gathered run 8 · tiktoken · seconds per node = 23.7 run 9 · requests · seconds per node = 16.8 · judged 25 of 106 gathered run 10 · flask · seconds per node = 20.5 · judged 25 of 59 gathered run 11 · sinatra · seconds per node = 16.9 · judged 25 of 59 gathered run 12 · commander-js · seconds per node = 18.7 run 13 · anthropic-quickstarts · seconds per node = 20.4 · judged 25 of 62 gathered run 14 · tiktoken · seconds per node = 0.1 0* 1* 2* 3* 4* 5* 6* 7* 8 9* 10* 11* 12 13* 14 probe-decided share measured share of decided nodes 0.00 0.50 1.00 run 0 · keel · probe-decided share = 0.00 · judged 25 of 32 gathered run 1 · anthropic-sdk-python · probe-decided share = 0.00 · judged 25 of 41 gathered run 2 · openai-python · probe-decided share = 0.16 · judged 25 of 95 gathered run 3 · vercel-ai · probe-decided share = 0.08 · judged 25 of 1014 gathered run 4 · aider · probe-decided share = 0.56 · judged 25 of 62 gathered run 5 · browser-use · probe-decided share = 0.32 · judged 25 of 104 gathered run 6 · mcp-python-sdk · probe-decided share = 0.00 · judged 25 of 121 gathered run 7 · simonw-llm · probe-decided share = 0.56 · judged 25 of 38 gathered run 8 · tiktoken · probe-decided share = 0.58 run 9 · requests · probe-decided share = 0.28 · judged 25 of 106 gathered run 10 · flask · probe-decided share = 0.00 · judged 25 of 59 gathered run 11 · sinatra · probe-decided share = 0.28 · judged 25 of 59 gathered run 12 · commander-js · probe-decided share = 0.19 run 13 · anthropic-quickstarts · probe-decided share = 0.24 · judged 25 of 62 gathered run 14 · tiktoken · probe-decided share = 1.00 0* 1* 2* 3* 4* 5* 6* 7* 8 9* 10* 11* 12 13* 14 probe library size measured probes in library 0 15 30 run 0 · keel · probe library size = 0 · judged 25 of 32 gathered run 1 · anthropic-sdk-python · probe library size = 3 · judged 25 of 41 gathered run 2 · openai-python · probe library size = 5 · judged 25 of 95 gathered run 3 · vercel-ai · probe library size = 8 · judged 25 of 1014 gathered run 4 · aider · probe library size = 10 · judged 25 of 62 gathered run 5 · browser-use · probe library size = 12 · judged 25 of 104 gathered run 6 · mcp-python-sdk · probe library size = 13 · judged 25 of 121 gathered run 7 · simonw-llm · probe library size = 15 · judged 25 of 38 gathered run 8 · tiktoken · probe library size = 17 run 9 · requests · probe library size = 19 · judged 25 of 106 gathered run 10 · flask · probe library size = 22 · judged 25 of 59 gathered run 11 · sinatra · probe library size = 23 · judged 25 of 59 gathered run 12 · commander-js · probe library size = 26 run 13 · anthropic-quickstarts · probe library size = 29 · judged 25 of 62 gathered run 14 · tiktoken · probe library size = 29 0* 1* 2* 3* 4* 5* 6* 7* 8 9* 10* 11* 12 13* 14 x axis: run index in the recorded corpus order (listed below). Squares are the raw per-run values; the dashed line is an ordinary-least-squares fit and is never shown without them. The fit is clipped to the panel, and withheld entirely (with the panel saying so) where a straight line would predict values the points cannot take — R^2 for every fit is in the trend notes below. * on run 0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13 = judged fewer nodes than gathered; per-node values are per JUDGED node. Coverage (judged) ci_step 224 · script 64 · test_target 46 · review_gate 10 · deploy_gate 1 Trend estimated tokens per node: falls — total fitted change -66.5% of the mean across 15 runs (R^2 0.28, so the line explains less than half the variance — read the raw squares). raw first 498 -> last 53. seconds per node: falls — total fitted change -72.9% of the mean across 15 runs (R^2 0.04, so the line explains less than half the variance — read the raw squares). raw first 0.1 -> last 0.1. probe-decided share: rises — total fitted change 144.9% of the mean across 15 runs (R^2 0.21, so the line explains less than half the variance — read the raw squares). raw first 0.00 -> last 1.00. probe library size: rises — total fitted change 187.7% of the mean across 15 runs (R^2 0.99). raw first 0 -> last 29. Run order run order derived from Report.generatedAt (ascending, filename as tiebreak) — no order.json manifest present 0 keel -> 1 anthropic-sdk-python -> 2 openai-python -> 3 vercel-ai -> 4 aider -> 5 browser-use -> 6 mcp-python-sdk -> 7 simonw-llm -> 8 tiktoken -> 9 requests -> 10 flask -> 11 sinatra -> 12 commander-js -> 13 anthropic-quickstarts -> 14 tiktoken Shuffle check shuffle check INCOMPLETE — the curve direction ran, the ratio direction did not Permutation (400 draws, seed 20260724): median |delta normalized slope| 0.633, 92% of draws move it by >= 0.15. A permutation reorders ALREADY-RECORDED runs; it cannot reproduce what a genuine re-run in a different order would have cost, because the probe library would have accumulated differently. It is a sanity signal on order-dependence, never a substitute for the empirical re-run. Ratio stability under shuffle was NOT checked: no re-run supplied (--shuffled) and none declared in corpus.meta.json. Permuting recorded runs cannot move a per-target ratio, so the permutation result below says nothing about it. Supply a re-run with --shuffled <dir>. Disclosures - Skipped 3 file(s) that are not usable run reports: corpus-summary.json (no string "target"); curve.json (no string "target"); keel.bindings.json (no "nodes" array). - Run 0 (keel) judged 25 of 32 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 1 (anthropic-sdk-python) judged 25 of 41 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 2 (openai-python) judged 25 of 95 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 3 (vercel-ai) judged 25 of 1014 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 4 (aider) judged 25 of 62 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 5 (browser-use) judged 25 of 104 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 6 (mcp-python-sdk) judged 25 of 121 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 7 (simonw-llm) judged 25 of 38 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 9 (requests) judged 25 of 106 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 10 (flask) judged 25 of 59 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 11 (sinatra) judged 25 of 59 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 13 (anthropic-quickstarts) judged 25 of 62 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 14 (tiktoken): decidedByProbe + decidedByAgent = 10 but nodesSampled = 12. Probe-decided share uses the decided total (10). - Provenance "measured" was declared on the COMMAND LINE (--provenance), not in <repo>/reports/corpus.meta.json. The declaration therefore lives in the invocation and is only as trustworthy as the run sheet that records it. - Token counts are ESTIMATES in 15 of 15 run(s) (RunEconomics.tokensEstimated). No API exposes session token usage to a skill, so the token axis reads "estimated tokens" and nothing here claims a measured token count. 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.

Run economics

0 decided by probe
25 decided by agent
0% probe-decided share
25 / 59 nodes judged / gathered
2,808 estimated tokens in
7,404 estimated tokens out
8m 33s wall clock
3 probes minted
22 probe library size

Token counts are estimated, and labelled so: a skill running inside an agent session has no API for its own usage, so the figure is ceil(chars/4) over the judgment payloads and responses. Wall clock and probe-decided share are measured directly. 25 of 59 gathered edges were judged — the cap is printed here and beside the ratio, never applied silently.