Contract Category Reference

app.check categories, default severity, fix targets, and severity override examples

Page actions AI-ready formats and sharing
Open LLM text
Share with AI
Ask Claude Ask ChatGPT Ask Gemini Ask Copilot

app.check()validates your hypermedia wiring at startup and reports every problem as a(category, severity, message)triple. The category is the stable handle you target in CI policy; the message names the exact route, template, block, selector, registration, config field, middleware, or import string to fix. This page lists every category, its default severity, and what to change.

New here? Start with how to read and diagnose check output and how app.check runs at startup.

The two policy levers

chirp check myapp:app --warnings-as-errorspromotes warnings at the CLI boundary. For app-specific policy, override a category before running checks:

from chirp.contracts.types import Severity

app.override_contract_severity("dead", Severity.WARNING)
app.override_contract_severity("page_handlers", Severity.WARNING)

Routing And Pages

Category Default severity Fix target
routing ERROR Replace Flask-style route params such as<id> with Chirp's {id}route syntax.
route_names ERROR Rename one route or set an explicit module-level route name soapp.url_for()is unambiguous.
route_contract ERROR / INFO / WARNING Align filesystem route files, metadata, and declared contracts with discovered routes. See the route-directory contract.
page_handlers ERROR / WARNING Add a recognizedpage.py handler (get, post, another HTTP method, or handler) and fix handler-shaped typos.
method ERROR Ensure handlers are callable and route methods are supported.
target ERROR Fix route target declarations that point at missing routes.
page_context WARNING Move page block dependencies into the context available to direct fragment renders.
page_shell ERROR Register or correct app-shell targets, outlets, and shell contracts used by filesystem pages.
layout_chain INFO / WARNING Fix duplicate layout targets, default innerbody targets, broad hx-disinherit, or inheritance inside composed layouts.
layout_outlet WARNING Declare and register the outlet used by boosted navigation so narrow htmx responses are selected correctly.
layout_frame WARNING Keep immutable frame targets out of the fragment-target registry.
context_cascade INFO / WARNING Fix_context.pysignatures, inherited context providers, and intentional child overrides.
mount_app_merge INFO Review parent-wins dropped entries frommount_app()such as globals, filters, providers, handlers, and severity overrides.
setup ERROR Fix checker setup problems such as missing template loaders before trusting downstream contract output.

Templates And Blocks

Category Default severity Fix target
dead WARNING Remove unused templates or add a route, include, import, layout, or explicit docs/tool reference.
orphan INFO Reference the route from a template, mark it explicitly referenced, or accept that static analysis cannot see dynamic navigation.
fragment ERROR FixFragmentContractdeclarations that point at missing templates or blocks.
fragment_scope WARNING Move imports or bindings into the fragment block when direct block rendering would skip ancestor scope.
fragment_target_orphan ERROR / WARNING Register the missing block for a required fragment target, or mark legitimately absent regions optional.
fragment_target_scan ERROR Fix the template parse/load error that prevented fragment target orphan checks from completing.
unreachable_block WARNING Move sibling page blocks under the rendered page root or make them real fragment targets.
composition_extends WARNING Stop extending layout templates from page templates; compose pages into layout content blocks instead.
htmx_partial ERROR Correct<htmx-partial>sources, blocks, and route references.
inline_template WARNING Replace inline template strings when a named template would be checkable and reusable.
boundary INFO Keep route, template, and extension boundaries aligned so checks can map diagnostics to the right source.
islands ERROR / WARNING Register island roots and targets consistently when island strictness is enabled.
component ERROR Fix Kida/chirp-ui component-call diagnostics; precision depends on available typed component metadata.
template_contract WARNING Replace legacy component action contracts with current declarations.
template_context ERROR / WARNING Add missing dotted context paths to the provided or optional contract data, or stop reading them.
template_escape WARNING Review trusted-markup or escaping diagnostics surfaced by Kida.
template_privacy WARNING Remove private literals or mark non-public template content appropriately.
i18n_missing_key WARNING Add thet("…") key to the locale JSON catalog(s) under the i18n directory, or remove the t()call.
macro_css WARNING Activate chirp-ui (use_chirp_ui(app)) or ship your own CSS for the core-macro classes (chirp-dropdown, chirp-modal, field--error, …) when neither is present.
chirpui_css_verify WARNING Fix typoed or stalechirpui-* class tokens in literal class=attributes so they resolve to classes in the installed chirp-ui CSS. Only runs when chirp-ui is active.

HTMX And Swaps

Category Default severity Fix target
hx-target WARNING Fix statichx-target="#id"selectors that do not match any known template ID.
hx-indicator WARNING Fix statichx-indicator="#id"selectors that do not match any known template ID.
hx-boost WARNING Use onlyhx-boost="true" or hx-boost="false"for static boost values.
selector_syntax ERROR Fix invalid selector syntax in static htmx selector-bearing attributes.
select_inheritance WARNING Override inherited broadhx-select with an explicit selector or hx-select="unset" on the mutating element. hx-disinheritonly affects descendants, not the element itself.
swap_safety INFO / WARNING Add explicit local targets or isolate SSE swaps from broad inheritedhx-target/hx-swap.
template_stream_client_shape WARNING Use plain form POST forTemplateStream routes, or return Fragment/EventStream when htmx swaps into #target.
sse_token_swap_mode WARNING Append per-token SSE Fragments withhx-swap="beforeend"instead of replace swaps.
sse_eager_connect INFO Prefer POST → Fragment with parametricsse-connectwhen streaming should start after user action.
fragment_island INFO Addhx-disinheritor a fragment-island wrapper around local mutation targets.
view_transition_scope WARNING Scope View Transitions to navigation-only elements, not broad OOB/SSE live-update containers.
oob_registry ERROR / WARNING Add the registered OOB block/target, fix a typo, or make the region optional only when absence is legitimate. See the OOB registry.
oob_target WARNING Fixhx-swap-oobIDs that do not appear in any known template.
duplicate_id WARNING Remove or rename repeated staticid="..."values in the same template — duplicate ids break targeting and accessibility.
oob_fragment_orphan WARNING Wire a route, EventStream, or signal render callback that yields the OOB fragment block, or remove the dead swap target.
htmx_provisioned WARNING Provision htmx withAppConfig(htmx=True) or an htmx <script> in the layout chain when a template emits hx-*/sse-*attributes.

SSE And Reactive

Category Default severity Fix target
sse ERROR Fix route-levelSSEContractdeclarations that point at missing or inconsistent event/template data.
sse_self_swap ERROR Movesse-swap from the sse-connectelement to a child sink.
sse_scope ERROR Add an SSE scope boundary such ashx-disinherit="hx-target hx-swap"when streams live inside broad htmx targets.
sse_crossref ERROR / INFO Alignsse-swap="event" listeners with declared or inferred SSEEvent(event=...) and Fragment(target=...)channels.
sse_speculation WARNING Addreferenced=Trueto SSE routes so browser speculation does not open long-lived prefetch streams.
sse_auth_gate ERROR / WARNING RegisterAuthMiddleware (after SessionMiddleware) when an EventStream generator reads the request user (get_user() / current_user()). Without it the connect-time-captured SSE user is AnonymousUserfor the whole stream. Env-aware (silent dev / WARNING staging / ERROR prod). See the dropdown below.
sse_context WARNING Semantic nudge only — the pattern WORKS. SSE user identity is pinned at connect time; a mid-stream logout / permission change is not reflected. Re-check authorization per event (or close the stream on revoke) for an auth-sensitive long-lived feed. Never ERROR. See the dropdown below.
reactive_block ERROR FixDependencyIndex BlockReftemplate or block names.
reactive_cycle WARNING Remove cycles fromDependencyIndex.derive()relationships.
reactive_paths WARNING Register every declared emitted path in the dependency index or remove stale metadata.
reactive_audience WARNING Pair audience-filtered scopes withreactive_stream(..., connection=ConnectionInfo(...)).
live_block_unknown ERROR Fixlive_blockreferences to unknown templates or blocks.
live_block_unreachable_route ERROR Reference live blocks from reachable routes or remove stale declarations.
signal_dead_binding ERROR Declare a@app.signal('x') / @app.derived('x', ...) producer for every signal('x') / signal_block('x') / sse-swap="x" binding under the merged /_chirp/liveconnection, or fix the name. A bound signal with no registered producer never updates.
signal_raw_sse_swap INFO Prefer{{ signal_attrs('x') }} over hand-written sse-swap="x" on pages composed under signal_connect()so the binding is validated.
signal_orphan INFO Bind the registered signal withsignal()/signal_block()in a template, or remove the unused producer. An orphan signal is produced but never displayed.
signal_connect_budget INFO Merge multiple persistent/_chirp/live scopes into one signal_connect()wrapper — browsers cap concurrent SSE connections per origin (HTTP/1.1 footgun).
signal_scope ERROR / WARNING RegisterSessionMiddleware before using audience="session" signals so each connection can resolve its /_chirp/live?aud=…key. WARNs when a derived signal depends on both global and session-scoped deps — verify the mixed dependency graph is intentional.

Forms, Commands, And Safety

Category Default severity Fix target
form ERROR / WARNING AlignFormContract fields with actual <input>, <select>, and <textarea>names.
form_contract INFO Add aFormContractto POST routes targeted by static forms, or accept the informational gap.
csrf_form WARNING Add{{ csrf_field() }}, csrf_token(), or _csrf_token to static mutating forms when CSRFMiddlewareis active.
command WARNING Fix command declarations, route handlers, or command metadata.
commandfor WARNING Fix command target references that cannot be resolved.
vary WARNING Add requiredVarybehavior for cache-sensitive htmx or middleware paths.
allowed_hosts ERROR / WARNING Configure explicit hosts outside development instead ofallowed_hosts=("*",).
trusted_proxies WARNING Configure explicit reverse-proxy peer IPs/hostnames instead oftrusted_proxies=("*",)outside development. Details in the dropdown below.
csrf_session ERROR RegisterSessionMiddleware before CSRFMiddleware.
middleware_chain INFO Diagnostic only — reports the freeze-resolved user middleware order (outermost → innermost). Useadd_middleware(priority=...) to make the order explicit; lower priority runs outermost. Ordering violations (CSRF outside Session) are still the csrf_sessionERROR, not this category.
security_stack ERROR / WARNING Wire the secure-by-default stack on apps with mutating routes. See thesecurity_stackcanonical reference below.
auth_middleware ERROR / WARNING / INFO RegisterAuthMiddleware (after SessionMiddleware) when any route declares auth via RouteMeta.auth or @login_required/@requires. Without it the auth gate's get_user() raises LookupError→ 500. See the dropdown below.
auth_spec ERROR / WARNING Fix aRouteMeta.auth permission/policy/scope that will silently fail. Registry-backed when you declare app.register_permission() / app.register_policy() / app.register_scope()(unknown permission/policy/scope → ERROR); otherwise a high-signal reserved-token typo heuristic. See the dropdown below.
access_grant_scalar_loop ERROR / WARNING UseQuery.accessible_to(user, perm, resource_type=...) for paginated list filtering instead of calling check_access() / require_access()inside a handler loop (N+1 trap). Env-aware (silent dev / WARNING staging / ERROR prod).
settings_spec ERROR / WARNING Fix a declaredapp.register_setting() spec that shadows a boot-time AppConfig field or marks a sensitive value persistable (secret=False). Env-aware (silent dev / WARNING staging / ERROR prod).
cookie_secure ERROR / WARNING Make the session cookieSecure. Keep SessionConfig(secure="auto") (resolves to Secure in production/staging) or set secure=True. A samesite="none" cookie that is not Secureis an env-independent ERROR. See the dropdown below.
hsts WARNING SetAppConfig(strict_transport_security="max-age=63072000; includeSubDomains")on a production app with an auth/mutating surface — once you have confirmed it is only ever reached over HTTPS. Never auto-emitted. See the dropdown below.
password_extra WARNING Installchirp[auth](argon2id) on a production app with a login/mutating surface — without it password hashing falls back to stdlib scrypt. Advisory only (silent in development); existing scrypt hashes upgrade on next login. See the dropdown below.
passkeys ERROR / WARNING Installchirp[passkeys] when AppConfig(passkeys=True) — without webauthn every ceremony fails at runtime (env-independent ERROR). With a CookieSessionStore the single-use challenge bloats the signed cookie — prefer RedisSessionStore or a short absolute_timeout_seconds(env-aware WARNING, silent dev). See the dropdown below.
csp_nonce ERROR / WARNING Enable a per-request nonce mechanism (AppConfig(csp_nonce_enabled=True)) so framework inline scripts carry a nonce under an inline-forbidding CSP. See the dropdown below.
chirpui_csp ERROR / WARNING chirp-ui apps only. Remove a conflicting static CSP souse_chirp_ui's auto-wired nonce CSP can keep Alpine alive. See the dropdown below.
middleware_signature ERROR / WARNING Make middleware callable asasync __call__(request, next).
static_streaming WARNING KeepStaticFiles static_stream_threshold sane so large static files stream from disk instead of buffering into memory. Warns when the threshold is <= 0 or effectively unbounded (>= 1 GiB).
secret_key ERROR / WARNING Set a productionsecret_keyand use an adequately long value.
nojs_floor INFO ReturnFormAction (303 for plain POST, fragments for htmx) from mutating routes instead of an htmx-only Fragment/OOB. INFO by default; promote with override_contract_severity("nojs_floor", Severity.ERROR)to enforce the no-JS floor.
deploy_debug ERROR Setdebug=False (or CHIRP_DEBUG=0) when env="production".
deploy_metrics ERROR Changemetrics_pathor move the colliding application route so the Prometheus endpoint does not shadow a route.
deploy_health ERROR Rename the colliding application route or changehealth_path / ready_path so the auto-mounted /health + /readyprobes are not shadowed by an app route.
deploy_sentry WARNING Set a non-zerosentry_traces_sample_ratewhen a Sentry DSN is configured, or clear the DSN.

chirp check --deploy: production-posture preflight

The env-aware categories above (secret_key, allowed_hosts, security_stack, cookie_secure, hsts, password_extra, passkeys, auth_middleware, auth_spec, access_grant_scalar_loop, settings_spec, sse_auth_gate, sse_context, csp_nonce, chirpui_csp, deploy_debug, deploy_metrics, deploy_health, deploy_sentry) pick their severity from config.env. In development most are silent or WARNING, so a dev app passesapp.check()while still carrying production-blocking misconfigurations.chirp check myapp:app --deployanswers "would this pass in production?" without changing your config: it runs those rules against a throwaway production-posture view of the config and treats warnings as errors (--deploy implies --warnings-as-errors). It is tighten-only — only severities rise, so a genuinely deploy-ready app still passes — and it never mutates the running app. Programmatically this isapp.check(deploy=True). Use it as a CI deploy gate.

security_stack: canonical reference

security_stackis the canonical owner of the mutating route definition and the secure-by-default presence check. Severity is env-aware: missing CSRFMiddleware or SessionMiddlewareis ERROR in production, WARNING in staging, silent in development; missingSecurityHeadersMiddlewareis always WARNING (env-independent). Chirp force-injects no security middleware — the chirp new scaffolds (including --minimal) wire SessionMiddlewareCSRFMiddlewareSecurityHeadersMiddlewarefor you, so generated apps pass out of the box.csrf_session checks the ordering of that stack; csrf_form checks individual template<form> tags; security_stackis the route-level presence check.

Env-severity matrix. Two distinct severity tracks:

Missing middleware development staging production
CSRFMiddleware or SessionMiddleware silent WARNING ERROR
SecurityHeadersMiddleware WARNING WARNING WARNING

ASecure cookie is only ever sent over HTTPS. A session cookie without Secure — whether it carries the session data (CookieSessionStore) or just the session id (RedisSessionStore) — can be sniffed over a plaintext path and replayed to hijack the session. The check is store-agnostic: both stores emit a Set-Cookie, so it fires on the effective Secureflag, never on the store type.

It is a no-op when noSessionMiddleware is registered — security_stackalready owns the presence check. When aSessionMiddlewareis present, the check reads its effectiveSecure flag (SessionMiddleware.secure, resolved through the same resolve_cookie_securethe runtime uses, so the check and runtime never disagree). The blessed defaultSessionConfig(secure="auto") resolves to Secure in production/staging, so a default-config app passes.

Two severity tracks:

Condition development staging production
Session cookie notSecure(hardening gap) silent WARNING ERROR
samesite="none" and cookie not Secure ERROR ERROR ERROR

Thesamesite="none" + not-Securecase is env-independent ERROR because browsers silently drop aSameSite=None cookie that is not Secure— the session simply breaks in every environment, including development. That is a correctness footgun, not a hardening preference, so it is reported regardless of env.

Fix: keepSessionConfig(secure="auto")(the default) and serve over HTTPS in production/staging, or setSessionConfig(secure=True)explicitly. For the SameSite=None case, either add Secure or use samesite="lax"/"strict". Severity is env-aware (silent dev / WARNING staging / ERROR prod) for the hardening track and escalates underchirp check --deploy.

hsts: HTTP Strict Transport Security nudge

WARNING (never ERROR) when an app declaresenv="production", has an auth/mutating surface (sameis_mutating_route definition security_stackowns), and leaves strict_transport_security unset both on AppConfigand on any SecurityHeadersMiddleware. Without HSTS a first request over plain HTTP can be downgraded/MITM'd before the redirect to HTTPS.

This is deliberately a WARNING + docs nudge only — Chirp does not auto-emit an HSTS header, andstrict_transport_security=Noneis not overloaded to mean "auto";Nonestays "off". An HSTS header is an irreversible multi-year browser pin: emitting it becauseenvsays production — while the app may actually be reached over plain HTTP behind a misconfigured proxy — is worse than the gap. So the contract surfaces the gap and leaves the irreversible decision to you.

Fix: once you have confirmed the app is only ever reached over HTTPS, set AppConfig(strict_transport_security="max-age=63072000; includeSubDomains")(or add aSecurityHeadersMiddlewareconfigured with it). Either source clears the warning. It is silent in development and staging — only the declared production posture is nudged — and surfaces underchirp check --deploy.

password_extra: argon2 in production

WARNING (never ERROR) when an app has a login/mutating surface (the same is_mutating_route definition security_stack owns) and argon2-cffiis not importable — so password hashing falls back to stdlib scrypt. scrypt is a correct, always-available fallback (no hash is ever rejected andverify_password auto-detects the algorithm from the PHC prefix), but argon2id is the recommended algorithm for new production deployments.

This is a posture advisory, never an ERROR: there is no correctness gap to fail loud on, and existing scrypt hashes upgrade to argon2 on the next successful login viaverify_and_upgrade()once the extra is installed. Severity is env-aware — silent in development (and the scrypt-only base CI environment), WARNING in staging/production — so dev apps and shipped examples stay clean, and it escalates underchirp check --deploy. argon2 availability is read via the same_has_argon2()predicate the runtime uses to pick the hashing algorithm, so the check and the runtime never disagree.

Fix:pip install chirp[auth] (pulls in argon2-cffi). New hashes then use argon2id; existing scrypt hashes re-derive to argon2 the next time each user logs in if you wireverify_and_upgrade()(see Auth Hardening).

passkeys: WebAuthn ceremony posture

Fires only whenAppConfig(passkeys=True). Two distinct tracks:

  • webauthnnot installed → ERROR, env-independent. Passkeys have no stdlib fallback (unlikepassword_extra's scrypt path), so without the webauthn package every ceremony raisesConfigurationErrorat runtime in every environment. This is a broken config, not a hardening gap, so it is reported regardless ofenv — like the samesite="none"cookie-drop ERROR in cookie_secure. Availability is read via the same _has_webauthn()find-spec probe the runtime uses, so the check and runtime never disagree.
  • CookieSessionStorefor the challenge → WARNING, env-aware (silent dev / WARNING staging+production). The single-use WebAuthn challenge lives in the session between the begin and finish ceremonies, and the cookie store signs the entire session — including the ~86-char base64url challenge — into the client cookie, whereasRedisSessionStore strips __-prefixed keys from durable storage. The challenge is popped on finish, but abandoned begin-flows carry it until session timeout. Advisory only (mirrorspassword_extra); escalates under chirp check --deploy.

What this rule deliberately does not re-check, to stay non-redundant: security_stack already requires SessionMiddleware/CSRFMiddlewareon the mutating finish endpoints;PasskeyConfig.__post_init__validates the rp_id-is-a-registrable-suffix-of-origin invariant at construction; and the HTTPS/secure-context posture is covered bycookie_secure (a Securecookie implies HTTPS).

Fix:pip install chirp[passkeys] (pulls in webauthn, which pins cryptography). For passkey-heavy traffic on the cookie store, switch to RedisSessionStore or set a short SessionConfig(absolute_timeout_seconds=...).

auth_middleware: auth-declaring routes need AuthMiddleware

A route can declare that it needs authentication two ways, and both call get_user() — which raises LookupError→ a 500 at request time when AuthMiddlewareis absent from the stack:

  • RouteMeta.auth on a filesystem page (_meta.py): None / "none"/ "optional" are open; "required"requires an authenticated user; any other non-empty string is treated as a single required permission.
  • @login_required / @requires on an @app.routehandler. These decorators carry a static_chirp_requires_authmarker on the handler the router stores, so the check can prove the route is auth-gated without executing it.

When any auth-declaring route exists and noAuthMiddlewareis registered, this fires, naming a concrete offending route and the fix (register AuthMiddleware after SessionMiddleware).

Env-severity matrix. Mirrorssecurity_stack— the dev 500 surfaces the gap locally, so a standing dev WARNING would be noise:

Condition development staging production
Auth-declaring route + noAuthMiddleware silent WARNING ERROR
Dynamicmeta() page + no AuthMiddleware INFO INFO INFO

Dynamicmeta() pages are a static blind spot. A page whose _meta.py definesmeta()resolves its auth value at runtime, so its requirement is invisible to a static check. Those pages are never false-ERRORed; instead, whenAuthMiddlewareis absent and such pages exist, a single INFO notes that auth wiring could not be statically verified — wireAuthMiddlewareif any dynamic-meta page is gated. This mirrors howroute_contract's section-coverage check handles meta providers.

Fix: registerAuthMiddleware after SessionMiddleware (app.add_middleware(AuthMiddleware(AuthConfig(load_user=...)))). It is silent in development and escalates underchirp check --deploy.

auth_spec: declared permissions/policies that silently fail

RouteMeta.auth treats any non-reserved string (and every AuthSpec.permissions entry) as a required permission, and anAuthSpec.policyas a named policy resolved against the app policy registry. A wrong permission silently403s; an unresolved policy name fails loud (500— a misconfiguration, not an auth denial) — both with no other startup signal. This check has two modes.

Registry-backed (precise). When you declare permissions/policies during setup:

app.register_permission("admin")
app.register_permission("editor")
app.register_policy("is_owner", lambda user, request: ...)

every RouteMeta.authpermission not in the permission registry is an ERROR, and everyAuthSpec.policynot in the policy registry is an ERROR. The registry is the source of truth, so even a plausible-looking"admni"typo is caught.

Permissions and policies are checked asymmetrically. Permission validation is opt-in: it only runs when a permission registry is declared (otherwise the heuristic below applies). Policy validation is always on: a referenced AuthSpec.policyname that is not registered is always an ERROR — even with no policy registered at all — because an unregistered policy name unconditionally 500s at request time, so there is no false-positive risk.

Heuristic-only (no permission registry). With no permission registry declared, permission validation falls back to a high-signal reserved-token-confusion heuristic. A near-miss of a reserved token —"Required", "REQUIRED", " required ", "None", "Optional", or a tight misspelling like "requied"— silently becomes a permission named that exact string and 403s forever instead of gating as intended. Empty-after-strip / whitespace-only auth (" ") is the same bug. It flags exactly:

  • a case/whitespace variant of a reserved token ("Required", " required ", "None", "Optional");
  • a tight misspelling of"required" (edit distance ≤ 2, e.g. "requied"); and
  • empty-after-strip / whitespace-only.

Plausible permission names ("admin", "editor", "billing.read") are not flagged in heuristic mode — without a registry Chirp cannot know which strings are real permissions, and false positives erode trust. Declare a permission registry to validate them precisely.

Machine-token scopes (AuthSpec.scopes). The machine-auth axis — webhook / cron / provisioning endpoints gate on a token-resolved client's scopes independently of human permissions — folds into this sameauth_speccategory (no separate category). Scope validation is opt-in like permissions: declare scopes withapp.register_scope("webhook:write") and every AuthSpec.scopes entry not in the registry becomes an env-aware ERROR. With no scope registry, scopes are free strings and are not heuristically flagged (a scope is an arbitrary machine token, so a plausible-name heuristic has no signal).

Severity is env-aware (silent dev / WARNING staging / ERROR prod), same as auth_middleware, and escalates under chirp check --deploy. Dynamic meta() pages are skipped (their auth value is not statically known).

Fix: declare the permission withapp.register_permission()(or fix the typo), register the policy withapp.register_policy(), declare the scope with app.register_scope(), or use the exact reserved token ("required" / "none"/ "optional").

sse_auth_gate / sse_context: reading the user inside an SSE generator

get_request(), get_user() / current_user(), get_csrf_token(), and g work inside anEventStreamgenerator — the request-scoped context is captured at connect time and re-established for the lifetime of the stream. Two checks guard the two ways this can surprise you.

sse_auth_gate — the user is AnonymousUser without AuthMiddleware. An EventStream generator that calls get_user() / current_user()resolves the connect-time-captured user, but only whenAuthMiddlewareis wired. Without it, the captured user isAnonymousUserfor the entire stream, so an auth-sensitive feed silently serves the anonymous view to everyone — no error, no startup signal. Env-aware, mirroringauth_middleware:

Condition development staging production
SSE generator reads the user + noAuthMiddleware silent WARNING ERROR

sse_context— the SSE user identity is pinned at connect time. This is a post-fix semantic nudge, never an ERROR — the pattern WORKS. When a generator reads the user inside a long-lived loop (while / async for / for), the identity is fixed for the connection's lifetime: a mid-stream logout or permission revoke is not reflected until the client reconnects. It fires as a WARNINGin staging/production (silent in development) so you can decide whether the feed is auth-sensitive enough to re-check authorization per event (or close the stream on revoke). A short-lived top-level user read (outside any loop) is not nudged — it resolves once and the stream ends.

These rules do not fire on the shippedchat / kanban / dashboard/ lucky_catSSE examples: their generators read global state (the message bus, the task store, the market feed), never the request user.

Data

Category Default severity Fix target
data ERROR Fixdb.fetch(cls, sql) / fetch_one / streamSELECT columns that map to no field on the target frozen dataclass. See the dropdown below for what it fires on.
shapecheck ERROR / WARNING / INFO Verify the render side of@shape-decorated row models. See the dropdown below for the four checks and escape hatches.

Debug, Extensions, Accessibility, And Plugins

Category Default severity Fix target
debug_wiring ERROR Fix debug/DevTools route, asset, or runtime wiring so diagnostics work in debug mode.
chirpui_runtime INFO Calluse_chirp_ui(app)or install/configure the optional UI runtime required by ChirpUI templates.
chirpui_alpine_runtime ERROR / WARNING Wirechirpui-alpine.js registration in the served HTML when templates use interactive ChirpUI Alpine factories (theme toggle, dialogs, dropdowns). ERROR when debug=True, WARNINGotherwise.
alpine_cdn_url ERROR Replace bare jsDelivr Alpine package URLs with explicit/dist/cdn.min.jsURLs or Chirp injection helpers.
defer_falsy WARNING Use{% if key is deferred %} or "key" in __chirp_defer_pending__to distinguish loading from loaded before testing resolved values. See Suspense deferred keys.
suspense_defer WARNING A template declares a Suspense-deferred key (is deferred / __chirp_defer_pending__) that no block depends on, so auto-discovery finds nothing to re-render. Reference the key inside a {% block ... %}, or pass the blocks explicitly with Suspense(..., defer_blocks=(...)).
a11y_interactive WARNING Add keyboard and semantic affordances for interactive elements.
a11y_label WARNING Add visible or accessible labels for form controls.
a11y_alt WARNING Add meaningfulalt text or intentionally empty decorative alt="".
a11y_heading WARNING Fix skipped or incoherent heading levels.
a11y_landmark WARNING Add page landmarks such asmain, nav, or headerwhere expected.
plugin_check_error ERROR Fix a custom check that raised duringapp.check(); the original exception should name the plugin/check.
plugin_quarantine ERROR Fix the plugin whoseregister() raised during app.mount(). The plugin was quarantined (skipped) so the app could still boot — leaving it half-configured — and a WARNING was logged at mount time. The message names the prefix, plugin, and original error.