# Aether V3 Development Standards & Strategy This document serves as the master guide for the Aether API V3. It combines core architectural principles with the finalized infrastructure standards established in January 2026. ## 1. Core Principles - **`id_random` Primary**: `id_random` (URL-safe string) is the only identifier exposed to clients. Internal integer `id` is a private implementation detail. - **Singular naming**: All object types and prefixes use singular form (e.g., `journal`, not `journals`). - **Inheritance**: All models must inherit from `CoreObject` to ensure consistent base fields. - **Separation of Concerns**: Business logic lives in `app/methods/`, CRUD helpers in `app/lib_sql_crud.py`, and routing in `app/routers/`. ## 2. Infrastructure Standards Finalized Jan 15, 2026, to ensure boot stability. ### Application Entry Point (`app/main.py`) - **Registry Pattern**: All routers are registered via `app/routers/registry.py`. - **Lifespan Management**: All startup tasks (logging setup, DB bootstrap, engine refresh) MUST reside within the `@contextlib.asynccontextmanager` lifespan. - **No Top-Level Logic**: No SQL queries or heavy initialization should execute at the module level. ### Database Layering - **`lib_sql_core.py`**: The foundational "Source of Truth" for the SQLAlchemy engine and global `db` connection. - **`lib_sql_crud.py`**: High-level logic for `sql_insert`, `sql_select`, etc. - **`db_sql.py`**: A backward-compatible facade. code should import from here. ### Logging - **Explicit Setup**: `setup_logging(settings)` must be called during the lifespan startup. - **`lib_log_v3.py`**: New home for logging configuration and the `logger_reset` decorator. ## 3. V3 CRUD Strategy ### URL Structure - **Top-Level**: `/v3/crud/{obj_type}/` - **Nested**: `/v3/crud/{parent_type}/{parent_id_random}/{obj_type}/` (Enforces parent ownership). ### Search API - **POST Based**: Complex filtering is handled via `POST /search` with a JSON body containing `and`, `or`, and `not` logic. - **Hybrid Filtering**: (Proposed) Query parameters should append simple standard filters (e.g., `?enabled=true`) to the complex body logic. ### Field Evolution Checklist When a table or view gains, loses, or renames fields, keep the API contract and search registry in sync: 1. Update the Pydantic model in `app/models/` first so CRUD serialization matches the new shape. 2. Update the SQL view or table projection so `GET` and `SEARCH` responses actually return the field. 3. Update `searchable_fields` in `app/object_definitions/` only for fields that should be searchable. 4. Add write-only, virtual, or view-only fields to `fields_to_exclude_from_db` when they must not be persisted. 5. Run the schema/search E2E tests that cover the object type before handing the change off. 6. **Restart the Docker API containers** (`docker compose restart ae_api`) — Python file changes inside containers are not picked up until restart. For `archive_content`, the public field set now includes `external_id` and `code`, and future additions should follow the same order of operations. #### Alt-view fields (fields only in `tbl_alt`) Some objects have a richer alternate SQL view (`tbl_alt`) that adds JOINed/computed columns absent from the default view (`tbl_default`). For example, `event_session` uses `v_event_session_w_file_count` as its alt view (triggered by `?view=alt` on search, or `?inc_file_count=true` on GET). - Fields from `tbl_alt` **must still be declared in the Pydantic model** and in `searchable_fields` — Pydantic strips undeclared fields, and the search whitelist rejects unknown field names regardless of the view. - When adding such a field, add a comment noting which view provides it (e.g., `# from v_event_session_w_file_count`). - Searching by an alt-view field on the default endpoint returns `400 Unknown column` — this is correct behaviour. Clients must pass `?view=alt` to use those fields in a search. - **Known alt-view fields restored May 2026:** `event_presentation_li_qry_str`, `event_presenter_li_qry_str` (event_session); `account_name`, `account_code`, and related convenience fields (site_domain). ### Response Views (`?view=` parameter) - The nested search router (`api_crud_v3_nested.py`) already supports `?view=` to switch between registered views. `view=default` uses `tbl_default`; `view=alt` uses `tbl_alt`; additional named views can be added to the object registry as `tbl_` / `mdl_`. - Flat search (`api_crud_v3.py`) does not yet support `?view=` — it always uses `tbl_default`. ## 4. Stability Rules 1. **Baby Step Testing**: Restart Docker and verify root health after *every* modular change. 2. **Avoid Shadowing**: Never name a module part of the `app.` package the same as a common instance variable (e.g., avoid `app.middleware` package if you use `app = FastAPI()`). 3. **Deferred Imports**: Use `from app.db_sql import ...` *inside* functions in library modules to prevent circular dependency traps. 4. **Model changes require container restart**: Editing Python files on the host does not hot-reload inside Docker. Always run `docker compose restart ae_api` after model or object-definition changes, then re-run E2E tests.