Keel

keel route

Independence cannot be manufactured, but it can be routed.

Every ungrounded verification edge in anthropic-sdk-python, paired — where a pairing exists — with an anchored producer already present in the same measurement. Nothing here invents an anchor, and nothing here has been applied.

targetanthropic-sdk-python
revision60c64fba5c2bf340567f627328e57cf0196b868f
source reportreports/anthropic-sdk-python.json
generated2026-07-25T05:15:24.487Z
statusproposed (all 4)
0.67

today — measured

·
0.67

projection if applied — 0 re-grounded · 0 constructed

anchored / (anchored + self_referential + unknown)

anchored 8 self_referential 2 unknown 2 not_a_check 13 excluded from the denominator

The second number is a projection: the ratio this target would carry if a human applied every route below and the applied routes measured as anchored on re-measurement. It is arithmetic on a hypothetical. It is not written to any report, it is not fed back into the grounding ratio, and no proposal on this page has been applied. Projected counts, for the same conditional: anchored 8 · self_referential 2 · unknown 2 · not_a_check 13. The delta is two different claims and is reported as two: 0 re-grounded — an existing check whose signal moves outside the write boundary, the proposition it asserts unchanged — and 0 constructed — a not_a_check node that becomes a check, entering the denominator as well as the numerator. Only the first repairs something that was already being claimed.

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.

Routes — 0 routable · 4 unroutable

Existing checks whose signal could come from somewhere they cannot write. Cheapest effort on top. no route is a first-class answer: it means the fix needs a policy decision rather than a rewiring, and it is reported here rather than dropped.

what's ungrounded route to why that's anchored effort
.github/CODEOWNERS#codeowners
review_gate · code owners (CODEOWNERS)
today the signal comes from GitHub's branch-protection engine, whose configuration is not in the repository
unknown
no route

The proposition here is 'a change reaching main was approved by a party responsible for the code'. It is unknown because the producer of that signal — GitHub's branch-protection engine — is configured outside the clone, so the edge cannot be traced to a fork point at all. Routing requires an anchored node that asserts THAT proposition, and this report contains none: every anchored candidate produces a machine verdict over the committed tree (ruff, pyright, mypy, the interpreter's import, the uv build backend, PyPI's upload response). A machine's verdict on the source is not an approval by a responsible party — substituting one for the other would silently redefine the claim rather than ground it. There is also no node in this report whose producer is a human decision of any kind.

why not: Two decisions have to be made before anything can be wired. First, the gating state must be made legible: whether 'Require review from Code Owners' is on lives in repository settings, so someone must decide to commit a GitHub ruleset into the repo or to have a job query the branch-protection API and fail on drift — that is a process choice with an owner and a permissions cost. Second, and independent of it, someone must decide whether review by @anthropics/sdk — the same team that owns the generated source — sits outside the write boundary at all. No rewiring answers that; it is a judgement about the org chart.

.github/workflows/claude.yml#38-run-claude-code
ci_step · Run Claude Code
today the signal comes from a large language model invoked through anthropics/claude-code-action
self_referential
no route

There is nothing here to re-ground, and that is the first reason. The workflow `if:` at claude.yml:15-19 makes this step mention-triggered, so it is not on the merge path; moving its signal to an anchored producer would change no admission decision. The second reason is that the propositions do not match. Whatever this step asserts is a broad judgement — 'this change or this answer is sound' — while every anchored candidate in the report asserts something strictly narrower and different in kind: ci.yml#34-run-lints and the pyproject linter tables say the tree type-checks, lints and imports; create-releases.yml#34-publish-to-pypi says the tree packages and PyPI took it; pyproject.toml#tool-pytest-ini-options says the collected tests executed to a clean exit. Declaring 'the lints are green' to be the grounded form of 'the model reviewed it' would rename the claim rather than anchor it. Those producers already run on push against anything this job commits, so the honest statement is that they cover what they cover and the model's judgement remains unverified — not that the judgement has been routed.

why not: The real defect is a permissions decision, not a missing wire: the job holds `contents: write` and `pull-requests: write` and `allowed_tools` grants `git commit`, `git push`, `git merge` and `gh pr`, so the reviewer can write into the branch whose state it reports on. Closing that means someone decides to strip the write-scoped tools, and separately decides which producers become required status checks on the merge path — and that second decision lives in branch-protection settings that are not in this repository (the same gap that leaves .github/CODEOWNERS#codeowners unknown).

.github/workflows/create-releases.yml#22-stainless-api-trigger-release-please-bb6677c5a04578eec1ccfd9e1913b5b78ed64c61-v1-4-0
ci_step · stainless-api/trigger-release-please@bb6677c5a04578eec1ccfd9e1913b5b78ed64c61 # v1.4.0
today the signal comes from the stainless-api/trigger-release-please action, whose implementation is not in this repository
unknown
no route

The proposition this step asserts is 'a release is warranted now, at this version, with this changelog and tag state'. The only anchored candidate anywhere near it is create-releases.yml#34-publish-to-pypi, and it fails on both counts. Direction: #34 runs BECAUSE of this step's `releases_created` output, so it is downstream and cannot be a producer this step reads — a check cannot be grounded by its own consequence. Proposition: PyPI's upload response asserts that an artefact was built and accepted; it rejects a duplicate version but says nothing about whether a release was warranted, whether the changelog is honest, or whether the version bump matches the diff. A wrongly-triggered release carrying a fresh version number passes #34 cleanly, so wiring these together would raise the number while leaving the actual claim ungrounded. The opacity itself is untouched either way: the action's body is pinned by SHA but not vendored, so nothing in this repo can be read to learn what it validates.

why not: The decision required is about a vendor boundary. Either vendor or replace the action so its release logic is inspectable in-tree, or accept it as opaque third-party plumbing — and in accepting it, decide explicitly whether Stainless being both the generator of src/anthropic (per .stats.yml and the tests/conftest.py header) and the operator of the release gate is an acceptable arrangement. That is a human call about a supplier relationship, and no rewiring of existing producers can substitute for it.

tests/conftest.py#conftest-py
test_target · pytest config (conftest.py)
today the signal comes from a Steady mock server generated from the OpenAPI spec, plus inline_snapshot recordings of the client's own HTTP traffic
self_referential
no route

The proposition this conftest is trying to assert is behavioural: that the client's requests and its handling of responses match the API it will actually talk to. Its producer is a Steady mock booted from the `openapi_spec_url` in .stats.yml plus inline_snapshot recordings of the client's own traffic — both derived from artefacts inside the write boundary, which is exactly why it is self_referential. Every anchored producer in this report sits on a different axis: ruff/pyright/mypy/`import anthropic` (.github/workflows/ci.yml#34-run-lints and the four pyproject linter tables) assert that the committed tree analyses and imports cleanly, and create-releases.yml#34-publish-to-pypi asserts that the tree packages and that PyPI accepted the upload. None of them observes the behaviour of the Anthropic API, so none of them can supply the oracle this suite is missing. pyproject.toml#tool-pytest-ini-options is the trap: it is anchored, but only on the EXECUTION axis, and its own measured argument names the identical spec-derived-oracle limit. Routing the oracle onto the config of the very suite whose oracle is in question is a circle, and would move the count without grounding the claim.

why not: This needs a policy decision, not a rewiring: someone must choose an oracle the SDK cannot author. Either (a) run a contract suite against the live Anthropic API with real credentials — which is a decision about cost, secrets, rate limits and flakiness in the merge path — or (b) declare the OpenAPI spec an external frozen contract, published and versioned independently of the generation input, so the mock is derived from something the SDK's authors do not also write. Both are human calls about what counts as ground truth for this SDK; neither is a wire an agent can move.

Construct — not yet.

A route makes a check read a signal from outside itself. It does not pair that check with a counter-metric, name who arbitrates when the pair disagrees, or set a cadence at which the route is re-audited. Those three fields are declared in schemas/route.ts and are filled by keel construct, which is not built. The columns are empty by construction, not by omission.

route pairedWith arbitratedBy auditEvery
no routes on this run