Task: e4845baa-2c2e-4b0a-abea-11afe697c555 — Admin routes: children
Feature: 16 — NestQuest API Service
Done-condition
Admin routes for children: create, edit (display name, colour, avatar_ref, sort_order), set_active, and reorder. Each route validates input and calls the core children business layer with NO business logic in the handler. Tests exercise each route as an admin and confirm the expected database change: a created child exists with the given name; an edit persists; deactivation sets the active flag false (never hard-deletes); reorder changes sort_order.
Changes
api/routes_admin.py: thin admin-plane adapters — POST /children (201), PATCH /children/{id}, PATCH /children/{id}/active, POST /children/reorder, plus a thin GET /children list. Every route sits under the router-level ONE require_admin dependency (no per-route auth). Handlers parse with Pydantic, call one nestquest_core.children function, serialize the returned ChildRecord (id, display_name, colour, avatar_ref, sort_order, is_active). Edit forwards only supplied fields via model_dump(exclude_unset=True) so an omitted field is never passed to the core; an explicit null is passed and rejected by the core. Error mapping (documented in the module docstring): non-existent child ValueError → 404; every other core ValueError → 422 (same class as Pydantic rejections); malformed bodies → 422 by FastAPI.
api/nestquest_core.py: re-export core_children (the bundled core children business layer).
tests/admin_jwt_harness.py (new): the local-RSA-key/stubbed-JWKS admin JWT harness factored out of test_api_admin_auth.py so the children route tests reuse it instead of duplicating it.
tests/conftest.py: shared temp_db_path + admin_client fixtures built on the harness.
tests/test_api_admin_auth.py: now imports the shared harness; all test bodies unchanged (40 tests still pass).
tests/test_api_admin_children.py (new, 17 tests): admin JWT authenticates; non-admin JWT → 403 / absent credential → 401 with no row written; created child exists with the given name (DB-verified); edit persists supplied fields and skips omitted ones; explicit null colour → 422 unchanged; deactivation sets is_active false and the row still exists (never hard-deleted); reorder changes sort_order (DB-verified) and non-permutations are 422 with no write; unknown child → 404.
Test result
TZ=UTC uv run pytest -q → 1669 passed in 9.18s (baseline 1652 + 17 new)
TZ=UTC uv run pytest -q tests/test_api_admin_children.py → 17 passed in 0.21s
## Maestro task
- **Task:** e4845baa-2c2e-4b0a-abea-11afe697c555 — Admin routes: children
- **Feature:** 16 — NestQuest API Service
## Done-condition
Admin routes for children: create, edit (display name, colour, avatar_ref, sort_order), set_active, and reorder. Each route validates input and calls the core children business layer with NO business logic in the handler. Tests exercise each route as an admin and confirm the expected database change: a created child exists with the given name; an edit persists; deactivation sets the active flag false (never hard-deletes); reorder changes sort_order.
## Changes
- `api/routes_admin.py`: thin admin-plane adapters — `POST /children` (201), `PATCH /children/{id}`, `PATCH /children/{id}/active`, `POST /children/reorder`, plus a thin `GET /children` list. Every route sits under the router-level ONE `require_admin` dependency (no per-route auth). Handlers parse with Pydantic, call one `nestquest_core.children` function, serialize the returned ChildRecord (id, display_name, colour, avatar_ref, sort_order, is_active). Edit forwards only supplied fields via `model_dump(exclude_unset=True)` so an omitted field is never passed to the core; an explicit null is passed and rejected by the core. Error mapping (documented in the module docstring): non-existent child ValueError → 404; every other core ValueError → 422 (same class as Pydantic rejections); malformed bodies → 422 by FastAPI.
- `api/nestquest_core.py`: re-export `core_children` (the bundled core children business layer).
- `tests/admin_jwt_harness.py` (new): the local-RSA-key/stubbed-JWKS admin JWT harness factored out of test_api_admin_auth.py so the children route tests reuse it instead of duplicating it.
- `tests/conftest.py`: shared `temp_db_path` + `admin_client` fixtures built on the harness.
- `tests/test_api_admin_auth.py`: now imports the shared harness; all test bodies unchanged (40 tests still pass).
- `tests/test_api_admin_children.py` (new, 17 tests): admin JWT authenticates; non-admin JWT → 403 / absent credential → 401 with no row written; created child exists with the given name (DB-verified); edit persists supplied fields and skips omitted ones; explicit null colour → 422 unchanged; deactivation sets is_active false and the row still exists (never hard-deleted); reorder changes sort_order (DB-verified) and non-permutations are 422 with no write; unknown child → 404.
## Test result
- `TZ=UTC uv run pytest -q` → **1669 passed in 9.18s** (baseline 1652 + 17 new)
- `TZ=UTC uv run pytest -q tests/test_api_admin_children.py` → **17 passed in 0.21s**
Task e4845baa (feature 16, NestQuest API service): the admin PWA needs
to manage the children registry, but the only removal path the domain
allows is deactivation and the name/NULL/bool/permutation policies all
live in core.children — so the plane gets thin adapters that forward
each request to ONE core call and never re-implement a rule.
WHY: create/edit/set_active/reorder (+ a thin GET list) hang off the
router's ONE require_admin dependency; edit forwards only the fields
the request supplied (exclude_unset) so an omitted field stays
unchanged while an explicit null is rejected by the core rather than
silently ignored. Core ValueErrors map to 404 (non-existent child) or
422 (invalid input, same class Pydantic reports) and are documented in
the module docstring; the core rejects before any write, so neither
status can leave a half-applied change.
The admin JWT test harness (local RSA keys, stubbed JWKS, token
minting, app runner) is factored into tests/admin_jwt_harness.py so
the children route tests reuse it instead of duplicating it; the auth
tests import the same harness with unchanged test bodies, and the
shared temp_db_path/admin_client fixtures now live in conftest.py.
TZ=UTC uv run pytest -q: 1669 passed (baseline 1652 + 17 new).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Maestro task
Done-condition
Admin routes for children: create, edit (display name, colour, avatar_ref, sort_order), set_active, and reorder. Each route validates input and calls the core children business layer with NO business logic in the handler. Tests exercise each route as an admin and confirm the expected database change: a created child exists with the given name; an edit persists; deactivation sets the active flag false (never hard-deletes); reorder changes sort_order.
Changes
api/routes_admin.py: thin admin-plane adapters —POST /children(201),PATCH /children/{id},PATCH /children/{id}/active,POST /children/reorder, plus a thinGET /childrenlist. Every route sits under the router-level ONErequire_admindependency (no per-route auth). Handlers parse with Pydantic, call onenestquest_core.childrenfunction, serialize the returned ChildRecord (id, display_name, colour, avatar_ref, sort_order, is_active). Edit forwards only supplied fields viamodel_dump(exclude_unset=True)so an omitted field is never passed to the core; an explicit null is passed and rejected by the core. Error mapping (documented in the module docstring): non-existent child ValueError → 404; every other core ValueError → 422 (same class as Pydantic rejections); malformed bodies → 422 by FastAPI.api/nestquest_core.py: re-exportcore_children(the bundled core children business layer).tests/admin_jwt_harness.py(new): the local-RSA-key/stubbed-JWKS admin JWT harness factored out of test_api_admin_auth.py so the children route tests reuse it instead of duplicating it.tests/conftest.py: sharedtemp_db_path+admin_clientfixtures built on the harness.tests/test_api_admin_auth.py: now imports the shared harness; all test bodies unchanged (40 tests still pass).tests/test_api_admin_children.py(new, 17 tests): admin JWT authenticates; non-admin JWT → 403 / absent credential → 401 with no row written; created child exists with the given name (DB-verified); edit persists supplied fields and skips omitted ones; explicit null colour → 422 unchanged; deactivation sets is_active false and the row still exists (never hard-deleted); reorder changes sort_order (DB-verified) and non-permutations are 422 with no write; unknown child → 404.Test result
TZ=UTC uv run pytest -q→ 1669 passed in 9.18s (baseline 1652 + 17 new)TZ=UTC uv run pytest -q tests/test_api_admin_children.py→ 17 passed in 0.21sIndependent Claude Sonnet 5 review completed for this exact commit.