# Changelog & Release Notes (/docs/changelog)
This page documents all notable architectural evolutions, breaking changes, security patches, and migration paths across ZCore releases.
**Upgrading ZCore:**
To fetch the latest pre-release build, run:
```bash
pip install --upgrade "fastapi-zcore-framework[all]==0.1.0rc2"
```
***
Welcome to **v0.1.0-rc.2 (Release Candidate 2)** of the ZCore Framework! π
As we approach the final validation phase before tagging our official **v1.0.0 Stable GA**, `v0.1.0-rc.2` focuses on framework-wide error normalization, operational robustness, and developer ergonomic refinements.
This release introduces an automated, normalized exception handling system conforming all HTTP errors to the `ResponseWrapper` envelope; implements an end-to-end soft-delete, restoration, and forced hard-delete lifecycle; scaffolds a high-performance, schema-projected `/lookup` endpoint; expands the dynamic AST search engine with negated operators and logical `NOT` grouping; establishes re-entrant, depth-aware Unit of Work coordination; generalizes user identifiers (`int`, `str`, `UUID`) across all subsystems; and introduces schema-level private field shielding for unauthenticated guests.
### π Core Features & Architectural Advancements [#-core-features--architectural-advancements]
#### 1. π‘οΈ Unified Exception Handling & Error Normalization (`ResponseWrapper`) [#1-οΈ-unified-exception-handling--error-normalization-responsewrapper]
All HTTP exceptions, validation failures, and internal server errors now strictly conform to the `ResponseWrapper` JSON envelope:
* **Pydantic Error Sanitization (`_sanitize_error_item`, `_format_error_location`):** Converts raw Pydantic error `loc` tuples into readable dot-separated paths (e.g., `body.email`, `query.limit`) and safely extracts validation context.
* **Standardized Handlers:**
* `request_validation_exception_handler`: Formats 422 errors with clean field-level error lists and a human-readable summary.
* `http_exception_handler`: Preserves original response headers while wrapping Starlette/FastAPI `HTTPException` instances.
* `response_validation_exception_handler`: Intercepts internal schema mismatch errors (500) and displays field diagnostics only when `settings.DEBUG` is enabled.
* `unhandled_exception_handler`: Catch-all fallback for uncaught runtime crashes with debug-gated stack traces.
* **Centralized Registration (`register_exception_handlers`):** Attach all handlers with a single call to `register_exception_handlers(app)` with selective inclusion flags.
* **Lazy Module Packaging:** Handlers are exported at the root package level via dynamic `__getattr__` resolution to prevent circular import overhead.
#### 2. ποΈ End-to-End Soft-Delete Lifecycle & Restoration (`restore`, `force`) [#2-οΈ-end-to-end-soft-delete-lifecycle--restoration-restore-force]
Soft deletion is now seamlessly integrated across the Repository, Service, and Web layers:
* **Atomic Batch Soft-Delete:** `delete_multi()` executes atomic `UPDATE ... SET deleted_at=now()` with SQL dialect-aware `RETURNING` support and pre-fetch fallbacks.
* **Single & Batch Restoration:** Added `restore(target)` and `restore_multi(ids)` to `WriteRepositoryMixin` and `WriteServiceMixin` to recover soft-deleted records (`deleted_at=None`).
* **Forced Physical Hard Deletes:** Added `force: bool = False` across repository, service execution orchestrators, and lifecycle hooks (`pre_delete`, `post_delete`, `pre_delete_multi`, `post_delete_multi`).
* **Web Router Query Flag:** Auto-scaffolded DELETE endpoints accept `?force=true` (e.g., `DELETE /tasks/{id}?force=true`) to permanently purge records when requested.
#### 3. π Lightweight `LOOKUP` Endpoint & Schema-Driven Projections (`POST /lookup`) [#3--lightweight-lookup-endpoint--schema-driven-projections-post-lookup]
Designed for autocomplete selectors, dropdown lists, and relational references:
* **Automatic Schema Projection (`_resolve_lookup_projections`):** Introspects `lookup_schema` against SQLAlchemy model metadata to selectively generate `load_only(*columns)` statements, always including primary keys.
* **Automatic Eager-Loader Configuration:** Detects relationships declared in `lookup_schema` and attaches optimal loader strategies (`selectinload` for collections, `joinedload` for scalar relations).
* **Strict Whitelist Protection (`allowed_lookup_fields`):** Client queries attempting to filter or sort on unauthorized fields are immediately rejected with a 400 `ValidationError`.
* **RBAC Integration:** Mapped to `Actions.LOOKUP` (`model.actions().LOOKUP`) for granular permission checks.
#### 4. β‘ Search Engine Negation & Logical `NOT` Grouping (`SearchEngine`) [#4--search-engine-negation--logical-not-grouping-searchengine]
The dynamic JSON search engine now supports inverted filter operations:
* **Inverted Operators:** Added `not_like`, `not_ilike`, `not_contains`, `not_startswith`, `not_endswith`, `not_in`, `not_between`, and `is_not_null`.
* **SQL Expression Wrapping:** `_compare_column` detects `not_` prefixes and wraps clauses in SQLAlchemy's `not_()` expression.
* **Logical `NOT` Blocks:** Evaluates nested filter groups under `op="not"` as `not_(and_(*sub_exprs))` or single-field inversions.
* **Null Semantics:** `is_null` and `is_not_null` now accept boolean flags (`True`/`False`) or `None` values consistently.
#### 5. π Re-entrant Unit of Work & Deferred Domain Events (`UnitOfWork`) [#5--re-entrant-unit-of-work--deferred-domain-events-unitofwork]
Coordinates multi-service business transactions across complex modular monolith architectures:
* **Depth-Aware Re-entrancy (`uow_depth`):** Tracks execution nesting depth in `session.info['uow_depth']`. Nested `UnitOfWork` blocks execute safe intermediate `session.flush()` calls, delegating the physical `session.commit()` solely to the outermost root boundary.
* **Buffered Post-Commit Events:** Domain events registered via `uow.register_event()` across nested scopes accumulate in `session.info['uow_events']` and dispatch atomically **only after the root transaction commits**.
* **Global Rollback Safeguard:** Any unhandled exception resets depth to `0`, clears pending events, and executes an immediate session rollback.
#### 6. π Polymorphic User Identifiers (`int`, `str`, `UUID`) [#6--polymorphic-user-identifiers-int-str-uuid]
Removed strict `uuid.UUID` type enforcement from core identity layers:
* **Polymorphic `ZContext` & `UserProtocol`:** `ZContext.user_id` and `UserProtocol.id` now accept `Any | None` (supporting integer IDs, UUIDs, Auth0/Firebase string IDs, or custom tenant keys).
* **Real-time Streaming (`StreamManager`):** Subscriber mapping queues (`users_queues`) now accept arbitrary user ID types with mixed-type string comparison routing.
* **Cursor Pagination PK Inspection:** Keyset cursor pagination dynamically inspects column types (`mapper.primary_key[0].type.python_type`), coercing cursor payload keys to `int` or `UUID` automatically.
#### 7. π Optional Authentication & Schema-Level Private Fields (`Zchema.__private__`) [#7--optional-authentication--schema-level-private-fields-zchema__private__]
Enables clean guest-friendly routes and sensitive field filtering:
* **Optional Authentication (`BaseAuth(auto_error=False)`):** Allows endpoints to accept optional authentication without raising `401 Unauthorized` on missing or invalid tokens.
* **Dependency Anchor (`get_optional_user_stub`):** Exported across `zcore` and `zcore.security` for optional route parameters.
* **Private Field Pruning (`__private__`):** Schemas can declare sensitive fields via `__private__: ClassVar[set[str]] = {"cost_price", "notes"}`. ZCore automatically prunes these fields from response payloads, input validations, and generated JSON schemas (`?schema=true`) when no authenticated user is present.
#### 8. βοΈ Tunable Framework Settings & Dynamic Bounds Validation [#8-οΈ-tunable-framework-settings--dynamic-bounds-validation]
Replaced magic numbers across subsystems with environment-driven settings in `Settings`:
* **Subsystem Tuning:** Added `PAGINATION_DEFAULT_SIZE` (20), `PAGINATION_MAX_SIZE` (100), `SEARCH_MAX_DEPTH` (3), `CACHE_LOCAL_MAXSIZE` (1000), `CACHE_DEFAULT_TTL` (3600), `CACHE_EVICTION_INTERVAL` (60), `STREAM_QUEUE_MAXSIZE` (100), and `AUTH_CACHE_TTL` (300).
* **Dynamic Parameter Validation:** Added Pydantic `@model_validator` across `PageNumberParams`, `CursorParams`, and `SearchRequest` to clamp requested page sizes within configured minimum and maximum boundaries.
#### 9. π§ͺ Advanced Testing Harness & Sandboxing Engine (`zcore.testing`) [#9--advanced-testing-harness--sandboxing-engine-zcoretesting]
* **Database Table Lifecycle (`setup_test_database`):** Drop and recreate test database tables across sync fixtures or running event loops via `ThreadPoolExecutor` safely.
* **Event Sandboxing (`EventDispatcherSandbox`):** Snapshots and restores registered event subscribers across test runs, eliminating listener pollution.
* **Multi-Dependency Overrides:** `DatabaseRollback` and `ZTestClient` support custom engine injections, multiple authentication stub overrides, and strict Pydantic user model validation (`user_model`).
#### 10. πͺ΅ Non-Intrusive Logging & Scope Context Isolation [#10--non-intrusive-logging--scope-context-isolation]
* **Status-Code-Aware Exception Logging:** Application and HTTP exceptions log at `DEBUG` for client errors (`< 500`) and `ERROR` for server failures (`>= 500`).
* **Independent Slow-Query Interceptor:** Slow queries exceeding `slow_query_threshold_ms` are logged even if general `log_sql_queries` is disabled.
* **Developer Console Mode:** Development mode preserves native Uvicorn console formatting via `passthrough_loggers`, switching cleanly to `ProcessorFormatter` JSON rendering in production.
* **Background Scope Context:** `background_scope` isolates Structlog contextvars, binds `task_id=scope_id`, and restores parent context on exit.
#### 11. π₯οΈ Cascading CLI Server Configuration & Dynamic Scaffolding [#11-οΈ-cascading-cli-server-configuration--dynamic-scaffolding]
* **Cascading Precedence (`zc run`):** Server parameters resolve with the precedence: `CLI Arguments > .env File > Defaults`.
* **Transparent Uvicorn Passthrough:** Uses `parse_known_args()` to forward arbitrary flags (e.g., `--root-path`, `--proxy-headers`) directly to Uvicorn.
* **Multi-Worker Safety:** Automatically disables `--reload` when `--workers > 1`.
* **Dynamic Test Filename Pattern:** `zc startapp ` generates Pytest files following standard naming conventions (`test_.py`).
***
### β οΈ Breaking Changes & Migration Guide [#οΈ-breaking-changes--migration-guide]
Replace manual exception handler bindings in `main.py` with the unified `register_exception_handlers` utility.
```python
# Before (v0.1.0-rc.1)
from zcore.exceptions import AppException, app_exception_handler
app.add_exception_handler(AppException, app_exception_handler)
# After (v0.1.0-rc.2)
from zcore import register_exception_handlers
register_exception_handlers(app)
```
Custom service classes overriding delete hooks must now accept the optional `force: bool = False` keyword argument.
```python
# Before (v0.1.0-rc.1)
class TaskService(BaseService[Task]):
async def pre_delete(self, id: Any) -> None:
...
async def post_delete(self, model: Task) -> None:
...
# After (v0.1.0-rc.2)
class TaskService(BaseService[Task]):
async def pre_delete(self, id: Any, force: bool = False) -> None:
...
async def post_delete(self, model: Task, force: bool = False) -> None:
...
```
If your custom code strictly expected `uuid.UUID` for `ctx.user_id` or `UserProtocol.id`, update type annotations to accept `Any`.
```python
# Before (v0.1.0-rc.1)
user_id: uuid.UUID | None = ctx.user_id
# After (v0.1.0-rc.2)
user_id: Any | None = ctx.user_id # Supports int, str, and uuid.UUID
```
`LoggingSettings.log_sql_queries` now defaults to `False` to reduce log noise. To enable SQL query dumping in development, update your settings or `.env`:
```bash
LOG_SQL_QUERIES=true
```
Welcome to **v0.1.0-rc.1 (Release Candidate 1)** of the ZCore Framework! π
As we officially transition from the Beta phase toward our milestone **v1.0.0** Stable GA, `v0.1.0-rc.1` marks a major milestone in framework stability, security hardening, and production readiness.
This release delivers dynamic primary key detection across REST endpoints, introduces an anti-shadowing weighted route sorting algorithm, optimizes ORM mutations to accept model instances directly (bypassing duplicate database fetches), adds dialect-aware batch fallbacks for non-RETURNING databases (such as MySQL), integrates a centralized IANA timezone subsystem (`ZDateTime`), redesigns the structured logging pipeline via `ProcessorFormatter`, completely hardens the file storage layer with strict sandboxing and active XSS/executable scanning, and achieves complete unit and integration test coverage across all subsystems.
### π Core Features & Architectural Advancements [#-core-features--architectural-advancements-1]
#### 1. π― Dynamic Primary Key Resolution & Generalized Routing (`BaseRouter`) [#1--dynamic-primary-key-resolution--generalized-routing-baserouter]
Previously, `BaseRouter` assumed model primary keys were strictly UUIDs. In `v0.1.0-rc.1`, routing infrastructure dynamically inspects ORM metadata:
* **Automatic PK Type Detection (`_resolve_pk_type`):** Automatically resolves whether the primary key is an `int`, `uuid.UUID`, or other scalar type via SQLAlchemy column inspection.
* **Dynamic Path Mapping (`_get_pk_path`):** Generates type-safe URL converter paths (e.g., `/{id:int}` for integer PKs, `/{id:uuid}` for UUIDs, or `/{id}` as a fallback).
* **Explicit PK Override:** Models or routers can explicitly declare `pk_type = int` to override dynamic reflection.
* **Streamlined Endpoint Signatures:** Cleaned up internal wrapper signatures by removing unused `**kwargs` and enforcing explicit parameter typing.
#### 2. βοΈ Weighted Route Specificity Sorting (Anti-Path Shadowing) [#2-οΈ-weighted-route-specificity-sorting-anti-path-shadowing]
A notorious issue in REST frameworks is Path Shadowingβwhere dynamic parameterized routes accidentally capture requests intended for static endpoints (e.g., `GET /{id}` shadowing `POST /search`):
* **Multi-Tier Weighted Scoring (`_sort_routes`):** Replaced basic string sorting with an advanced scoring tuple: `(has_dynamic, total_dynamic, segment_scores, -len(segments))`.
* **Strict Route Precedence:** Guarantees that static segment routes (`/search`) are always matched before single dynamic parameters (`/{id}`) and catch-all path parameters (`/{path:path}`), regardless of declaration order.
#### 3. β‘ Optimized Instance-Aware ORM Mutations (`update`, `delete`, `update_multi`) [#3--optimized-instance-aware-orm-mutations-update-delete-update_multi]
To eliminate redundant database lookups when an entity is already loaded in memory:
* **Direct Model Acceptance:** `BaseRepository.update()` and `BaseRepository.delete()` now accept `target: ModelType | Any`. If a model instance is passed directly, the preliminary `SELECT` query is completely skipped.
* **Instance-Keyed Batch Updates:** `update_multi` now accepts dictionaries keyed by either model instances or primary key scalars (`dict[ModelType | Any, BaseModel]`), resolving the PK seamlessly via `getattr(key, self.pk_name, key)`.
* **Aligned Service Hooks:** Updated `pre_update`, `on_update`, `pre_delete`, and `on_delete` in `BaseService` to accept `target: ModelType | Any`.
#### 4. π Cross-Dialect Batch Compatibility (Dialect-Aware Fallbacks) [#4--cross-dialect-batch-compatibility-dialect-aware-fallbacks]
Enables seamless database portability across SQL engines that lack native `RETURNING` support:
* **Dialect-Aware `create_multi`:** Inspects the engine's `insert_returning` capability. If supported (PostgreSQL, modern SQLite), it executes `insert().returning()`. Otherwise (MySQL, older SQLite), it falls back gracefully to `db.add_all()` and `db.flush()`.
* **Dialect-Aware `delete_multi`:** Inspects `delete_returning`. If unsupported, it fetches records via `get_by_ids()` prior to atomic deletion, guaranteeing deleted instances are returned intact.
#### 5. β±οΈ Centralized Timezone Management & `ZDateTime` Subsystem [#5-οΈ-centralized-timezone-management--zdatetime-subsystem]
Introduced `zcore.utils.timezone` for robust, timezone-aware datetime manipulation:
* **IANA ZoneInfo Resolution (`get_app_timezone`):** Resolves timezones with LRU caching and falls back safely to UTC if an invalid timezone is provided.
* **`ZDateTime` Type Alias:** Custom type alias utilizing Pydantic's `PlainSerializer` to automatically format datetimes as timezone-aware ISO 8601 strings with offsets during JSON serialization.
* **Timezone-Aware Helpers:** Added `now()`, `utc_now()`, `to_app_timezone()`, and `format_iso_with_app_timezone()`.
* **Soft Delete & JSON Sync:** `SoftDeleteMixin` and `CustomJSONEncoder` now natively utilize timezone-aware timestamps.
* **Windows Support:** Added `tzdata>=2024.1` for guaranteed IANA timezone database resolution on Windows environments.
#### 6. πͺ΅ Enterprise Structured Logging (`LoggingSettings` & `ProcessorFormatter`) [#6--enterprise-structured-logging-loggingsettings--processorformatter]
Redesigned the logging pipeline to seamlessly unify structlog and Python's standard logging library:
* **`ProcessorFormatter` Integration:** Eliminates duplicate log emissions and bridges third-party loggers (`uvicorn`, `sqlalchemy.engine`) into a unified structlog pipeline.
* **`LoggingSettings` Model:** Configured via `Settings.LOGGING` with backward-compatible bidirectional synchronization to legacy `LOG_LEVEL`.
* **Slow Query Interceptor:** Added `slow_query_threshold_ms` to silently filter out fast queries and log only statements exceeding latency thresholds.
* **Built-in File Rotation & DictConfig:** Added `RotatingFileHandler` support (10MB, 5 backups) via `file_path` setting and support for full `logging.config.dictConfig` overrides.
* **Enhanced Telemetry:** `RequestLogMiddleware` now captures real HTTP status codes from `http.response.start` and client IPs.
#### 7. π¦ Sandboxed Local Storage & Advanced Threat Validation [#7--sandboxed-local-storage--advanced-threat-validation]
Expanded `zcore.storage` into an enterprise-grade, sandboxed asset management subsystem:
* **Path Traversal Defense:** `LocalStorageProvider` incorporates `_extract_key`, `_key_to_path`, and `_resolve_path` to strictly bound all read/write/delete operations within `base_path`.
* **URL Resolution & Existence Check:** Added `get_url(file_path_or_url)` and `exists(file_path_or_url)` to the `StorageProvider` base interface.
* **Executable & XSS Threat Detection (`SafeMimeTypeValidator`):**
* Increased sample inspection buffer from 2KB to 8KB.
* Rejects binary executable headers (`MZ` for `.exe`/`.dll`, `#!/` for shell scripts, `\x7fELF` for Linux binaries).
* Scans file payloads for Stored XSS and active script injections (`