Task: da0226b3-a504-4db2-8cd8-b4fb07adbdb9 (Feature 16 — NestQuest API Service)
Done-condition: Admin routes for uncomplete and regenerate. Uncomplete reverses a completion by appending a reversal event (never editing or deleting the original) and returns the instance to open. Regenerate re-materializes the rolling horizon for the household (or a definition/child) through the same core path the integration uses. Each validates input and calls core with no business logic in the handler. Tests as an admin confirm uncomplete appends a reversal so the derived state is open again, and regenerate re-materializes without duplicating existing instances.
What this does
POST /api/v1/admin/instances/{instance_id}/uncomplete — calls nestquest_core.completion.uncomplete_instance with actor_source="user" and actor_user_id taken from the verified JWT sub (the router-level require_admin dependency is the ONE auth check). The core APPENDS an uncompleted event — the original completed row is never edited or deleted — and the derived state returns to open (or stays honest as missed when past due). On an actual reversal (.appended) it publishes nestquest_quest_uncompleted through the existing api/transitions.publish_quest_uncompleted helper; a no-op publishes nothing. Response: derived state + appended.
POST /api/v1/admin/regenerate — scope household (default) = core.materialize.materialize over [today, today + const.DEFAULT_HORIZON_DAYS] (the core default; the API config supplies no horizon knob); scope definition/child = regenerate_for_definition/regenerate_for_child after ONE DAO existence read each (unknown id → 404 — the core regenerate path itself takes no existence stance). Idempotent upsert: re-running never duplicates. Response: scope, scoped id, core upsert count.
As an admin: uncomplete appends the reversal — original completed row proven byte-identical, new uncompleted row attributed to the token sub, derived state open again; second uncomplete appends nothing; a no-op on a past-due open instance reports missed and appends nothing.
SSE: an actual reversal publishes exactly ONE quest-uncompleted transition (subscribed live as tests/test_api_sse.py does); the no-op second call publishes nothing.
Regenerate: household run twice — same upsert count, instance table stable (no duplicates); definition scope materializes then stays stable across runs; child scope rebuilds only that child's side (other child's rows keep identity).
TZ=UTC uv run pytest -q → 1736 passed in 11.51s (baseline 1718 + 18 new)
TZ=UTC uv run pytest -q tests/test_api_admin_uncomplete.py → 18 passed
No merge — awaiting independent review.
Task: da0226b3-a504-4db2-8cd8-b4fb07adbdb9 (Feature 16 — NestQuest API Service)
Done-condition: Admin routes for uncomplete and regenerate. Uncomplete reverses a completion by appending a reversal event (never editing or deleting the original) and returns the instance to open. Regenerate re-materializes the rolling horizon for the household (or a definition/child) through the same core path the integration uses. Each validates input and calls core with no business logic in the handler. Tests as an admin confirm uncomplete appends a reversal so the derived state is open again, and regenerate re-materializes without duplicating existing instances.
## What this does
- `POST /api/v1/admin/instances/{instance_id}/uncomplete` — calls `nestquest_core.completion.uncomplete_instance` with `actor_source="user"` and `actor_user_id` taken from the verified JWT `sub` (the router-level `require_admin` dependency is the ONE auth check). The core APPENDS an `uncompleted` event — the original `completed` row is never edited or deleted — and the derived state returns to `open` (or stays honest as `missed` when past due). On an actual reversal (`.appended`) it publishes `nestquest_quest_uncompleted` through the existing `api/transitions.publish_quest_uncompleted` helper; a no-op publishes nothing. Response: derived `state` + `appended`.
- `POST /api/v1/admin/regenerate` — scope `household` (default) = `core.materialize.materialize` over `[today, today + const.DEFAULT_HORIZON_DAYS]` (the core default; the API config supplies no horizon knob); scope `definition`/`child` = `regenerate_for_definition`/`regenerate_for_child` after ONE DAO existence read each (unknown id → 404 — the core regenerate path itself takes no existence stance). Idempotent upsert: re-running never duplicates. Response: scope, scoped id, core upsert `count`.
- Error mapping kept narrow: unknown instance/definition/child → 404; malformed input and rejected core arguments → 4xx (422); anything unexpected re-raises (no swallowing).
- `api/nestquest_core.py` re-exports `core_materialize`, `core_const`, `core_dao_children`, `core_dao_rules` for the route.
## Tests (tests/test_api_admin_uncomplete.py, shared admin JWT harness)
- As an admin: uncomplete appends the reversal — original completed row proven byte-identical, new `uncompleted` row attributed to the token `sub`, derived state `open` again; second uncomplete appends nothing; a no-op on a past-due open instance reports `missed` and appends nothing.
- SSE: an actual reversal publishes exactly ONE quest-uncompleted transition (subscribed live as tests/test_api_sse.py does); the no-op second call publishes nothing.
- Regenerate: household run twice — same upsert count, instance table stable (no duplicates); definition scope materializes then stays stable across runs; child scope rebuilds only that child's side (other child's rows keep identity).
- Non-admin JWT → 403, absent credential → 401, nothing written; malformed scope/id bodies and unknown ids → 422/404.
## Checks
- `TZ=UTC uv run pytest -q` → `1736 passed in 11.51s` (baseline 1718 + 18 new)
- `TZ=UTC uv run pytest -q tests/test_api_admin_uncomplete.py` → `18 passed`
No merge — awaiting independent review.
The admin plane could reverse nothing and re-materialize nothing: a
mis-tapped completion could only be fixed by hand in the database, and
a config or presence change left the household's rolling horizon
stale until the integration itself regenerated it.
- POST /api/v1/admin/instances/{id}/uncomplete appends the reversal
through core uncomplete_instance (actor_source 'user' with the
verified JWT's sub — D-008), so the original completed row is never
edited or deleted and the derived state returns to open. An actual
reversal publishes the quest-uncompleted transition via the existing
api.transitions helper; an already-open no-op publishes nothing and
still answers with the honest derived state (open, or missed when
past due).
- POST /api/v1/admin/regenerate re-materializes the rolling horizon
through the same core path the integration uses: household (default)
= materialize over [today, today + const.DEFAULT_HORIZON_DAYS] (the
API config supplies no horizon knob), definition/child =
regenerate_for_definition / regenerate_for_child after ONE DAO
existence read each (unknown id -> 404, which the core's regenerate
path itself does not check). The walk is idempotent, so re-running
never duplicates an instance.
Both handlers are thin adapters: Pydantic models carry the request
shape (scope/id pairing is the model's job), the business rules stay
in core, and the error mapping stays narrow — unknown
instance/definition/child 404, rejected argument 422, anything else
re-raises.
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.
Task: da0226b3-a504-4db2-8cd8-b4fb07adbdb9 (Feature 16 — NestQuest API Service)
Done-condition: Admin routes for uncomplete and regenerate. Uncomplete reverses a completion by appending a reversal event (never editing or deleting the original) and returns the instance to open. Regenerate re-materializes the rolling horizon for the household (or a definition/child) through the same core path the integration uses. Each validates input and calls core with no business logic in the handler. Tests as an admin confirm uncomplete appends a reversal so the derived state is open again, and regenerate re-materializes without duplicating existing instances.
What this does
POST /api/v1/admin/instances/{instance_id}/uncomplete— callsnestquest_core.completion.uncomplete_instancewithactor_source="user"andactor_user_idtaken from the verified JWTsub(the router-levelrequire_admindependency is the ONE auth check). The core APPENDS anuncompletedevent — the originalcompletedrow is never edited or deleted — and the derived state returns toopen(or stays honest asmissedwhen past due). On an actual reversal (.appended) it publishesnestquest_quest_uncompletedthrough the existingapi/transitions.publish_quest_uncompletedhelper; a no-op publishes nothing. Response: derivedstate+appended.POST /api/v1/admin/regenerate— scopehousehold(default) =core.materialize.materializeover[today, today + const.DEFAULT_HORIZON_DAYS](the core default; the API config supplies no horizon knob); scopedefinition/child=regenerate_for_definition/regenerate_for_childafter ONE DAO existence read each (unknown id → 404 — the core regenerate path itself takes no existence stance). Idempotent upsert: re-running never duplicates. Response: scope, scoped id, core upsertcount.api/nestquest_core.pyre-exportscore_materialize,core_const,core_dao_children,core_dao_rulesfor the route.Tests (tests/test_api_admin_uncomplete.py, shared admin JWT harness)
uncompletedrow attributed to the tokensub, derived stateopenagain; second uncomplete appends nothing; a no-op on a past-due open instance reportsmissedand appends nothing.Checks
TZ=UTC uv run pytest -q→1736 passed in 11.51s(baseline 1718 + 18 new)TZ=UTC uv run pytest -q tests/test_api_admin_uncomplete.py→18 passedNo merge — awaiting independent review.
The admin plane could reverse nothing and re-materialize nothing: a mis-tapped completion could only be fixed by hand in the database, and a config or presence change left the household's rolling horizon stale until the integration itself regenerated it. - POST /api/v1/admin/instances/{id}/uncomplete appends the reversal through core uncomplete_instance (actor_source 'user' with the verified JWT's sub — D-008), so the original completed row is never edited or deleted and the derived state returns to open. An actual reversal publishes the quest-uncompleted transition via the existing api.transitions helper; an already-open no-op publishes nothing and still answers with the honest derived state (open, or missed when past due). - POST /api/v1/admin/regenerate re-materializes the rolling horizon through the same core path the integration uses: household (default) = materialize over [today, today + const.DEFAULT_HORIZON_DAYS] (the API config supplies no horizon knob), definition/child = regenerate_for_definition / regenerate_for_child after ONE DAO existence read each (unknown id -> 404, which the core's regenerate path itself does not check). The walk is idempotent, so re-running never duplicates an instance. Both handlers are thin adapters: Pydantic models carry the request shape (scope/id pairing is the model's job), the business rules stay in core, and the error mapping stays narrow — unknown instance/definition/child 404, rejected argument 422, anything else re-raises.Independent Claude Sonnet 5 review completed for this exact commit.