@mrciphersmith/keryx 0.3.5 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/cli.js +872 -316
  2. package/docs/README.md +2 -0
  3. package/package.json +1 -1
  4. package/src/gdskills/bundled/install-manifest.json +319 -76
  5. package/src/gdskills/bundled/stacks/django/agent-refs.json +3 -0
  6. package/src/gdskills/bundled/stacks/django/governance/eval.json +1763 -0
  7. package/src/gdskills/bundled/stacks/django/governance/scout.json +40 -0
  8. package/src/gdskills/bundled/stacks/django/pack.json +43 -0
  9. package/src/gdskills/bundled/stacks/django/rules/coding-style.mdc +80 -0
  10. package/src/gdskills/bundled/stacks/django/rules/patterns.mdc +92 -0
  11. package/src/gdskills/bundled/stacks/django/rules/security.mdc +92 -0
  12. package/src/gdskills/bundled/stacks/django/rules/testing.mdc +89 -0
  13. package/src/gdskills/bundled/stacks/django/skills/django-build-fix/SKILL.md +149 -0
  14. package/src/gdskills/bundled/stacks/django/skills/django-build-fix/evals.json +49 -0
  15. package/src/gdskills/bundled/stacks/django/skills/django-code-review/SKILL.md +137 -0
  16. package/src/gdskills/bundled/stacks/django/skills/django-code-review/evals.json +48 -0
  17. package/src/gdskills/bundled/stacks/django/skills/django-implementation/SKILL.md +147 -0
  18. package/src/gdskills/bundled/stacks/django/skills/django-implementation/evals.json +75 -0
  19. package/src/gdskills/bundled/stacks/django/skills/django-migrate/SKILL.md +166 -0
  20. package/src/gdskills/bundled/stacks/django/skills/django-migrate/evals.json +49 -0
  21. package/src/gdskills/bundled/stacks/django/skills/django-testing/SKILL.md +130 -0
  22. package/src/gdskills/bundled/stacks/django/skills/django-testing/evals.json +48 -0
  23. package/src/gdskills/bundled/stacks/fastapi/agent-refs.json +3 -0
  24. package/src/gdskills/bundled/stacks/fastapi/governance/eval.json +1777 -0
  25. package/src/gdskills/bundled/stacks/fastapi/governance/scout.json +34 -0
  26. package/src/gdskills/bundled/stacks/fastapi/pack.json +43 -0
  27. package/src/gdskills/bundled/stacks/fastapi/rules/coding-style.mdc +68 -0
  28. package/src/gdskills/bundled/stacks/fastapi/rules/patterns.mdc +108 -0
  29. package/src/gdskills/bundled/stacks/fastapi/rules/security.mdc +99 -0
  30. package/src/gdskills/bundled/stacks/fastapi/rules/testing.mdc +85 -0
  31. package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-build-fix/SKILL.md +157 -0
  32. package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-build-fix/evals.json +76 -0
  33. package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-code-review/SKILL.md +150 -0
  34. package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-code-review/evals.json +74 -0
  35. package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-implementation/SKILL.md +158 -0
  36. package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-implementation/evals.json +75 -0
  37. package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-testing/SKILL.md +146 -0
  38. package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-testing/evals.json +74 -0
  39. package/src/gdskills/bundled/stacks/java-kotlin-spring/agent-refs.json +3 -0
  40. package/src/gdskills/bundled/stacks/java-kotlin-spring/governance/eval.json +2194 -0
  41. package/src/gdskills/bundled/stacks/java-kotlin-spring/governance/scout.json +39 -0
  42. package/src/gdskills/bundled/stacks/java-kotlin-spring/pack.json +40 -0
  43. package/src/gdskills/bundled/stacks/java-kotlin-spring/rules/coding-style.mdc +67 -0
  44. package/src/gdskills/bundled/stacks/java-kotlin-spring/rules/patterns.mdc +65 -0
  45. package/src/gdskills/bundled/stacks/java-kotlin-spring/rules/security.mdc +69 -0
  46. package/src/gdskills/bundled/stacks/java-kotlin-spring/rules/testing.mdc +80 -0
  47. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-build-fix/SKILL.md +144 -0
  48. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-build-fix/evals.json +74 -0
  49. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-code-review/SKILL.md +129 -0
  50. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-code-review/evals.json +74 -0
  51. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-implementation/SKILL.md +147 -0
  52. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-implementation/evals.json +75 -0
  53. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-migrate/SKILL.md +139 -0
  54. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-migrate/evals.json +74 -0
  55. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-testing/SKILL.md +128 -0
  56. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-testing/evals.json +73 -0
  57. package/src/gdskills/bundled/stacks/python/agent-refs.json +2 -1
  58. package/src/gdskills/bundled/stacks/python/pack.json +1 -1
  59. package/src/gdskills/bundled/stacks/rust/agent-refs.json +3 -0
  60. package/src/gdskills/bundled/stacks/rust/governance/eval.json +1823 -0
  61. package/src/gdskills/bundled/stacks/rust/governance/scout.json +32 -0
  62. package/src/gdskills/bundled/stacks/rust/pack.json +42 -0
  63. package/src/gdskills/bundled/stacks/rust/rules/coding-style.mdc +93 -0
  64. package/src/gdskills/bundled/stacks/rust/rules/patterns.mdc +85 -0
  65. package/src/gdskills/bundled/stacks/rust/rules/security.mdc +85 -0
  66. package/src/gdskills/bundled/stacks/rust/rules/testing.mdc +82 -0
  67. package/src/gdskills/bundled/stacks/rust/skills/rust-build-fix/SKILL.md +141 -0
  68. package/src/gdskills/bundled/stacks/rust/skills/rust-build-fix/evals.json +78 -0
  69. package/src/gdskills/bundled/stacks/rust/skills/rust-code-review/SKILL.md +127 -0
  70. package/src/gdskills/bundled/stacks/rust/skills/rust-code-review/evals.json +72 -0
  71. package/src/gdskills/bundled/stacks/rust/skills/rust-implementation/SKILL.md +133 -0
  72. package/src/gdskills/bundled/stacks/rust/skills/rust-implementation/evals.json +79 -0
  73. package/src/gdskills/bundled/stacks/rust/skills/rust-testing/SKILL.md +130 -0
  74. package/src/gdskills/bundled/stacks/rust/skills/rust-testing/evals.json +75 -0
  75. package/src/gdskills/bundled/agents/python-build-fixer.md +0 -52
  76. package/src/gdskills/bundled/agents/python-code-auditor.md +0 -49
@@ -0,0 +1,34 @@
1
+ [
2
+ {
3
+ "query": "Use when a FastAPI app itself fails to start, import, or register a router -- app-startup failures, dependency-injection wiring errors (Depends() on the wrong callable, a missing sub-dependency), Pydantic v1/v2 model/validation errors, and a checker flagging a path-operation signature or response_model specifically, with the smallest root-cause fix. For a generic Python ModuleNotFoundError/packaging/dependency-resolver failure not specific to FastAPI's own app wiring, use python-build-fix.",
4
+ "decision": "fork",
5
+ "topMatch": "nestjs/nestjs-build-fix",
6
+ "recordedAt": "2026-09-25T18:06:20.873Z",
7
+ "skillName": "fastapi-build-fix",
8
+ "justification": "top match python/python-build-fix is the same build-fix category for generic Python tooling (mypy/ruff/pip), not FastAPI's own Depends resolution, router registration, and Pydantic validation-error failure modes; a FastAPI-scoped fork is warranted since python-build-fix has no visibility into FastAPI's own dependency-injection wiring."
9
+ },
10
+ {
11
+ "query": "Use when reviewing FastAPI changes for correctness and safety risks -- checks a synchronous driver or library call left in a route handler's coroutine, request bodies accepted as dict/Any instead of Pydantic models, missing response_model filtering, missing or misplaced auth dependencies, unsafe CORS configuration, and hard-coded secrets. Read-only: reports findings, does not edit code, and is scoped to FastAPI's own request lifecycle rather than general-purpose backend correctness.",
12
+ "decision": "create",
13
+ "topMatch": "fastapi/fastapi-testing",
14
+ "recordedAt": "2026-09-25T18:06:21.226Z",
15
+ "skillName": "fastapi-code-review",
16
+ "justification": "top match python/python-code-review is the same review category for generic Python risks, not FastAPI's own async/blocking-call, response_model, and auth-dependency concerns; a FastAPI-scoped fork covers concerns python-code-review cannot see."
17
+ },
18
+ {
19
+ "query": "Use when implementing or extending a FastAPI path operation, dependency, or Pydantic schema -- covers async vs. sync path operations and FastAPI's threadpool behavior, Depends()-based dependency injection, Pydantic v2 request/response models, response_model filtering, background tasks, and OpenAPI/router conventions. Scoped to FastAPI's own request-handling surface, not general-purpose Python application code with no HTTP layer.",
20
+ "decision": "fork",
21
+ "topMatch": "fastapi/fastapi-testing",
22
+ "recordedAt": "2026-09-25T18:06:21.577Z",
23
+ "skillName": "fastapi-implementation",
24
+ "justification": "top match fastapi/fastapi-code-review is this same pack's read-only review skill, sharing only generic FastAPI-project vocabulary from its own audit scope, not implementation guidance; decision is fork since both are legitimately FastAPI-scoped but cover disjoint workflows (writing new code vs. auditing an existing diff without editing it)."
25
+ },
26
+ {
27
+ "query": "Use when you write, extend, or fix a FastAPI project's test suite -- driving synchronous or async HTTP requests against the running app in-process, app.dependency_overrides for auth/DB fixtures, asserting response status codes and response_model filtering, and mocking an external HTTP call. Scoped to a project that actually has a FastAPI HTTP layer under test, not a generic Python test suite with none.",
28
+ "decision": "fork",
29
+ "topMatch": "fastapi/fastapi-implementation",
30
+ "recordedAt": "2026-09-25T18:06:21.899Z",
31
+ "skillName": "fastapi-testing",
32
+ "justification": "top match python/python-testing is the same test-authoring category for generic pytest suites, not FastAPI's own TestClient/dependency_overrides/response-model-assertion idiom; a FastAPI-scoped fork is warranted since python-testing has no FastAPI-specific HTTP-layer guidance."
33
+ }
34
+ ]
@@ -0,0 +1,43 @@
1
+ {
2
+ "id": "fastapi",
3
+ "family": "framework",
4
+ "extends": "python",
5
+ "modules": ["fastapi-rules", "fastapi-skills"],
6
+ "detectionMarkers": ["fastapi"],
7
+ "provenance": {
8
+ "origin": "authored",
9
+ "sourceRef": "flow 335, Wave 4 batch 3"
10
+ },
11
+ "stability": "experimental",
12
+ "skills": {
13
+ "implement": ["fastapi-implementation"],
14
+ "test": ["fastapi-testing"],
15
+ "review": ["fastapi-code-review"],
16
+ "build-fix": ["fastapi-build-fix"],
17
+ "migrate": []
18
+ },
19
+ "agentProfile": {
20
+ "displayName": "FastAPI",
21
+ "auditFocus": [
22
+ "a blocking call (sync DB driver, `requests`, `time.sleep`, sync file I/O) inside an `async def` path operation or dependency instead of a `def` one FastAPI can offload to its threadpool",
23
+ "a request body or query model that skips Pydantic validation by typing a parameter as `dict`/`Any` instead of a `BaseModel`",
24
+ "a path operation with no `response_model`/return type where one would prevent leaking internal fields (e.g. `hashed_password`) in the response",
25
+ "a dependency-injected auth check (`Depends(get_current_user)`) missing from a path operation that should require it",
26
+ "`CORSMiddleware` configured with `allow_origins=[\"*\"]` alongside `allow_credentials=True`, which browsers reject and which is unsafe if they didn't",
27
+ "secrets/config read from hard-coded values instead of `pydantic-settings`/environment, or a `SECRET_KEY` committed in source"
28
+ ],
29
+ "buildCommands": [
30
+ "ruff check .",
31
+ "ruff format --check .",
32
+ "mypy . || pyright",
33
+ "pytest -x -q",
34
+ "python -c \"import <app_module>\" # or: uvicorn <app_module>:app --port 0 as an app-startup smoke check"
35
+ ],
36
+ "fixGuardrails": [
37
+ "never add `# type: ignore` or `# noqa` to silence a checker without fixing or explaining the underlying issue",
38
+ "never move a blocking call into an `async def` path operation to \"simplify\" it -- keep it `def` so FastAPI's threadpool handles it, or make the call itself async",
39
+ "never widen a Pydantic model's field type to `Any`/`dict` just to make validation errors stop",
40
+ "fix the smallest root cause; do not refactor unrelated code while resolving a build/lint/type failure"
41
+ ]
42
+ }
43
+ }
@@ -0,0 +1,68 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.py"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # FastAPI coding style
9
+
10
+ Narrows `python`'s coding style to FastAPI's own project conventions. A
11
+ FastAPI project is still a Python project — everything not specific to
12
+ FastAPI's own idiom still comes from `python`'s `rules/coding-style.mdc`,
13
+ which this file extends.
14
+
15
+ ## Path operations
16
+
17
+ - Name path operation functions for the action they perform
18
+ (`read_item`, `create_user`), not for the HTTP method alone — the
19
+ decorator (`@app.get(...)`, `@app.post(...)`) already states the method.
20
+ - Group related path operations behind an `APIRouter` per resource/domain
21
+ (`routers/items.py` exposing `router = APIRouter(prefix="/items",
22
+ tags=["items"])`) rather than registering every route directly on a single
23
+ `app` instance once the project has more than a handful of endpoints.
24
+ - Declare an explicit `response_model` (or a return type annotation FastAPI
25
+ can use as one) on every path operation that returns a Pydantic model or
26
+ ORM object — this is what filters the response to the declared fields and
27
+ keeps an internal-only field (a password hash, an audit flag) from
28
+ leaking into the API response.
29
+ - Use `Annotated[<type>, Depends(...)]`/`Annotated[<type>, Query(...)]` etc.
30
+ for parameter declarations (the current recommended style) rather than
31
+ the bare `= Depends(...)`/`= Query(...)` default-value form, unless the
32
+ project's own code still uses the older form consistently.
33
+
34
+ ## Pydantic models
35
+
36
+ - Define request/response schemas as Pydantic `BaseModel` subclasses, not
37
+ as bare `dict`/`Any` parameters — see `rules/patterns.mdc` for why this is
38
+ also the project's injection defense, not just a style preference.
39
+ - Keep a model's field constraints on the field itself (`Field(gt=0,
40
+ le=100)`, `Literal[...]` for a closed set of values) instead of
41
+ re-validating the same constraint by hand in the path operation body.
42
+ - Separate the model used for input (what a client may send) from the model
43
+ used for output (what the API returns) when they differ by even one field
44
+ — do not reuse a single model for both and rely on route logic to strip
45
+ the extra field before responding.
46
+ - Configure a model with `model_config = ConfigDict(...)` (Pydantic v2), not
47
+ the v1 inner `class Config:` — the project's Pydantic version is declared
48
+ in `pyproject.toml`; confirm it before assuming v2-only syntax.
49
+
50
+ ## Dependencies
51
+
52
+ - Keep a dependency function small and single-purpose (fetch the current
53
+ user, open a DB session, parse a header) — a dependency that both
54
+ authenticates and performs business logic should split so the auth part
55
+ is reusable on its own.
56
+ - Prefer a `yield`-based dependency (`def get_db(): ... yield session ...
57
+ finally: session.close()`) for anything that needs teardown, over a
58
+ plain-return dependency plus a manual close call in the path operation.
59
+
60
+ ## Project layout
61
+
62
+ - Match the project's existing layout — a `src/<package>/`
63
+ `routers/`, `models/`/`schemas/`, `dependencies.py`, `main.py` split for a
64
+ multi-router app, or a single `main.py` for a small service — rather than
65
+ introducing a different structure alongside it.
66
+ - Keep the FastAPI `app = FastAPI(...)` instantiation and top-level
67
+ middleware/router registration in one place (typically `main.py`); do not
68
+ scatter `app.include_router(...)` calls across unrelated modules.
@@ -0,0 +1,108 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.py"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # FastAPI patterns
9
+
10
+ Narrows `python`'s stack-agnostic design guidance (and its own
11
+ `asyncio`/resource patterns) to FastAPI's own request lifecycle: dependency
12
+ injection, async vs. sync path operations, response modeling, and
13
+ background work. Applies only to FastAPI project files.
14
+
15
+ ## Async vs. sync path operations
16
+
17
+ - Declare a path operation `async def` only when its body actually awaits
18
+ something async (an async DB driver, `httpx.AsyncClient`, another
19
+ awaitable). An `async def` path operation runs directly on the event
20
+ loop, so a blocking call inside it (a sync DB driver, `requests.get`,
21
+ `time.sleep`, blocking file I/O, a CPU-bound loop) stalls every other
22
+ in-flight request on that worker.
23
+ - Declare a path operation with plain `def` when its body calls a blocking
24
+ library with no async equivalent. FastAPI runs a `def` path operation in
25
+ an external threadpool automatically — this is current, documented
26
+ behavior, not a workaround — so a blocking call inside it does not block
27
+ the event loop.
28
+ - The same rule applies to dependencies: a `def` dependency runs in the
29
+ threadpool, an `async def` dependency runs on the loop and must not
30
+ block. FastAPI accepts either kind of dependency under either kind of
31
+ path operation (an `async def` dependency under a `def` path operation
32
+ and vice versa), so choose per-dependency based on what its own body
33
+ does, not to match the path operation's own declaration.
34
+ - For a compute-only path operation with no I/O at all, prefer `async def`
35
+ — it avoids the threadpool's scheduling overhead for work that was never
36
+ going to block anything.
37
+
38
+ ## Dependency injection
39
+
40
+ - Use `Depends(...)` (or `Security(...)` for an auth dependency that also
41
+ carries OAuth2 scopes) to inject anything a path operation needs but
42
+ should not construct itself: a DB session, the current authenticated
43
+ user, pagination parameters, a feature flag. This is what makes each
44
+ piece independently testable via FastAPI's `app.dependency_overrides`.
45
+ - Chain dependencies (a dependency that itself depends on another) instead
46
+ of duplicating the same setup logic across sibling dependencies — FastAPI
47
+ caches a dependency's result per request by default, so a shared
48
+ sub-dependency runs once even when several path-level dependencies need
49
+ it.
50
+ - Use `yield` in a dependency that owns a resource needing teardown (a DB
51
+ session, a lock, an open connection); the code after `yield` runs after
52
+ the response is sent, even when the path operation raised — this is
53
+ FastAPI's own resource-lifecycle pattern for a request-scoped resource,
54
+ not a manual `try`/`finally` bolted onto a plain-return dependency.
55
+
56
+ ## Request/response modeling
57
+
58
+ - Model every request body as a Pydantic `BaseModel`, never as a raw
59
+ `dict`/`Any` parameter — FastAPI validates the incoming JSON against the
60
+ model before the path operation body runs, which is the framework's own
61
+ first line of defense against malformed or malicious input (see
62
+ `rules/security.mdc`).
63
+ - Use `field_validator`/`model_validator` (Pydantic v2) for a constraint
64
+ that spans multiple fields or needs custom logic beyond `Field(...)`'s
65
+ declarative constraints (`gt`, `max_length`, `pattern`), not a manual
66
+ `if`-check inside the path operation body after the model has already
67
+ validated.
68
+ - Declare `response_model` (or a return-type annotation FastAPI can infer
69
+ one from) so the response is filtered to the declared fields even when
70
+ the object returned from the path operation carries more — this is a
71
+ correctness property, not just documentation: it is what keeps an
72
+ internal-only field out of the response body.
73
+
74
+ ## Background work
75
+
76
+ - Use `BackgroundTasks` (injected the same way as any other dependency)
77
+ for work that must run after the response is sent but is short, best-
78
+ effort, and does not need retries, a result, or cross-process durability
79
+ (writing a log line, sending a best-effort notification).
80
+ - Reach for a real task queue (Celery, arq, a message broker) instead of
81
+ `BackgroundTasks` once the work needs retries, visibility into failure,
82
+ distribution across multiple worker processes, or survival past the
83
+ serving process' lifetime — `BackgroundTasks` runs in-process and is lost
84
+ if the process restarts before it completes.
85
+
86
+ ## OpenAPI and schema
87
+
88
+ - Let FastAPI generate the OpenAPI schema from type hints, `response_model`,
89
+ and `Field(...)` constraints rather than hand-maintaining a separate
90
+ OpenAPI YAML/JSON file alongside the code — the generated schema is what
91
+ keeps documentation and implementation from drifting apart.
92
+ - Use `tags=[...]` on each `APIRouter`/path operation and a `summary`/
93
+ `description` (or a docstring, which FastAPI uses as the description)
94
+ for anything beyond an obvious CRUD endpoint, so the generated docs stay
95
+ usable as the API grows.
96
+
97
+ ## Anti-patterns to flag, not introduce
98
+
99
+ - An `async def` path operation that calls a synchronous DB driver or
100
+ `requests.get` directly — this silently degrades every concurrent request
101
+ on that worker instead of raising an obvious error.
102
+ - Accepting the request body as `request: Request` and manually calling
103
+ `await request.json()` to bypass Pydantic validation — this discards
104
+ FastAPI's own validation, error responses, and OpenAPI schema generation
105
+ for that endpoint.
106
+ - Using `BackgroundTasks` for work that must not be lost (payment capture,
107
+ an email a user is relying on) instead of a real task queue with
108
+ retries.
@@ -0,0 +1,99 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.py"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # FastAPI security
9
+
10
+ Narrows `python`'s security guidance to where FastAPI's own attack surface
11
+ actually lives: path operations, dependencies, and request-body/response
12
+ models — the code an unauthenticated or under-authenticated client can
13
+ reach directly. In practice that means `routers/`/`api/` modules,
14
+ `dependencies.py`, and `schemas/`/`models/` request-body definitions, not
15
+ test files, where the same patterns (a hard-coded test JWT, a permissive
16
+ CORS config asserted against) are often deliberately exercised rather than
17
+ shipped.
18
+
19
+ ## Input validation
20
+
21
+ - Type every request body, query, and path parameter through Pydantic (a
22
+ `BaseModel` for the body, typed function parameters for query/path
23
+ params) rather than reading raw JSON off `Request` — Pydantic validation
24
+ is FastAPI's primary injection defense: a field typed `int` cannot carry
25
+ a SQL-injection string or an oversized payload past validation the way an
26
+ untyped `dict`/`Any` parameter can.
27
+ - Use `Field(...)` constraints (`max_length`, `gt`/`le`, `pattern`) on any
28
+ field whose size or shape matters for downstream safety (a filename, a
29
+ free-text field written to a file path, a numeric field used in a
30
+ calculation) — validating shape at the model is cheaper and more
31
+ reliable than validating it again deeper in the call stack.
32
+ - Never accept a client-supplied value as a filesystem path, shell
33
+ argument, or SQL fragment without the same validation/parameterization
34
+ `python`'s `rules/security.mdc` already requires — FastAPI's own
35
+ validation does not substitute for parameterized SQL or `shell=True`
36
+ avoidance once the value leaves the Pydantic model.
37
+
38
+ ## Authentication and authorization
39
+
40
+ - Put an auth check in a `Depends(...)` (or `Security(...)` for scoped
41
+ OAuth2) dependency shared across every path operation that needs it, not
42
+ duplicated inline in each path operation body — a dependency added once
43
+ and reused is much harder to accidentally omit from a new endpoint than
44
+ a copy-pasted inline check.
45
+ - Use `OAuth2PasswordBearer`/`OAuth2PasswordRequestForm` (or the project's
46
+ existing auth scheme) with a real JWT library (`pyjwt`) and a password
47
+ hashing library (`pwdlib`/`passlib`) for credential handling — never
48
+ compare a plaintext password directly or hash it with a fast general-
49
+ purpose hash (`md5`, `sha256` with no salt/work factor).
50
+ - Run the password-verify step even on a failed username lookup (compare
51
+ against a dummy hash) — skipping verification when the username is
52
+ unknown lets an attacker time the difference between "unknown user" and
53
+ "wrong password" and enumerate valid usernames.
54
+ - Never trust a client-supplied role/permission field on the request body
55
+ to grant elevated access — authorization decisions come from the
56
+ authenticated user record (via the auth dependency) or a trusted store,
57
+ never from data the same request also supplies.
58
+
59
+ ## CORS
60
+
61
+ - Configure `CORSMiddleware`'s `allow_origins` with an explicit list of
62
+ trusted origins for any API that accepts cookies/credentialed requests;
63
+ never combine `allow_origins=["*"]` with `allow_credentials=True` —
64
+ browsers reject that combination, and if a proxy or older client allowed
65
+ it anyway it would let any origin make authenticated requests on the
66
+ user's behalf.
67
+ - Scope `allow_methods`/`allow_headers` to what the API actually needs
68
+ rather than defaulting every project to `["*"]`/`["*"]` out of habit.
69
+
70
+ ## Secrets and configuration
71
+
72
+ - Read secrets (`SECRET_KEY`, database URLs, third-party API keys) through
73
+ `pydantic-settings`' `BaseSettings` (or the project's existing settings
74
+ module) backed by environment variables, never hard-coded in source —
75
+ including in an example/tutorial-style constant left over from scaffolding.
76
+ - Generate a real `SECRET_KEY` per environment (e.g. `openssl rand -hex
77
+ 32`) and never reuse a tutorial/example key found in documentation or a
78
+ starter template in anything beyond local experimentation.
79
+
80
+ ## Data access
81
+
82
+ - Build SQL through the ORM's query builder or parameterized raw queries,
83
+ never by interpolating a Pydantic model's field into a SQL string — the
84
+ same rule `python`'s `rules/security.mdc` states for any Python code,
85
+ restated here because a validated Pydantic field is still just a string
86
+ once it reaches a raw query.
87
+
88
+ ## Red flags
89
+
90
+ - "I'll read the body with `await request.json()` here, the Pydantic model
91
+ felt like overkill for one field" — skips validation entirely for that
92
+ endpoint; use a `BaseModel`, even a one-field one.
93
+ - "`allow_origins=[\"*\"]` is fine, we don't use cookies" — true only until
94
+ the API adds a credentialed endpoint later; scope origins explicitly from
95
+ the start rather than relying on the current absence of cookies.
96
+ - "This SECRET_KEY from the FastAPI tutorial is just a placeholder, I'll
97
+ swap it before we ship" — a placeholder committed to source control is a
98
+ leaked secret the moment the repository is; generate the real one at
99
+ deploy time from the environment instead.
@@ -0,0 +1,85 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.py"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # FastAPI testing
9
+
10
+ Narrows `python`'s `pytest` conventions to FastAPI's own test client and
11
+ dependency-override patterns. Applies only to FastAPI project files; see
12
+ `skills/fastapi-testing/SKILL.md` for the full test-authoring workflow this
13
+ rule file backs.
14
+
15
+ ## Test client
16
+
17
+ - Test path operations through `fastapi.testclient.TestClient` (sync tests)
18
+ or `httpx.AsyncClient` with `ASGITransport` (async tests) against the
19
+ real `app` instance, rather than calling the path operation function
20
+ directly — calling the function directly skips FastAPI's own request
21
+ parsing, dependency resolution, and response-model filtering, which is
22
+ exactly what most bugs in a path operation live in.
23
+ - Build the `TestClient`/`AsyncClient` from a fixture shared across a test
24
+ module (or `conftest.py` once a second module needs it), not
25
+ re-instantiated per test, unless a test needs a differently configured
26
+ app instance.
27
+
28
+ ## Dependency overrides
29
+
30
+ - Override a dependency for a test with `app.dependency_overrides[real_dep]
31
+ = fake_dep`, not by monkeypatching the dependency function's module
32
+ attribute — `dependency_overrides` is FastAPI's own supported mechanism
33
+ and is what keeps the override scoped to the app instance under test.
34
+ - Clear `app.dependency_overrides` after each test that sets one (a fixture
35
+ `yield`ing the override and clearing it in teardown, or `.clear()` in the
36
+ test itself) — an override left in place leaks into unrelated tests run
37
+ afterward in the same process.
38
+ - Use a dependency override for the current-user/auth dependency to test
39
+ both an authenticated and an unauthenticated path without standing up
40
+ real credential handling in every test.
41
+
42
+ ## Request/response assertions
43
+
44
+ - Assert on the parsed response body (`response.json()`) and
45
+ `response.status_code`, not just that the call didn't raise — a path
46
+ operation that returns the wrong status code or a malformed body with no
47
+ exception is a real bug a bare "it didn't crash" assertion misses.
48
+ - For a validation-error case, assert the `422` status FastAPI returns for
49
+ a Pydantic validation failure, not a generic exception — a request that
50
+ fails Pydantic validation never reaches the path operation body at all.
51
+ - When `response_model` is declared, assert that a field the model
52
+ excludes (e.g. `hashed_password`) is actually absent from the response
53
+ body for at least one test — this is the behavior `response_model`
54
+ exists to guarantee, and a typo in the model's field list would
55
+ otherwise ship unnoticed.
56
+
57
+ ## Database and external dependencies
58
+
59
+ - Use a real test database (a throwaway SQLite file, a test-scoped
60
+ Postgres schema, or the project's own configured test-DB fixture) behind
61
+ a `get_db`-style dependency override, not a hand-rolled mock of the ORM
62
+ session, for anything beyond the simplest unit test — an ORM session
63
+ mock only checks that the code called the mock the way the mock expects.
64
+ - Mock genuinely external services (a third-party HTTP API, an email
65
+ provider) at the client boundary (`httpx`/`requests` mock, or the
66
+ project's configured HTTP mocking library), matching `python`'s own
67
+ testing rule: mock external dependencies, not internal collaborators.
68
+
69
+ ## Async tests
70
+
71
+ - Mark an async test `pytest.mark.asyncio` (or the project's configured
72
+ async plugin/mode) exactly as `python`'s testing rule requires; this
73
+ applies the same way to a test that awaits `AsyncClient` calls against a
74
+ FastAPI app.
75
+
76
+ ## Coverage expectations
77
+
78
+ - Every new or touched path operation gets at least one test for its
79
+ success path, its validation-failure path (a `422` for bad input when
80
+ the body is a Pydantic model), and, when it requires auth, both an
81
+ authenticated and an unauthenticated case.
82
+ - A dependency with nontrivial logic (parses a header, decodes a token,
83
+ applies a business rule) gets its own test independent of any path
84
+ operation that happens to use it, in addition to the path operation's
85
+ own integration-level test.
@@ -0,0 +1,157 @@
1
+ ---
2
+ name: fastapi-build-fix
3
+ description: "Use when a FastAPI app itself fails to start, import, or register a router -- app-startup failures, dependency-injection wiring errors (Depends() on the wrong callable, a missing sub-dependency), Pydantic v1/v2 model/validation errors, and a checker flagging a path-operation signature or response_model specifically, with the smallest root-cause fix. For a generic Python ModuleNotFoundError/packaging/dependency-resolver failure not specific to FastAPI's own app wiring, use python-build-fix."
4
+ triggers:
5
+ - "fix this FastAPI app startup error"
6
+ - "the FastAPI app fails to import after adding a new router"
7
+ - "this Depends() dependency is failing to resolve"
8
+ - "this FastAPI path operation's response_model is rejecting valid data"
9
+ - "the linter flags this FastAPI router file specifically"
10
+ - "this Pydantic model keeps raising an unexpected validation error"
11
+ - "fix this FastAPI router registration error causing a 404"
12
+ metadata:
13
+ origin: authored
14
+ category: build-fix
15
+ version: "1.0.0"
16
+ compatible_harnesses: "claude,codex,cursor,zed,opencode"
17
+ license: "MIT"
18
+ ---
19
+
20
+ # FastAPI build-fix
21
+
22
+ Resolve a broken FastAPI app startup, dependency-injection wiring, type-
23
+ check, or lint failure with the smallest change that fixes the actual
24
+ cause. Scoped to FastAPI's own framework layer — for a generic Python
25
+ `ModuleNotFoundError`, packaging, or dependency-resolver failure not
26
+ specific to FastAPI's own app wiring, use `python-build-fix`; for adding a
27
+ feature use `fastapi-implementation`; for writing/fixing test content (not
28
+ a collection error) use `fastapi-testing`; for reviewing without fixing use
29
+ `fastapi-code-review`.
30
+
31
+ ## Workflow
32
+
33
+ ### Step 1: Reproduce and classify the failure
34
+
35
+ Run the project's own configured commands (discover the run prefix — `uv
36
+ run`, `poetry run`, or none — from `pyproject.toml`):
37
+
38
+ ```bash
39
+ ruff check .
40
+ ruff format --check .
41
+ mypy . # or: pyright
42
+ pytest -x -q
43
+ python -c "import <app_module>" # or: uvicorn <app_module>:app --port 0
44
+ ```
45
+
46
+ Read the *first* error in each tool's output. Classify:
47
+
48
+ - **App startup/import** — `app = FastAPI(...)` module fails to import, or
49
+ `uvicorn` fails to start the app (distinct from a generic Python
50
+ `ModuleNotFoundError` unrelated to FastAPI wiring — that goes to
51
+ `python-build-fix`).
52
+ - **Router registration** — `app.include_router(...)` raises, a route
53
+ conflict (duplicate path + method), or a route silently 404s because it
54
+ was never registered.
55
+ - **Dependency-injection wiring** — `Depends(...)` resolves to the wrong
56
+ callable, a sub-dependency is missing, or FastAPI reports it cannot
57
+ determine how to resolve a dependency's own parameters.
58
+ - **Pydantic model errors** — a `BaseModel` raises at class-definition time
59
+ (bad field type, invalid `Field(...)` constraint) or at request-parse
60
+ time in a way that shouldn't be a validation error at all (a v1/v2
61
+ syntax mismatch is a common cause).
62
+ - **Type errors** — `mypy`/`pyright` reports a real mismatch on a path
63
+ operation's parameters, return type, or a `Depends(...)`-injected value.
64
+ - **Lint failures** — `ruff check` reports a rule violation.
65
+
66
+ ### Step 2: Find the root cause
67
+
68
+ - **App startup/import**: does `app = FastAPI(...)` fail because a router
69
+ module it imports itself fails to import (circular import between
70
+ `main.py` and a router, or a router importing something not yet
71
+ defined)? Read the actual traceback's innermost frame, not just the
72
+ outermost "app failed to start" message.
73
+ - **Router registration**: check `app.include_router(router, prefix=...,
74
+ tags=[...])` — a duplicated `prefix` across two routers, or a path
75
+ operation decorated on the wrong router variable, are the common causes
76
+ of a route that 404s despite the code appearing correct.
77
+ - **Dependency-injection wiring**: read FastAPI's own error message about
78
+ which parameter it couldn't resolve; check whether the dependency
79
+ function's own parameters are all themselves either request data
80
+ (query/path/body) or other `Depends(...)` — a parameter with no default
81
+ and no `Depends(...)`/request-data annotation is what breaks resolution.
82
+ - **Pydantic model errors**: confirm the pinned Pydantic version
83
+ (`pyproject.toml`) and whether the failing syntax is v1-only (`class
84
+ Config:`, `@validator`) or v2-only (`model_config = ConfigDict(...)`,
85
+ `@field_validator`) — mixing the two is the most common cause of a
86
+ confusing model-definition error.
87
+ - **Type errors**: read the exact mismatch; a `Depends(...)`-injected
88
+ parameter's annotated type must match what the dependency function
89
+ actually returns — fix whichever side is actually wrong given the
90
+ dependency's real contract.
91
+ - **Lint failures**: apply `ruff check --fix .` for mechanical fixes; for a
92
+ substantive rule, fix the flagged code.
93
+
94
+ ### Step 3: Apply the smallest fix
95
+
96
+ - Fix the actual cause identified in Step 2 — the wrong `Depends(...)`
97
+ target, the router registered on the wrong variable, the v1/v2 syntax
98
+ mismatch, the real type mismatch.
99
+ - Touch only what the failure requires; do not refactor unrelated
100
+ path operations or schemas while fixing a build failure.
101
+
102
+ ### Step 4: Verify
103
+
104
+ Re-run every command from Step 1 in order; all must exit 0, including the
105
+ app-import/startup check — a fix that satisfies `mypy`/`ruff` can still
106
+ leave the app failing to start if a router or dependency wiring mistake
107
+ remains.
108
+
109
+ ### Step 5: Report
110
+
111
+ ```
112
+ Fixed: new `/webhooks/stripe` endpoint returns 404 in every environment
113
+ Root cause: routers/webhooks.py defines `router = APIRouter()` and the
114
+ route with `@router.post("/stripe")`, but main.py still calls
115
+ `app.include_router(legacy_router)` -- a leftover router object from
116
+ an earlier refactor that was never renamed -- so the new router
117
+ object is built but never mounted onto the app at all.
118
+ Fix: changed main.py to `app.include_router(webhooks_router,
119
+ prefix="/webhooks")`, pointing at the actual router object the new
120
+ route was added to, and removed the unused `legacy_router` import.
121
+ Verified: ruff check, mypy, pytest -x -q, and app import all green
122
+ ```
123
+
124
+ ## Rules
125
+
126
+ - NEVER add `# type: ignore` or `# noqa` as a blanket suppression to make a
127
+ real error disappear without fixing or explicitly justifying it inline.
128
+ - NEVER move a blocking call into an `async def` path operation (or leave
129
+ one there) to make an error disappear — if the fix requires the function
130
+ to stay blocking, keep it `def` so FastAPI's threadpool handles it.
131
+ - NEVER widen a Pydantic field's type to `Any`/`dict` to make a validation
132
+ error stop, when the actual cause is a v1/v2 syntax mismatch or a genuine
133
+ schema bug.
134
+ - Fix the root cause with the smallest change; do not refactor beyond what
135
+ the failure requires.
136
+
137
+ ## Red Flags
138
+
139
+ | Rationalization | Why it is wrong |
140
+ |---|---|
141
+ | "This Pydantic validation error is annoying, I'll just type the field `Any`" | Hides the real schema mismatch (often a v1/v2 syntax mix) instead of fixing it; find and fix the actual cause |
142
+ | "The dependency resolution error is confusing, I'll just remove the `Depends(...)` and call the function directly" | Removes dependency injection entirely, losing `dependency_overrides` testability and any caching FastAPI provided; fix the actual parameter FastAPI couldn't resolve |
143
+ | "I'll add `# type: ignore` here, the real fix is bigger" | Hides the type hole permanently; if the real fix is out of scope, say so in the report and leave the error visible |
144
+ | "ruff flagged something in this router, I'll disable the rule in `pyproject.toml`" | Disables it project-wide for all future code, not just this failure; fix the flagged code instead |
145
+
146
+ ## Verification
147
+
148
+ Do not report the fix done until all of the following hold:
149
+
150
+ - The originally failing command now exits 0.
151
+ - `ruff check .`, `ruff format --check .`, `mypy .`/`pyright`, `pytest -x
152
+ -q`, and the app import/startup check all exit 0.
153
+ - No new `# type: ignore`/`# noqa` was added without an inline reason, and
154
+ none was added as a blanket suppression.
155
+ - `git status` shows only the files whose actual cause was diagnosed in
156
+ Step 2 — no unrelated refactor.
157
+ - The report names the root cause, not just the symptom that was fixed.