docs: document RFC 9457 dual response formats in OpenAPI schema#506
docs: document RFC 9457 dual response formats in OpenAPI schema#506
Conversation
Add ProblemDetail schema and dual content-type error responses (application/json envelope + application/problem+json bare) to the OpenAPI spec via a post-processing step in the export pipeline. Closes #494
- Fix misleading docstring return description in _build_problem_detail_schema - Extract _inject_operation_responses helper (function length + nesting) - Add comment explaining DELETE exclusion from Conflict (409) - Change test_full_app_schema_enhancement marker to @pytest.mark.integration - Add tests: DELETE skips 409, public paths skip 503, retryable examples - Update CLAUDE.md package structure and quick commands for openapi.py Pre-reviewed by 7 agents, 9 findings addressed
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds an OpenAPI post-processor that injects an RFC 9457 ProblemDetail schema and reusable dual-format error responses into generated OpenAPI output, wires the post-processor into the export script, adds an observability event constant, and includes unit and integration tests validating behavior. Changes
Sequence DiagramsequenceDiagram
participant Export as scripts/export_openapi.py
participant Litestar as Litestar App
participant Processor as inject_rfc9457_responses()
participant Schema as OpenAPI Schema
participant Client as API Client
Export->>Litestar: Generate initial OpenAPI schema_dict
Litestar-->>Export: Return schema_dict
Export->>Processor: Call inject_rfc9457_responses(schema_dict)
Processor->>Schema: Add ProblemDetail component and reusable responses
Processor->>Schema: Inject response refs into operations (by path/method/context)
Processor-->>Export: Return enhanced schema_dict
Export->>Export: Serialize enhanced schema to JSON
Export-->>Client: Serve OpenAPI spec including RFC 9457 docs
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
✨ Simplify code
📝 Coding Plan
Comment |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the OpenAPI schema generation by implementing a robust post-processing step to support RFC 9457 for structured error responses. The changes ensure that the API documentation accurately reflects the dual-format error handling capabilities, providing clearer and more consistent error details for API consumers. This improves the overall developer experience by standardizing error reporting and making the API easier to integrate with. Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a post-processing step for the OpenAPI schema to correctly document RFC 9457 error responses. A new module src/synthorg/api/openapi.py contains the logic to inject dual-format error responses (application/json and application/problem+json) into the schema, and it is called from the scripts/export_openapi.py script. The changes are well-implemented and accompanied by a comprehensive new test suite in tests/unit/api/test_openapi.py that covers various scenarios, including unit, integration, and idempotency tests. The documentation in CLAUDE.md is also updated to reflect these changes.
My review includes a couple of suggestions for the new openapi.py module to improve maintainability and performance, but overall this is a high-quality contribution.
src/synthorg/api/openapi.py
Outdated
| _ERROR_RESPONSES: Final[ | ||
| tuple[tuple[int, str, str, ErrorCode, ErrorCategory, str, bool], ...] | ||
| ] = ( | ||
| ( | ||
| 400, | ||
| "BadRequest", | ||
| "Validation error — request body or parameters are invalid.", | ||
| ErrorCode.REQUEST_VALIDATION_ERROR, | ||
| ErrorCategory.VALIDATION, | ||
| "Validation error", | ||
| False, | ||
| ), | ||
| ( | ||
| 401, | ||
| "Unauthorized", | ||
| "Authentication required — missing or invalid credentials.", | ||
| ErrorCode.UNAUTHORIZED, | ||
| ErrorCategory.AUTH, | ||
| "Authentication required", | ||
| False, | ||
| ), | ||
| ( | ||
| 403, | ||
| "Forbidden", | ||
| "Insufficient permissions for this operation.", | ||
| ErrorCode.FORBIDDEN, | ||
| ErrorCategory.AUTH, | ||
| "Forbidden", | ||
| False, | ||
| ), | ||
| ( | ||
| 404, | ||
| "NotFound", | ||
| "Requested resource does not exist.", | ||
| ErrorCode.RECORD_NOT_FOUND, | ||
| ErrorCategory.NOT_FOUND, | ||
| "Resource not found", | ||
| False, | ||
| ), | ||
| ( | ||
| 409, | ||
| "Conflict", | ||
| "Resource conflict — duplicate or invalid state transition.", | ||
| ErrorCode.RESOURCE_CONFLICT, | ||
| ErrorCategory.CONFLICT, | ||
| "Resource conflict", | ||
| False, | ||
| ), | ||
| ( | ||
| 429, | ||
| "TooManyRequests", | ||
| "Rate limit exceeded — back off and retry.", | ||
| ErrorCode.RATE_LIMITED, | ||
| ErrorCategory.RATE_LIMIT, | ||
| "Rate limit exceeded", | ||
| True, | ||
| ), | ||
| ( | ||
| 500, | ||
| "InternalError", | ||
| "Internal server error.", | ||
| ErrorCode.INTERNAL_ERROR, | ||
| ErrorCategory.INTERNAL, | ||
| "Internal server error", | ||
| False, | ||
| ), | ||
| ( | ||
| 503, | ||
| "ServiceUnavailable", | ||
| "Required service is temporarily unavailable.", | ||
| ErrorCode.SERVICE_UNAVAILABLE, | ||
| ErrorCategory.INTERNAL, | ||
| "Service unavailable", | ||
| True, | ||
| ), | ||
| ) |
There was a problem hiding this comment.
For improved readability and maintainability, consider using a dataclasses.dataclass or typing.NamedTuple for the items in _ERROR_RESPONSES instead of a raw tuple. This would make the data structure self-documenting and accessing fields less dependent on position.
For example:
from dataclasses import dataclass
@dataclass(frozen=True)
class ErrorResponseDef:
status_code: int
key: str
description: str
error_code: ErrorCode
error_category: ErrorCategory
detail: str
retryable: bool
_ERROR_RESPONSES: Final[tuple[ErrorResponseDef, ...]] = (
ErrorResponseDef(
status_code=400,
key="BadRequest",
# ...
),
# ...
)This would change the loops that iterate over _ERROR_RESPONSES to use attribute access (e.g., err.status_code) instead of tuple unpacking, which can be more robust to future changes.
src/synthorg/api/openapi.py
Outdated
| "NotFound": has_params, | ||
| # DELETE is excluded — it's idempotent and conflicts are | ||
| # a create/update concern in this API's model. | ||
| "Conflict": method in {"post", "put", "patch"}, |
There was a problem hiding this comment.
To improve performance and avoid creating a new set on each function call, you can reuse the is_write boolean that is already calculated. The current logic is equivalent to checking if the method is a write method but not 'delete', which is more explicit about the intent described in the comment on line 325.
| "Conflict": method in {"post", "put", "patch"}, | |
| "Conflict": is_write and method != "delete", |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/synthorg/api/openapi.py`:
- Around line 171-178: _build_problem_detail_schema() currently rewrites
internal refs via _rewrite_refs(raw) but pops "$defs" and never materializes
them, leaving inject_rfc9457_responses() with dangling refs; instead, after
calling ProblemDetail.model_json_schema(mode="serialization") and rewriting
refs, take the popped "$defs" (if any) and merge them into the OpenAPI
components under "components":{"schemas":{...}} (renaming keys as needed so
'#/$defs/X' -> '#/components/schemas/X'), ensuring _rewrite_refs() still points
to those component names; apply the same change to the other places where
raw.pop("$defs", None) is used (the other _build_* blocks around the similar
logic) so all rewritten refs have corresponding entries in components.schemas
rather than being discarded.
- Around line 134-153: The current assignment to _INFO_DESCRIPTION overwrites
any existing app schema description; instead preserve the existing
info.description and append the RFC 9457 section idempotently. Update the
OpenAPI generation logic (where _INFO_DESCRIPTION is used and in the related
locations around the other references at lines indicated) so it reads the
current app.openapi_schema["info"]["description"] (or the existing
info.description) and concatenates the RFC 9457 block only if it’s not already
present, leaving the original description intact and avoiding duplicate appends.
- Around line 20-29: Add structured logging to this module by importing
get_logger and instantiating logger = get_logger(__name__) at module scope
(i.e., add from synthorg.observability import get_logger and logger =
get_logger(__name__)). Then locate business-logic functions in this file that
perform schema generation/injection or raise ProblemDetail / use
ErrorCategory/ErrorCode/CATEGORY_TITLES and add logger.warning or logger.error
calls with contextual information (include the function name, relevant
identifiers, and the exception or error details) immediately before raising or
re-raising errors so all error paths log context as required.
In `@tests/unit/api/test_openapi.py`:
- Around line 511-519: The test currently asserts exact equality of
components.responses and iterates over all responses, which breaks when
unrelated reusable responses are added; change the assertions to scope to the
RFC 9457 response set by asserting that _EXPECTED_RESPONSE_KEYS is a subset of
responses.keys() (not equal) and iterate only over _EXPECTED_RESPONSE_KEYS
(e.g., for key in _EXPECTED_RESPONSE_KEYS: resp = responses[key]) when checking
that each response has both "application/json" and "application/problem+json"
content types.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 73cddc0c-0ec8-4428-af8e-672d6ce36121
📒 Files selected for processing (4)
CLAUDE.mdscripts/export_openapi.pysrc/synthorg/api/openapi.pytests/unit/api/test_openapi.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Build Sandbox
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Do not usefrom __future__ import annotationsin Python code. Python 3.14 has PEP 649 native lazy annotations.
Useexcept A, B:(no parentheses) for exception syntax on Python 3.14. Do not useexcept (A, B):.
Add type hints to all public functions and classes. Use mypy strict mode.
Use Google-style docstrings on all public classes and functions. Docstrings are enforced by ruff D rules.
Create new objects for immutability. Never mutate existing objects. For non-Pydantic internal collections, usecopy.deepcopy()at construction and wrap withMappingProxyTypefor read-only enforcement.
Use frozen Pydantic models for config and identity. Use separate mutable-via-copy models withmodel_copy(update=...)for runtime state that evolves.
Do not mix static config fields with mutable runtime fields in one Pydantic model.
Use Pydantic v2 conventions:@computed_fieldfor derived values,NotBlankStrfor identifier/name fields (including optional and tuple variants), andmodel_validator/ConfigDict.
Useasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code. Prefer structured concurrency over barecreate_task.
Keep function bodies under 50 lines and files under 800 lines.
Handle errors explicitly. Never silently swallow exceptions.
Validate user input, external API responses, and config files at system boundaries.
Useruff checkandruff formatfor Python linting and formatting (88-character line length).
Usemypywith strict mode for type-checking all Python code.
Python version must be 3.14 or higher. PEP 649 provides native lazy annotations.
Files:
tests/unit/api/test_openapi.pysrc/synthorg/api/openapi.pyscripts/export_openapi.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, and@pytest.mark.slowto mark test cases.
Prefer@pytest.mark.parametrizefor testing similar cases with multiple inputs.
Use Hypothesis for property-based testing in Python with@given+@settings. Control viaHYPOTHESIS_PROFILEenv var (dev: 1000 examples, ci: 200 examples).
Files:
tests/unit/api/test_openapi.py
src/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.py: Every module with business logic must import logger withfrom synthorg.observability import get_loggerthenlogger = get_logger(__name__). Never useimport logging,logging.getLogger(), orprint()in application code.
Always useloggeras the variable name for loggers. Never use_loggerorlog.
Use event name constants from domain-specific modules undersynthorg.observability.eventsfor all logging calls. Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Use structured logging withlogger.info(EVENT, key=value)syntax. Never use format string logging likelogger.info('msg %s', val).
All error paths must log at WARNING or ERROR level with context before raising.
All state transitions must log at INFO level.
Use DEBUG level for object creation, internal flow, and entry/exit of key functions.
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names:example-provider,example-large-001,example-medium-001,example-small-001,large/medium/smallas aliases. Usetest-provider,test-small-001, etc. in tests.
Library API reference is auto-generated from docstrings via mkdocstrings + Griffe indocs/api/. Use Google-style docstrings for public APIs.
Use Pydantic BaseModel for all data models. Frozen models for config/identity. Mutable-via-copy models for runtime state.
Files:
src/synthorg/api/openapi.py
src/synthorg/api/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/api/**/*.py: Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
API error responses use RFC 9457 structured errors: ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation.
Files:
src/synthorg/api/openapi.py
scripts/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Scripts in
scripts/have relaxed ruff rules:
Files:
scripts/export_openapi.py
🧠 Learnings (12)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/api/**/*.py : API error responses use RFC 9457 structured errors: ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation.
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Property-based testing: Python uses Hypothesis (given + settings). Hypothesis profiles: ci (200 examples, default) and dev (1000 examples), controlled via HYPOTHESIS_PROFILE env var. Run dev profile: HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to pyproject.toml : Dependencies: all versions use == in pyproject.toml. Groups: test (pytest + plugins, hypothesis), dev (includes test + ruff, mypy, pre-commit, commitizen, pip-audit). Required: mem0ai (Mem0 memory backend — the default and currently only backend). Install: uv sync installs everything (dev group is default).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-16T20:59:42.651Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.651Z
Learning: Always use `uv sync` to install dependencies. Use `uv sync --group docs` for docs toolchain.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to tests/**/*.py : Use Hypothesis for property-based testing in Python with `given` + `settings`. Control via `HYPOTHESIS_PROFILE` env var (dev: 1000 examples, ci: 200 examples).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to .github/workflows/pages.yml : Pages workflow builds OpenAPI schema (`scripts/export_openapi.py`), Astro landing page, and Zensical docs. Triggered on docs, config, and workflow changes. Deploys to GitHub Pages.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Pre-push hooks: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional on cli/**/*.go) (fast gate before push, skipped in pre-commit.ci — dedicated CI jobs already run these).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/api/**/*.py : Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
Applied to files:
CLAUDE.mdsrc/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.651Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.651Z
Learning: Use source layout: Python source in `src/synthorg/`, tests in `tests/`, web dashboard in `web/`, CLI binary in `cli/`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/api/**/*.py : API error responses use RFC 9457 structured errors: ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation.
Applied to files:
CLAUDE.mdtests/unit/api/test_openapi.pysrc/synthorg/api/openapi.pyscripts/export_openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/security/**/*.py : Security module includes SecOps agent, rule engine (soft-allow/hard-deny), audit log, output scanner, risk classifier, autonomy levels (4 strategies), timeout policies.
Applied to files:
CLAUDE.md
🧬 Code graph analysis (3)
tests/unit/api/test_openapi.py (1)
src/synthorg/api/openapi.py (1)
inject_rfc9457_responses(360-416)
src/synthorg/api/openapi.py (1)
src/synthorg/api/errors.py (1)
category_type_uri(117-126)
scripts/export_openapi.py (1)
src/synthorg/api/openapi.py (1)
inject_rfc9457_responses(360-416)
🔇 Additional comments (1)
scripts/export_openapi.py (1)
93-99: Nice placement for the schema augmentation.Running the post-processor immediately after
to_schema()keeps the RFC 9457 rewrite isolated to the exported artifact and leaves the rest of the script flow unchanged.
| import copy | ||
| from typing import Any, Final | ||
|
|
||
| from synthorg.api.dto import ProblemDetail | ||
| from synthorg.api.errors import ( | ||
| CATEGORY_TITLES, | ||
| ErrorCategory, | ||
| ErrorCode, | ||
| category_type_uri, | ||
| ) |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Add the standard logger to this new src/ module.
This transformer lives under src/ and currently has no structured logging, so schema-generation/injection failures will bubble out without the context the repo expects.
As per coding guidelines, src/**/*.py: "Every module with business logic must import logger with from synthorg.observability import get_logger then logger = get_logger(__name__)" and "All error paths must log at WARNING or ERROR level with context before raising."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/api/openapi.py` around lines 20 - 29, Add structured logging to
this module by importing get_logger and instantiating logger =
get_logger(__name__) at module scope (i.e., add from synthorg.observability
import get_logger and logger = get_logger(__name__)). Then locate business-logic
functions in this file that perform schema generation/injection or raise
ProblemDetail / use ErrorCategory/ErrorCode/CATEGORY_TITLES and add
logger.warning or logger.error calls with contextual information (include the
function name, relevant identifiers, and the exception or error details)
immediately before raising or re-raising errors so all error paths log context
as required.
| raw = ProblemDetail.model_json_schema(mode="serialization") | ||
|
|
||
| # Strip $defs — they'll be merged separately. | ||
| raw.pop("$defs", None) | ||
|
|
||
| # Rewrite $ref from '#/$defs/X' to '#/components/schemas/X'. | ||
| result: dict[str, Any] = _rewrite_refs(raw) | ||
| return result |
There was a problem hiding this comment.
Materialize the rewritten $defs instead of dropping them.
_build_problem_detail_schema() rewrites #/$defs/* into #/components/schemas/* and then discards the defs, while the reusable JSON responses always assume ApiResponse_NoneType_ already exists. On a schema without those preexisting components, inject_rfc9457_responses() returns dangling refs instead of a valid minimal spec.
Also applies to: 257-268, 384-406
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/api/openapi.py` around lines 171 - 178,
_build_problem_detail_schema() currently rewrites internal refs via
_rewrite_refs(raw) but pops "$defs" and never materializes them, leaving
inject_rfc9457_responses() with dangling refs; instead, after calling
ProblemDetail.model_json_schema(mode="serialization") and rewriting refs, take
the popped "$defs" (if any) and merge them into the OpenAPI components under
"components":{"schemas":{...}} (renaming keys as needed so '#/$defs/X' ->
'#/components/schemas/X'), ensuring _rewrite_refs() still points to those
component names; apply the same change to the other places where
raw.pop("$defs", None) is used (the other _build_* blocks around the similar
logic) so all rewritten refs have corresponding entries in components.schemas
rather than being discarded.
…ocal agents Source changes (openapi.py): - Add logger declaration (CodeRabbit + code-reviewer + conventions-enforcer) - Replace raw tuples with NamedTuple for _ERROR_RESPONSES (Gemini + code-reviewer) - Extract 4 helpers from inject_rfc9457_responses to fix 50-line limit (code-reviewer) - Preserve existing info.description and append RFC section idempotently (CodeRabbit) - Simplify Conflict check: is_write and method != "delete" (Gemini) - Expand 400-replacement comment documenting intentional behavior (CodeRabbit) - Update _rewrite_refs and _build_problem_detail_schema docstrings (code-reviewer) Test changes: - Add 13 new tests (98 total): status-matches-HTTP-code, PUT method injection, BadRequest negative case on GET, unknown key fallback, $defs absence, path-level parameters guard, description preservation - Move integration test from tests/unit/ to tests/integration/api/ (code-reviewer) - Use issubset for integration response key check (CodeRabbit)
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/synthorg/api/openapi.py`:
- Around line 37-39: The module imports a logger but doesn't use it; add
logger.debug and logger.warning calls in the OpenAPI schema processing functions
(e.g., inside functions such as transform_schema, build_components, parse_paths
or any helpers that perform schema transformation) to log entry/exit and key
steps, and use logger.warning when encountering unexpected structures (missing
schemas, unsupported types). Ensure each function logs at DEBUG on start/end and
major decisions, and logs contextual details (object names/keys) in WARNING
messages, using the existing logger variable declared as logger =
get_logger(__name__).
- Around line 376-382: The current block that replaces 400 responses (condition
using status_code and op_responses) unconditionally overwrites any 400 entry;
change it to first detect Litestar's auto-generated ValidationException schema
before replacing by inspecting op_responses["400"]'s schema/description for
Litestar's ValidationException signature (e.g., known description text or JSON
structure), and only call _response_ref(key) and assign to
op_responses[status_code] when that heuristic matches; leave existing custom 400
responses intact when the heuristic does not match.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: fdb0cc2c-cd9a-4c06-8081-ac9d41d4f3e6
📒 Files selected for processing (4)
src/synthorg/api/openapi.pytests/integration/api/__init__.pytests/integration/api/test_openapi_integration.pytests/unit/api/test_openapi.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Build Web
- GitHub Check: Build Backend
- GitHub Check: Test (Python 3.14)
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Do not usefrom __future__ import annotationsin Python code. Python 3.14 has PEP 649 native lazy annotations.
Useexcept A, B:(no parentheses) for exception syntax on Python 3.14. Do not useexcept (A, B):.
Add type hints to all public functions and classes. Use mypy strict mode.
Use Google-style docstrings on all public classes and functions. Docstrings are enforced by ruff D rules.
Create new objects for immutability. Never mutate existing objects. For non-Pydantic internal collections, usecopy.deepcopy()at construction and wrap withMappingProxyTypefor read-only enforcement.
Use frozen Pydantic models for config and identity. Use separate mutable-via-copy models withmodel_copy(update=...)for runtime state that evolves.
Do not mix static config fields with mutable runtime fields in one Pydantic model.
Use Pydantic v2 conventions:@computed_fieldfor derived values,NotBlankStrfor identifier/name fields (including optional and tuple variants), andmodel_validator/ConfigDict.
Useasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code. Prefer structured concurrency over barecreate_task.
Keep function bodies under 50 lines and files under 800 lines.
Handle errors explicitly. Never silently swallow exceptions.
Validate user input, external API responses, and config files at system boundaries.
Useruff checkandruff formatfor Python linting and formatting (88-character line length).
Usemypywith strict mode for type-checking all Python code.
Python version must be 3.14 or higher. PEP 649 provides native lazy annotations.
Files:
tests/integration/api/test_openapi_integration.pytests/unit/api/test_openapi.pysrc/synthorg/api/openapi.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, and@pytest.mark.slowto mark test cases.
Prefer@pytest.mark.parametrizefor testing similar cases with multiple inputs.
Use Hypothesis for property-based testing in Python with@given+@settings. Control viaHYPOTHESIS_PROFILEenv var (dev: 1000 examples, ci: 200 examples).
Files:
tests/integration/api/test_openapi_integration.pytests/unit/api/test_openapi.py
src/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.py: Every module with business logic must import logger withfrom synthorg.observability import get_loggerthenlogger = get_logger(__name__). Never useimport logging,logging.getLogger(), orprint()in application code.
Always useloggeras the variable name for loggers. Never use_loggerorlog.
Use event name constants from domain-specific modules undersynthorg.observability.eventsfor all logging calls. Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Use structured logging withlogger.info(EVENT, key=value)syntax. Never use format string logging likelogger.info('msg %s', val).
All error paths must log at WARNING or ERROR level with context before raising.
All state transitions must log at INFO level.
Use DEBUG level for object creation, internal flow, and entry/exit of key functions.
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names:example-provider,example-large-001,example-medium-001,example-small-001,large/medium/smallas aliases. Usetest-provider,test-small-001, etc. in tests.
Library API reference is auto-generated from docstrings via mkdocstrings + Griffe indocs/api/. Use Google-style docstrings for public APIs.
Use Pydantic BaseModel for all data models. Frozen models for config/identity. Mutable-via-copy models for runtime state.
Files:
src/synthorg/api/openapi.py
src/synthorg/api/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/api/**/*.py: Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
API error responses use RFC 9457 structured errors: ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation.
Files:
src/synthorg/api/openapi.py
🧠 Learnings (13)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/api/**/*.py : API error responses use RFC 9457 structured errors: ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation.
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/api/**/*.py : API error responses use RFC 9457 structured errors: ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/api/**/*.py : Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger` (not `_logger`, not `log`).
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/**/*.py : Every module with business logic must import logger with `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging`, `logging.getLogger()`, or `print()` in application code.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability includes structured logging via `get_logger(__name__)`, correlation tracking, and log sinks.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to src/synthorg/**/*.py : All state transitions must log at INFO.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to src/synthorg/**/*.py : DEBUG logging for object creation, internal flow, entry/exit of key functions.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` for all logging calls. Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/**/*.py : All error paths must log at WARNING or ERROR level with context before raising.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/**/*.py : Always use `logger` as the variable name for loggers. Never use `_logger` or `log`.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to src/synthorg/**/*.py : Structured kwargs in logging: always `logger.info(EVENT, key=value)` — never `logger.info('msg %s', val)`.
Applied to files:
src/synthorg/api/openapi.py
🧬 Code graph analysis (2)
tests/integration/api/test_openapi_integration.py (2)
src/synthorg/api/openapi.py (1)
inject_rfc9457_responses(418-451)src/synthorg/api/app.py (1)
create_app(475-635)
tests/unit/api/test_openapi.py (1)
src/synthorg/api/openapi.py (2)
_should_inject(326-354)inject_rfc9457_responses(418-451)
🔇 Additional comments (14)
src/synthorg/api/openapi.py (6)
41-154: LGTM!Constants are well-structured using immutable types (
Final,tuple,frozenset,NamedTuple). The error response specifications are comprehensive and cover the expected RFC 9457 error scenarios.
178-206: LGTM!The
_build_problem_detail_schemafunction correctly handles Pydantic schema generation with appropriate$refrewriting. The docstring properly documents the assumption that referenced schemas (ErrorCode,ErrorCategory) must already exist incomponents.schemas.
208-227: LGTM!The
_rewrite_refsfunction is correctly implemented as a recursive, idempotent transformation. It only rewrites#/$defs/-prefixed refs, leaving already-rewritten refs unchanged.
229-278: LGTM!Example builders correctly construct both envelope and bare RFC 9457 examples with all required fields. The use of
CATEGORY_TITLESandcategory_type_uriensures consistency with the error system.
407-412: LGTM!The
_update_info_descriptionfunction correctly implements idempotent append by checking for the presence of the RFC 9457 section header before appending. This preserves any existing description while avoiding duplicate appends.
418-451: LGTM!The
inject_rfc9457_responsesfunction is well-designed:
- Deep-copies input to ensure immutability
- Uses
setdefaultfor defensive handling of missing sections- Orchestrates all transformation steps clearly
- Returns the enhanced copy
tests/unit/api/test_openapi.py (6)
1-13: LGTM!Module docstring clearly describes the test scope. Imports are minimal and appropriate, including the private
_should_injectfor direct unit testing of injection logic.
39-91: LGTM!The
_minimal_schemahelper andbase_schemafixture are well-designed. The base schema includes representative paths covering:
- Public endpoints (
/health,/auth/login,/auth/setup)- Authenticated endpoints with various methods (
/tasks,/agents)- Parameterized paths (
/tasks/{task_id},/agents/{agent_name})This provides comprehensive coverage for injection rule testing.
97-166: LGTM!
TestProblemDetailSchemaprovides thorough coverage of the ProblemDetail schema generation including:
- Schema addition to components
- Required fields validation
- Property completeness
$refrewriting to#/components/schemas/- Preservation of pre-existing custom schemas
- Absence of leftover
$defs
195-295: LGTM!
TestReusableResponsesuses@pytest.mark.parametrizeeffectively to test all eight RFC 9457 responses. Coverage includes:
- Dual content types (
application/jsonandapplication/problem+json)- Schema references to envelope and ProblemDetail
- Example presence and structure
- HTTP status code consistency in ProblemDetail examples
300-536: LGTM!
TestOperationInjectionprovides exhaustive coverage of the injection rules:
- Universal 500 injection
- Authentication responses (401/403) for non-public endpoints
- 404 for parameterized paths
- Conflict (409) for write methods (POST, PUT, PATCH but not DELETE)
- Rate limiting (429) and service unavailability (503) rules
- Preservation of custom responses vs. replacement of Litestar defaults
- Handling of non-operation path entries (e.g.,
parameters)- Defensive fallback for unknown response keys
542-603: LGTM!
TestInfoDescriptionandTestIdempotencyAndImmutabilitycomplete the test coverage with:
- RFC 9457 documentation verification
- Content negotiation and error reference checks
- Preservation of existing description text
- Idempotency guarantee (same result on repeated calls)
- Immutability guarantee (input unchanged)
- Edge case handling (empty paths, missing components)
tests/integration/api/test_openapi_integration.py (2)
1-24: LGTM!Module docstring and imports are appropriate. The
_EXPECTED_RESPONSE_KEYSconstant duplicates the one from the unit tests, which is acceptable for test isolation.
27-63: LGTM!The integration test properly exercises the end-to-end flow:
- Uses
@pytest.mark.integrationmarker- Lazy imports
create_appto avoid import-time issues- Uses subset check for response keys (future-proof against additional responses)
- Validates ProblemDetail schema, dual content types, operation injection, and public endpoint exclusion rules
The
create_app()call without dependencies is intentional for testing and will trigger an expected warning log without failing.
| from synthorg.observability import get_logger | ||
|
|
||
| logger = get_logger(__name__) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Logger imported but never used.
The logger is correctly imported per coding guidelines, but there are no logging calls in this module. Consider adding DEBUG-level logging for schema transformation steps or WARNING-level logging if transformation encounters unexpected structures (e.g., missing expected schemas).
💡 Example logging addition
def _add_problem_detail_schema(schemas: dict[str, Any]) -> None:
"""Add ``ProblemDetail`` to ``components.schemas`` if absent."""
if "ProblemDetail" not in schemas:
schemas["ProblemDetail"] = _build_problem_detail_schema()
+ logger.debug("openapi_schema_enhanced", added_schema="ProblemDetail")
+ else:
+ logger.debug("openapi_schema_skipped", reason="ProblemDetail already exists")As per coding guidelines, src/**/*.py: "DEBUG logging for object creation, internal flow, entry/exit of key functions."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/api/openapi.py` around lines 37 - 39, The module imports a
logger but doesn't use it; add logger.debug and logger.warning calls in the
OpenAPI schema processing functions (e.g., inside functions such as
transform_schema, build_components, parse_paths or any helpers that perform
schema transformation) to log entry/exit and key steps, and use logger.warning
when encountering unexpected structures (missing schemas, unsupported types).
Ensure each function logs at DEBUG on start/end and major decisions, and logs
contextual details (object names/keys) in WARNING messages, using the existing
logger variable declared as logger = get_logger(__name__).
Add API_OPENAPI_SCHEMA_ENHANCED event constant and a debug log call in inject_rfc9457_responses to satisfy the logger-imported-but-unused finding from CodeRabbit's second review pass.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #506 +/- ##
==========================================
+ Coverage 93.06% 93.08% +0.02%
==========================================
Files 501 502 +1
Lines 24098 24202 +104
Branches 2298 2313 +15
==========================================
+ Hits 22426 22529 +103
Misses 1327 1327
- Partials 345 346 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…sponses - Add _is_litestar_validation_400() heuristic that checks for the "Validation Exception" schema description — custom 400 responses are now preserved instead of unconditionally replaced - Add debug logging to _add_problem_detail_schema for schema transformation visibility - Add test_custom_400_preserved to verify custom 400s survive injection
…estar_validation_400
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/synthorg/api/openapi.py`:
- Around line 54-57: The name _WRITE_METHODS is ambiguous because DELETE is
included but treated specially elsewhere (DELETE is excluded from 409 injection
by the existing 409-handling logic); either rename _WRITE_METHODS to
_BODY_METHODS to reflect that it represents HTTP methods that accept a request
body, or add a concise clarifying comment above the frozenset explaining:
"Includes methods that may carry a request body; DELETE is included for 400/403
handling but is intentionally excluded from 409 injection by the 409-handling
logic." Update all references to _WRITE_METHODS accordingly (or keep the symbol
name and only add the comment) so the intent matches the separate
DELETE-special-case code path.
In `@tests/unit/api/test_openapi.py`:
- Around line 509-521: The test test_non_retryable_example_has_retryable_false
omits the InternalError response which should also assert retryable is False;
update the pytest.mark.parametrize "key" list in tests/unit/api/test_openapi.py
to include "InternalError" so the loop covers that case (the behavior is defined
by inject_rfc9457_responses / the responses mapping that marks InternalError as
retryable=False).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: b2695e65-c29f-40c4-86fc-f422f147c55a
📒 Files selected for processing (2)
src/synthorg/api/openapi.pytests/unit/api/test_openapi.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Build Sandbox
- GitHub Check: Build Web
- GitHub Check: Build Backend
- GitHub Check: Test (Python 3.14)
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Do not usefrom __future__ import annotationsin Python code. Python 3.14 has PEP 649 native lazy annotations.
Useexcept A, B:(no parentheses) for exception syntax on Python 3.14. Do not useexcept (A, B):.
Add type hints to all public functions and classes. Use mypy strict mode.
Use Google-style docstrings on all public classes and functions. Docstrings are enforced by ruff D rules.
Create new objects for immutability. Never mutate existing objects. For non-Pydantic internal collections, usecopy.deepcopy()at construction and wrap withMappingProxyTypefor read-only enforcement.
Use frozen Pydantic models for config and identity. Use separate mutable-via-copy models withmodel_copy(update=...)for runtime state that evolves.
Do not mix static config fields with mutable runtime fields in one Pydantic model.
Use Pydantic v2 conventions:@computed_fieldfor derived values,NotBlankStrfor identifier/name fields (including optional and tuple variants), andmodel_validator/ConfigDict.
Useasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code. Prefer structured concurrency over barecreate_task.
Keep function bodies under 50 lines and files under 800 lines.
Handle errors explicitly. Never silently swallow exceptions.
Validate user input, external API responses, and config files at system boundaries.
Useruff checkandruff formatfor Python linting and formatting (88-character line length).
Usemypywith strict mode for type-checking all Python code.
Python version must be 3.14 or higher. PEP 649 provides native lazy annotations.
Files:
tests/unit/api/test_openapi.pysrc/synthorg/api/openapi.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, and@pytest.mark.slowto mark test cases.
Prefer@pytest.mark.parametrizefor testing similar cases with multiple inputs.
Use Hypothesis for property-based testing in Python with@given+@settings. Control viaHYPOTHESIS_PROFILEenv var (dev: 1000 examples, ci: 200 examples).
Files:
tests/unit/api/test_openapi.py
src/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.py: Every module with business logic must import logger withfrom synthorg.observability import get_loggerthenlogger = get_logger(__name__). Never useimport logging,logging.getLogger(), orprint()in application code.
Always useloggeras the variable name for loggers. Never use_loggerorlog.
Use event name constants from domain-specific modules undersynthorg.observability.eventsfor all logging calls. Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Use structured logging withlogger.info(EVENT, key=value)syntax. Never use format string logging likelogger.info('msg %s', val).
All error paths must log at WARNING or ERROR level with context before raising.
All state transitions must log at INFO level.
Use DEBUG level for object creation, internal flow, and entry/exit of key functions.
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names:example-provider,example-large-001,example-medium-001,example-small-001,large/medium/smallas aliases. Usetest-provider,test-small-001, etc. in tests.
Library API reference is auto-generated from docstrings via mkdocstrings + Griffe indocs/api/. Use Google-style docstrings for public APIs.
Use Pydantic BaseModel for all data models. Frozen models for config/identity. Mutable-via-copy models for runtime state.
Files:
src/synthorg/api/openapi.py
src/synthorg/api/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/api/**/*.py: Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
API error responses use RFC 9457 structured errors: ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation.
Files:
src/synthorg/api/openapi.py
🧠 Learnings (14)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/api/**/*.py : API error responses use RFC 9457 structured errors: ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation.
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/api/**/*.py : API error responses use RFC 9457 structured errors: ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/api/**/*.py : Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger` (not `_logger`, not `log`).
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/**/*.py : Every module with business logic must import logger with `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging`, `logging.getLogger()`, or `print()` in application code.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability includes structured logging via `get_logger(__name__)`, correlation tracking, and log sinks.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to src/synthorg/**/*.py : All state transitions must log at INFO.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to src/synthorg/**/*.py : DEBUG logging for object creation, internal flow, entry/exit of key functions.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` for all logging calls. Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/**/*.py : All error paths must log at WARNING or ERROR level with context before raising.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/**/*.py : Always use `logger` as the variable name for loggers. Never use `_logger` or `log`.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to src/synthorg/**/*.py : Structured kwargs in logging: always `logger.info(EVENT, key=value)` — never `logger.info('msg %s', val)`.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Fix everything valid — never skip when review agents find valid issues (including pre-existing issues in surrounding code, suggestions, and findings adjacent to the PR's changes). No deferring, no 'out of scope' skipping.
Applied to files:
src/synthorg/api/openapi.py
🧬 Code graph analysis (2)
tests/unit/api/test_openapi.py (1)
src/synthorg/api/openapi.py (2)
_should_inject(327-355)inject_rfc9457_responses(448-488)
src/synthorg/api/openapi.py (1)
src/synthorg/api/errors.py (1)
category_type_uri(117-126)
🔇 Additional comments (12)
src/synthorg/api/openapi.py (6)
1-40: LGTM! Module structure and imports are well-organized.The module correctly:
- Uses the required
get_logger(__name__)pattern with structured event constants- Imports RFC 9457 error types (
ErrorCategory,ErrorCode,ProblemDetail, etc.)- Has comprehensive Google-style docstrings explaining the module's purpose
179-206: LGTM! Schema building with clear documentation of assumptions.The function correctly documents the assumption that
ErrorCodeandErrorCategoryalready exist in Litestar's generatedcomponents.schemas. The$defsstripping approach is sound given this documented precondition.
230-306: LGTM! Example generation and reusable response building.The helper functions correctly:
- Build examples with all RFC 9457 required fields
- Use the imported
CATEGORY_TITLESandcategory_type_urifor consistency- Create dual content-type responses with proper
$refstructures
406-421: LGTM! Extracted steps with appropriate logging.The
_add_problem_detail_schemafunction demonstrates proper structured logging with:
- Event constant
API_OPENAPI_SCHEMA_ENHANCED- Meaningful context keys (
step,added,reason)- DEBUG level for internal flow as per coding guidelines
448-487: LGTM! Main function is well-designed.The
inject_rfc9457_responsesfunction:
- Uses
copy.deepcopyto ensure immutability of input- Gracefully handles missing sections with
setdefault- Has comprehensive docstring explaining inputs/outputs
- Logs summary metrics for observability
358-369: The concern about custom 400 responses being incorrectly replaced is inaccurate.The detection logic is actually defensive: if the description doesn't match
"Validation Exception",_is_litestar_validation_400()returnsFalse, and the existing 400 response is preserved. Custom responses are already protected by this fallback behavior, which is validated by thetest_custom_400_preservedtest.The actual version-dependent risk is narrower: if Litestar changes the description string in a future version, Litestar's auto-generated 400s may not be replaced (they'll simply pass through unchanged). Since Litestar 2.21.1 is pinned in
pyproject.tomland the behavior is covered by integration tests, this is acceptable. If Litestar is upgraded, this assumption can be re-validated at that time.tests/unit/api/test_openapi.py (6)
1-16: LGTM! Test module structure and imports.The module:
- Has comprehensive docstring explaining purpose
- Uses
@pytest.mark.unitas required by coding guidelines- Imports both the public function and a private helper for targeted testing
18-91: LGTM! Well-designed fixtures for comprehensive testing.The fixtures provide good coverage of:
- Public endpoints (
/health,/auth/login,/auth/setup)- Authenticated endpoints (
/tasks,/agents)- Parameterized endpoints (
/tasks/{task_id},/agents/{agent_name})- Various HTTP methods (GET, POST, PATCH, DELETE)
97-166: LGTM! Comprehensive ProblemDetail schema tests.The test class covers all critical aspects:
- Schema addition and required fields
- Property completeness
- Reference reuse for existing schemas
- Idempotent behavior with pre-existing schemas
- Clean output (no leftover
$defs)
195-294: LGTM! Excellent use of parametrized tests.The test class effectively uses
@pytest.mark.parametrizeto:
- Test all 8 error response types without code duplication
- Verify dual content types (
application/jsonandapplication/problem+json)- Validate schema references and example structures
- Check status code consistency between HTTP status and ProblemDetail
statusfield
579-607: LGTM! Info description tests.The tests verify:
- RFC 9457 documentation is appended
- Both content types are mentioned
- Error reference link is included
- Existing descriptions are preserved (not overwritten)
612-640: LGTM! Idempotency and immutability tests.The tests verify the critical contract:
- Function is idempotent (calling twice yields identical results)
- Input schema is not mutated
- Graceful handling of edge cases (empty paths, missing components)
…-retryable test - Update _WRITE_METHODS comment to explain DELETE inclusion for 400/403 and its intentional exclusion from 409 injection - Add InternalError to test_non_retryable_example_has_retryable_false parametrize list (retryable=False was untested for this response)
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/synthorg/api/openapi.py (1)
180-207:⚠️ Potential issue | 🟠 MajorPreserve rewritten
$defsto avoid dangling OpenAPI references.On Line 203,
$defsis dropped after rewriting refs, but_add_problem_detail_schema()only addsProblemDetail. When the input schema does not already containErrorCode/ErrorCategory, this produces unresolved#/components/schemas/*references.♻️ Proposed fix
-def _build_problem_detail_schema() -> dict[str, Any]: +def _build_problem_detail_schema() -> tuple[dict[str, Any], dict[str, Any]]: @@ - # Strip $defs — referenced types already exist in components.schemas. - raw.pop("$defs", None) + defs = raw.pop("$defs", {}) @@ - result: dict[str, Any] = _rewrite_refs(raw) - return result + result: dict[str, Any] = _rewrite_refs(raw) + rewritten_defs: dict[str, Any] = { + name: _rewrite_refs(schema_def) + for name, schema_def in defs.items() + } + return result, rewritten_defs @@ def _add_problem_detail_schema(schemas: dict[str, Any]) -> None: @@ if "ProblemDetail" not in schemas: - schemas["ProblemDetail"] = _build_problem_detail_schema() + problem_detail_schema, referenced_defs = _build_problem_detail_schema() + for name, schema_def in referenced_defs.items(): + schemas.setdefault(name, schema_def) + schemas["ProblemDetail"] = problem_detail_schemaAlso applies to: 407-410
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/openapi.py` around lines 180 - 207, The code currently drops raw "$defs" in _build_problem_detail_schema(), causing dangling refs when referenced types (e.g., ErrorCode/ErrorCategory) aren't already in components.schemas; instead of popping and discarding "$defs", capture it, run _rewrite_refs(raw) to rewrite internal "$ref" targets, then merge the captured "$defs" entries into the OpenAPI components.schemas namespace (e.g., result.setdefault("components", {}).setdefault("schemas", {})[name] = def_schema for each name, schema in defs.items()) so the rewritten refs resolve; apply the same change pattern where "$defs" is stripped in _add_problem_detail_schema() to ensure those definitions are preserved and available under components.schemas.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/synthorg/api/openapi.py`:
- Around line 180-207: The code currently drops raw "$defs" in
_build_problem_detail_schema(), causing dangling refs when referenced types
(e.g., ErrorCode/ErrorCategory) aren't already in components.schemas; instead of
popping and discarding "$defs", capture it, run _rewrite_refs(raw) to rewrite
internal "$ref" targets, then merge the captured "$defs" entries into the
OpenAPI components.schemas namespace (e.g., result.setdefault("components",
{}).setdefault("schemas", {})[name] = def_schema for each name, schema in
defs.items()) so the rewritten refs resolve; apply the same change pattern where
"$defs" is stripped in _add_problem_detail_schema() to ensure those definitions
are preserved and available under components.schemas.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 1e9bdfb8-f055-40dc-baa3-d8ddedc18611
📒 Files selected for processing (2)
src/synthorg/api/openapi.pytests/unit/api/test_openapi.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Build Web
- GitHub Check: Build Backend
- GitHub Check: Build Sandbox
- GitHub Check: Test (Python 3.14)
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Do not usefrom __future__ import annotationsin Python code. Python 3.14 has PEP 649 native lazy annotations.
Useexcept A, B:(no parentheses) for exception syntax on Python 3.14. Do not useexcept (A, B):.
Add type hints to all public functions and classes. Use mypy strict mode.
Use Google-style docstrings on all public classes and functions. Docstrings are enforced by ruff D rules.
Create new objects for immutability. Never mutate existing objects. For non-Pydantic internal collections, usecopy.deepcopy()at construction and wrap withMappingProxyTypefor read-only enforcement.
Use frozen Pydantic models for config and identity. Use separate mutable-via-copy models withmodel_copy(update=...)for runtime state that evolves.
Do not mix static config fields with mutable runtime fields in one Pydantic model.
Use Pydantic v2 conventions:@computed_fieldfor derived values,NotBlankStrfor identifier/name fields (including optional and tuple variants), andmodel_validator/ConfigDict.
Useasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code. Prefer structured concurrency over barecreate_task.
Keep function bodies under 50 lines and files under 800 lines.
Handle errors explicitly. Never silently swallow exceptions.
Validate user input, external API responses, and config files at system boundaries.
Useruff checkandruff formatfor Python linting and formatting (88-character line length).
Usemypywith strict mode for type-checking all Python code.
Python version must be 3.14 or higher. PEP 649 provides native lazy annotations.
Files:
src/synthorg/api/openapi.pytests/unit/api/test_openapi.py
src/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.py: Every module with business logic must import logger withfrom synthorg.observability import get_loggerthenlogger = get_logger(__name__). Never useimport logging,logging.getLogger(), orprint()in application code.
Always useloggeras the variable name for loggers. Never use_loggerorlog.
Use event name constants from domain-specific modules undersynthorg.observability.eventsfor all logging calls. Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Use structured logging withlogger.info(EVENT, key=value)syntax. Never use format string logging likelogger.info('msg %s', val).
All error paths must log at WARNING or ERROR level with context before raising.
All state transitions must log at INFO level.
Use DEBUG level for object creation, internal flow, and entry/exit of key functions.
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names:example-provider,example-large-001,example-medium-001,example-small-001,large/medium/smallas aliases. Usetest-provider,test-small-001, etc. in tests.
Library API reference is auto-generated from docstrings via mkdocstrings + Griffe indocs/api/. Use Google-style docstrings for public APIs.
Use Pydantic BaseModel for all data models. Frozen models for config/identity. Mutable-via-copy models for runtime state.
Files:
src/synthorg/api/openapi.py
src/synthorg/api/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/api/**/*.py: Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
API error responses use RFC 9457 structured errors: ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation.
Files:
src/synthorg/api/openapi.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, and@pytest.mark.slowto mark test cases.
Prefer@pytest.mark.parametrizefor testing similar cases with multiple inputs.
Use Hypothesis for property-based testing in Python with@given+@settings. Control viaHYPOTHESIS_PROFILEenv var (dev: 1000 examples, ci: 200 examples).
Files:
tests/unit/api/test_openapi.py
🧠 Learnings (15)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/api/**/*.py : API error responses use RFC 9457 structured errors: ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation.
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/api/**/*.py : API error responses use RFC 9457 structured errors: ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/api/**/*.py : Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger` (not `_logger`, not `log`).
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/**/*.py : Every module with business logic must import logger with `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging`, `logging.getLogger()`, or `print()` in application code.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability includes structured logging via `get_logger(__name__)`, correlation tracking, and log sinks.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to src/synthorg/**/*.py : All state transitions must log at INFO.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to src/synthorg/**/*.py : DEBUG logging for object creation, internal flow, entry/exit of key functions.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` for all logging calls. Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/**/*.py : All error paths must log at WARNING or ERROR level with context before raising.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/**/*.py : Always use `logger` as the variable name for loggers. Never use `_logger` or `log`.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to src/synthorg/**/*.py : Structured kwargs in logging: always `logger.info(EVENT, key=value)` — never `logger.info('msg %s', val)`.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Fix everything valid — never skip when review agents find valid issues (including pre-existing issues in surrounding code, suggestions, and findings adjacent to the PR's changes). No deferring, no 'out of scope' skipping.
Applied to files:
src/synthorg/api/openapi.py
📚 Learning: 2026-03-16T20:59:42.650Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:59:42.650Z
Learning: Applies to src/synthorg/providers/**/*.py : Mark errors as retryable (`is_retryable=True`) for: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. All other errors are non-retryable and raise immediately.
Applied to files:
tests/unit/api/test_openapi.py
🧬 Code graph analysis (2)
src/synthorg/api/openapi.py (1)
src/synthorg/api/errors.py (1)
category_type_uri(117-126)
tests/unit/api/test_openapi.py (1)
src/synthorg/api/openapi.py (2)
_should_inject(328-356)inject_rfc9457_responses(449-489)
🤖 I have created a release *beep* *boop* --- ## [0.3.1](v0.3.0...v0.3.1) (2026-03-17) ### Features * **api:** RFC 9457 Phase 2 — ProblemDetail and content negotiation ([#496](#496)) ([30f7c49](30f7c49)) * **cli:** verify container image signatures and SLSA provenance on pull ([#492](#492)) ([bef272d](bef272d)), closes [#491](#491) * **engine:** implement context budget management in execution loops ([#520](#520)) ([181eb8a](181eb8a)), closes [#416](#416) * implement settings persistence layer (DB-backed config) ([#495](#495)) ([4bd99f7](4bd99f7)), closes [#450](#450) * **memory:** implement dual-mode archival in memory consolidation ([#524](#524)) ([4603c9e](4603c9e)), closes [#418](#418) * migrate config consumers to read through SettingsService ([#510](#510)) ([32f553d](32f553d)) * **settings:** implement settings change subscriptions for service hot-reload ([#526](#526)) ([53f908e](53f908e)), closes [#503](#503) * **settings:** register API config in SettingsService with 2-phase init ([#518](#518)) ([29f7481](29f7481)) * **tools:** add SSRF prevention for git clone URLs ([#505](#505)) ([492dd0d](492dd0d)) * **tools:** wire RootConfig.git_clone to GitCloneTool instantiation ([#519](#519)) ([b7d8172](b7d8172)) ### Bug Fixes * **api:** replace JWT query parameter with one-time ticket for WebSocket auth ([#493](#493)) ([22a25f6](22a25f6)), closes [#343](#343) ### Documentation * add uv cache lock contention handling to worktree skill ([#500](#500)) ([bd85a8d](bd85a8d)) * document RFC 9457 dual response formats in OpenAPI schema ([#506](#506)) ([8dd2524](8dd2524)) ### Maintenance * upgrade jsdom from 28 to 29 ([#499](#499)) ([1ea2249](1ea2249)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary
ProblemDetailschema to OpenAPIcomponents.schemas(generated from the Pydantic model with$refrewriting)components.responses, each with dual content types (application/jsonenvelope +application/problem+jsonbare RFC 9457)ValidationException) with the actualApiResponseenvelopeinfo.descriptionCloses #494
Test plan
scripts/export_openapi.pygenerates schema with ProblemDetail and dual content typesReview coverage
7 agents ran in parallel: code-reviewer, python-reviewer, docs-consistency, conventions-enforcer, issue-resolution-verifier, logging-audit, test-quality-reviewer. All 3 acceptance criteria verified as RESOLVED (confidence 95-98).