evals / library-fastapi

al-second-head

Expected

Hidden from the reviewer, like the grading note below.

api-fastapi-route-authorization
holds
api-fastapi-wire-schemas-and-errors
holds
api-fastapi-resource-layout
holds
security-resource-access-control
holds
service-transaction-side-effects
holds
data-alembic-revision-history
violation
code-comment-attribution
holds
test-curation
holds

A new revision adds item.archived_at with down_revision fe56fa70289e, but the head is 1a31ce608336. The history now has two heads, and alembic upgrade head fails. The column, model field, and downgrade are otherwise sound.

Change

Contracts

api-fastapi-route-authorization
---
name: api-fastapi-route-authorization
message: Declare route admission as named shared dependencies and apply ownership in the statement, never after loading.
---

## Rules

Declare every route's admission, meaning which principals and credential types may call it, as a FastAPI dependency.
Define each admission dependency once, as a named constant in a shared admission-policy module, and import it into routers.
Never build an admission dependency inline in a route signature or define a private copy in a resource's router.
Add a missing policy to the shared module beside the policies it extends.
Keep the admission-policy module a leaf: it loads no resource, opens no session, and imports no route module.
Declare a deliberately public route with a named public-admission constant, so every public route is explicit and enumerable.

Treat admission as entry only, and apply the owner or visibility scope that `security-resource-access-control` requires inside each query, update, and delete statement before it executes.
Never load an unscoped row and compare its owner afterwards in Python.
Pass the principal's scope from the admission dependency to the service and query explicitly.
Use an owner-bound scope for every route that writes principal-owned rows unless its admission policy names a wider write grant.

Override admission in tests through FastAPI dependency overrides keyed by the shared dependency, not by patching modules.
Keep an enumerated record of each route's admission, and update it in the same change that changes admission.

## Checks

Review each changed router: does every admission dependency come from the shared policy module or the public constant?
Review each changed statement for a scope applied before execution, on list, detail, update, and delete paths.
Review each POST, PUT, PATCH, and DELETE route for an owner-bound scope or a named wider write grant.
Regenerate the route admission record after changing dependencies, and confirm every difference is intended.
Exercise one resource as two different owners and confirm that own actions succeed and foreign actions are denied.
api-fastapi-wire-schemas-and-errors
---
name: api-fastapi-wire-schemas-and-errors
message: Declare explicit wire models, keep secrets on mint responses only, and map each failure to one status and detail.
---

## Rules

Define each resource's request and response models in its schema module, with a type on every field.
Forbid unexpected fields on a request model whose shape is closed.
Declare a response model for every route, through its return annotation or `response_model`.
Never return a storage or ORM object, or an untyped dictionary, as an implicit response.
Build each response from the fields the wire exposes; never dump a storage model wholesale into a response.
Keep each conversion function, which builds a response model from storage objects, pure: it selects fields from already loaded objects and never queries, commits, calls clients, or generates secrets.
A service operation may still call clients before or after it calls a conversion, such as calling another service after its commit.
Reuse a shared response model when its owner already defines the wire shape, instead of copying it.
Preserve existing field names and wire types when refactoring schemas.

Return a minted secret only through a response model that its minting route alone uses, and keep secrets and their hashes out of every other response model; see `security-api-key-lifecycle`.

Map each domain failure to one HTTP status and one detail shape, and use that mapping in every route that can produce the failure.
Keep the failure mapping in one place a reviewer can read, with any resource-specific exception stated beside it.
Translate domain failures into HTTP responses in exactly one layer, such as the service or an exception handler, and keep that translation out of every other layer.
Give malformed input, a conflict with current state, and a consumed, revoked, or expired capability distinct statuses.
Follow `security-resource-access-control` for the status a request for an inaccessible resource receives.

Name each database constraint the code reacts to once, beside the model that declares it, and compare the violated constraint against that name rather than a string literal.
Catch an integrity error only around the write or commit that can raise it, map only the named constraint, and re-raise any other cause.
Chain the original exception when raising a mapped error.
Catch only the specific exception type a library raises for bad input; never catch a broad exception to produce a client error.
Leave unmapped failures unhandled, so the client receives only a generic server error.

## Checks

Review every changed route for a declared response model.
Review changed conversions for I/O, client calls, or secret generation.
Confirm that list, detail, update, and delete responses cannot carry hashes, plaintext secrets, or unrelated storage fields.
Review each raised HTTP error against the mapping, and confirm it comes from the chosen layer only.
Review each `except` clause in changed code for a specific exception type, a single write or commit, a named constraint, and a chained re-raise.
api-fastapi-resource-layout
---
name: api-fastapi-resource-layout
message: Give each resource one folder of router, schema, query and service modules, and keep URLs fixed when code moves.
---

## Rules

Keep each FastAPI resource in one folder with up to four modules: `router.py`, `schemas.py`, `queries.py`, and `service.py`.
Limit `router.py` to paths, dependencies, parameter parsing, and delegation; it takes no database session and holds no database statements or transaction boundaries.
Keep wire models and their conversions in `schemas.py`.
Put database reads and writes in `queries.py` functions that receive the caller's session and never create, commit, or close one.
Put application operations, including single reads, in `service.py`, which owns session lifetime and transaction boundaries and passes the session to queries.
Create a role module only when the resource has behavior for that role; do not add empty role modules.

Create one folder per resource, not per endpoint, and do not require an index module or package initializer.
Nest a sub-resource folder only when its endpoints share a path segment under the parent's identifier and it has its own storage or wire shape.
Keep endpoints that only rename or set defaults on the parent record in the parent folder.

Name each version folder after the URL prefix it serves, and place each resource in the folder whose prefix its paths carry.
Declare new public resources under the current version prefix.
Never change a URL by moving code; a URL change is a wire change made in its own change.
Comment each router that keeps an inherited path outside its folder's prefix, naming the callers that depend on that path.
Register a static path segment before a parameterized route that would match it, because FastAPI matches routes in registration order.

Mount sub-resource routers from the parent `router.py` with `include_router`, and declare tags only on the parent router.
Compose each version folder's resources in one router that carries the version prefix, and mount each composition root once in the application.

Import only up the tree: a sub-resource may import its ancestors' modules, and an ancestor imports a descendant only to include its router.
Never import between sibling resources, including sub-resources under different parents.
Never import shared types or constants from a router module.
Put an operation that several resources share in a module named for the question it answers, outside every resource folder, and pass it the caller's session.
Place shared code beside the routes when only routes call it, and in the owning domain package when anything outside the routes calls it.

Mirror the resource tree in the test tree, and drive a resource's route tests through the real router.

## Checks

Review each changed resource folder against the four roles: does a router hold database statements or transactions, or a query open or commit a session?
Compare the application's registered paths and methods before and after a code move; any difference is a URL change.
Review imports in changed resource folders for sibling or downward imports, or run the project's import-boundary linter.
Review each new sub-resource folder against the nesting test.
Request a static path that shares a prefix with a parameterized route and confirm its own handler answers.
security-resource-access-control
---
name: security-resource-access-control
message: Authorize every request on the server from current grants; logins, plain links, and caller-supplied facts grant nothing.
---

## Rules

Authorize every request on the server against the principal's current account state, memberships, grants, and revocations.
Deny disabled, unverified, and revoked principals even when their credential is otherwise valid.

A successful login establishes a principal and grants no access; each service authorizes it against its own resources and grants.
A link, shared URL, or reference inside content grants no access by itself to the resource it points to.
Caller-supplied attribution, context, selection identifiers, or capability flags never grant access or prove a relationship between records.
Never derive authority from user-editable or descriptive data such as display names, contact emails, titles, or directory teams.
Grant access through a capability, such as a sharing code, only after validating its stored record, and take its owner and authority from that record, never from values supplied beside it.
Consume a single-use capability atomically with the action it authorizes.

Keep one authoritative grant model per resource kind; do not keep parallel access flags that can disagree with it.
Never let a grant on one resource or scope widen access to another.
Keep the authority to administer grants separate from the authority to read or change the resources they cover.
Give each credential type an explicit capability ceiling; a programmatic credential gains administrative or bypass powers only through an explicit grant.
Authorize a delegated request only when both the human subject and the acting service hold the required grants.
When acting on another principal's behalf, resolve access from that principal's current grants, never the operator's.
Give new resources and migrated records only explicitly declared grants.
Records attached to a private resource inherit its audience, and removing a reader removes access to its history and attachments.

Apply access scope before lookup, search, counting, and pagination, so results and totals never reflect records the principal cannot read.
Check authorization and apply the write it guards atomically, such as in one transaction or one scoped statement.
Resolve a name selector to exactly one permitted resource, or fail and ask the caller to disambiguate.
Never fall back to a more privileged credential or another principal after a denial.
Keep authorization decisions out of shared caches, and never serve a principal-specific response from a cache shared across principals.

Answer a missing or invalid credential as unauthenticated, such as HTTP 401.
Answer a request for a resource the principal may not know exists as not found, such as HTTP 404, with the identical status and body for missing, unpublished, and foreign-owned resources.
Answer forbidden, such as HTTP 403, only when the principal may already know the resource exists, such as a visible resource it may read but not change.
Record administrative grant changes with principal, credential, and target identifiers, excluding secrets.

## Checks

Exercise each changed operation with no credential, an invalid credential, a disabled principal, a principal without the grant, and another owner's principal, and compare statuses and bodies.
Revoke a grant or membership and confirm the next request is denied without a restart or cache expiry.
Does any access decision read login state, a link, an attribution field, a client-supplied capability flag, or user-editable profile data?
Compare the responses for a missing resource and another owner's private resource; can a principal tell them apart?
Check that search results, listing totals, and pagination exclude records the principal cannot read.
Present a capability with a substituted owner, and a single-use capability a second time, and confirm both are refused.
Review administrative, delegated, and service paths for access they inherit without an explicit grant.
service-transaction-side-effects
---
name: service-transaction-side-effects
message: Emit best-effort effects after commit, and commit durable delivery intent with the state change.
---

## Rules

Do not call external systems, such as analytics, notifications, queues, or other services, inside a database transaction.
Emit a best-effort effect only after its transaction commits, so a failed commit emits nothing.
Capture the values an effect needs, such as generated identifiers, before the transaction scope closes, and pass them to the effect explicitly.
Do pure preparation, such as token generation and payload building, before the transaction, and move only the external call after it.
Return success to the caller only after the commit.

Write the records a state change owns, such as its events, audit entries, and receipts, in the same transaction as the change.
Classify each effect as best-effort or required; a send after commit is best-effort only.
Commit the intent for a required effect, such as an outbox row or dispatch request, in the same transaction as the state change.
Derive required effects from committed intent, not by polling business records for changes.
Deliver committed intent from a server-side dispatcher that claims work with bounded leases and runs without any client present.
Retry delivery with a stable key derived from the intent, so the receiver never repeats accepted work after a lost acknowledgement.
Mark intent delivered only after the receiver acknowledges it; a send attempt is not delivery.

When an external call must precede the state change, record its intent first as `code-execution-lifecycle` requires, and record its observed result in a following transaction.
If recording that result fails, leave a visible gap; never repeat the external call or report success.

## Checks

Review each external client call in changed code and confirm it sits outside every transaction scope.
Force a commit failure, such as a constraint violation, and confirm no best-effort effect was emitted.
Stop the process between commit and dispatch, restart it, and confirm the required effect is delivered and applied once.
Drop a delivery acknowledgement and confirm the retry reuses the same key without repeating accepted work.
Inject a failure while recording an external call's result, and confirm the call is not repeated and no success is reported.
data-alembic-revision-history
---
name: data-alembic-revision-history
message: Keep one Alembic head, never delete or re-identify a merged revision, and undo applied work with a new revision.
---

## Rules

Apply these rules to Alembic revision files and to the code that runs migrations; changes to other files do not affect them.
A revision is merged once the default branch contains it; review each change against the branch it merges into, where every revision file that already exists is merged.

Set a new revision's `down_revision` to the current head of the default branch, and keep the docstring's `Revises:` line equal to it.
Check the head again after every rebase, and re-parent the revision onto any revision the default branch gained.
When the default branch already has two heads, add a merge revision whose `down_revision` names both.
Keep exactly one head on the default branch, because every path that runs `alembic upgrade head` fails while two exist.

Keep every merged revision file.
Never change a merged revision's `revision` or `down_revision`, including by renaming it to a new identifier.
Stop a merged revision from running by tombstoning it: keep the file and both identifiers, make `upgrade()` and `downgrade()` do nothing, and record why in the docstring.
Do the corrected work in a new revision at the head, written to converge whether or not the tombstoned revision already ran.
Never rewrite a merged revision's work in place, such as a column type, server default, index, or data statement in its `upgrade()`, because databases that recorded it will not run it again.

Write `downgrade()` to reverse the schema change for local development, tests, and deliberate operator rollback.
When `upgrade()` drops data that `downgrade()` cannot restore, restore the structure empty and name the lost data in a comment.
Raise an error naming the lost data only when an empty restore would mislead, such as after a revision that deletes rows without changing the schema.
Leave `downgrade()` empty only in a merge revision or when there is nothing to reverse, and say why in a comment or the docstring.

Undo an applied revision in production with a new revision at the head that reverses it, not with a downgrade.
Keep the migration runner forward-only: it upgrades, does nothing, or fails, and never downgrades on its own.
Treat a manual production downgrade as an operator action that needs the data owner's approval, and tombstone the revision first or the next deploy reapplies it.
Make statements that run before an `autocommit_block()` safe to run again, because entering the block commits them and a retry repeats them.

## Checks

Run `alembic heads` and expect exactly one head, the new revision.
Compare revision files, `revision` values, and `down_revision` values against the merge base, and treat a deleted file or changed identifier as a violation.
Run `upgrade`, `downgrade`, and `upgrade` again for each new revision on a scratch database.
Review each `downgrade()` for a reversal, a named data loss, or a stated reason for being empty.
Review each fix to a merged revision for a tombstone plus a new revision rather than an in-place rewrite.
code-comment-attribution
---
name: code-comment-attribution
message: Explain source behavior without attributing it to repository contracts or review tools.
---

## Rules

Apply these rules to maintained production code, tests, and examples, including their comments and inline documentation.
Keep requirements in guidance documents; source comments explain behavior and why it matters to callers.
Do not name a repository contract's number, name, or path as the reason for source behavior.
Do not name a review tool, automated reviewer, or review finding as the reason for source behavior.
Do not add attribution comments such as `// Contract 011: keep the session cookie host-only`, ``# `orders-retention-lifecycle`: purge closed orders``, or `// See the route-paths contract`.
A comment that names a contract this way is an attribution even when it never uses the word contract and even when it also states a reason.

Explain the external constraint, failure consequence, or caller dependency that makes a behavior matter, for example `// A failed revocation returns focus to its invoker so the user can retry`.
When removing an attribution, keep the behavioral reason it carried, or remove the comment when the code already makes that reason clear.
A comment that explains behavior without naming a contract or review tool complies, including one that cites an incident, issue, or design specification as the source of that behavior.

Contract authoring, discovery, review, and check tooling may refer to contracts in its implementation, diagnostics, and fixtures when those references perform the tool's purpose.
Guidance documents, API or protocol contracts, and ordinary domain uses of the word "contract", such as a comment about the response contract that clients rely on, are outside these rules.
Generated and copied third-party source follows its own generation or copying policy.

## Checks

Search changed source for common attribution forms, such as a contract number, a contract folder path, or a review tool's name, and inspect each match against the tooling exception.
Read changed comments directly, because a search misses attributions written in other words.
Where an attribution was removed, does a behavioral explanation remain wherever the code alone does not make the reason clear?
Run the affected package checks after editing source.
test-curation
---
name: test-curation
message: Treat development tests as disposable, and commit only checks that guard consequential regressions and declared example tests.
requires-tenet: 0.11.0
---

## Rules

Apply this contract to added, changed, and removed tests, fixtures, and test-only dependencies; adopting it does not require deleting an existing suite wholesale.
A change that touches no test, fixture, test report, or test-only dependency leaves this contract unaffected.
Test freely during development with temporary tests, probes, broad cases, and manual checks, and treat their results as development evidence.
Keep temporary tests, probes, fixtures, and test-only dependencies out of every commit, including intermediate commits and commits pushed only to run CI, unless a retained check uses them.
Passing locally or catching the original bug does not by itself justify keeping a test.

Keep example tests as `test-example-suite` requires; they need no guarded regression to be retained and no reason or remaining protection to be deleted, but the rule below against hiding a regression still covers them.
Retain a new automated check only when it guards a concrete, consequential regression that existing checks miss and its outcome is stable and observable.
Consequential regressions include leaked credentials, lost or corrupted data, incorrect charges, repeated paid work, unbounded work, swallowed errors, and broken cancellation, recovery, or publication safety.
Name the guarded regression and the gap in existing coverage in the change description for each retained new check.
Apply the same retention standard to every kind of test; do not keep or reject a test because it is called unit, integration, end-to-end, or smoke.
Test each guarantee at the boundary that owns it, extending an existing representative flow when one reaches that boundary.

Leave structure and conventions to types, linters, contracts, and code review rather than tests.
Do not assert private helpers, internal data shapes, internal call counts, incidental copy, rendered snapshots, or behavior owned by a dependency.
Do not repeat a test per fixture or variant unless the variant changes the guarded behavior.
Keep distinct concerns in separate cases; do not merge unrelated scenarios to reduce the reported test count.
Treat sampled or model-graded scores as measurements, not exact expected values.
Apart from a declared example limit, do not set coverage-percentage targets, test-count targets, or test-count gates.

Removing or skipping a test, case, or assertion deletes the coverage it provided.
Delete existing coverage only for a stated duplicate, a superseded requirement, or behavior explicitly accepted as development-only verification, and name the protection that remains.
Never loosen or delete a check to hide a regression; difficulty, slowness, or an unfavorable result is not a reason to remove it.

Synchronize concurrent tests with explicit gates and bounded cleanup rather than sleeps.
Where the test runner takes a per-test timeout, give every test one as a number literal, only to bound a hung test, never to define expected ordering.
Fix a flaky test's fixture ownership or synchronization instead of raising timeouts, adding retries, or deleting the only protection against a critical failure.
Record live provider checks as separate evidence, and do not present a mocked provider as proof of live provider behavior.
Write temporary test reports and outputs outside tracked source.

## Checks

Inspect each commit, not only the complete diff, for temporary tests, probes, fixtures, reports, and test-only dependencies.
For each retained new check, can the author name the consequential regression, the gap in existing coverage, and why its boundary detects the defect?
For removed or weakened coverage, identify the remaining protection or the explicit acceptance of development-only verification.
Temporarily break the critical behavior a changed check guards, confirm the check fails for that reason, then restore the implementation and remove the probe.
Run the affected tests with the repository's standard test command.
Run the repository's lint and this contract's `assets/` configuration over its test files from the repository root, and confirm they fail on a test that declares no timeout.