POST /api/v1/panel/instances/{id}/complete (panel service token required) writes a completion as actor=panel with the tapped actor_child_id and returns success. A second call for the same instance is a no-op that still returns success (the core completion layer's appended flag decides, so no duplicate event). A request for a non-existent instance returns 404; a request without the panel token returns 401. Tests cover the first call, the idempotent second call, the 404, and the 401.
Changes
api/routes_panel.py: POST /instances/{instance_id}/complete on the existing panel router (inherits the ONE require_panel_token dependency — no second token check). Pydantic PanelCompleteRequest with a positive-int actor_child_id validator (documented, OpenAPI-visible). Thin handler: db off app.state.db, one tz-aware clock read via _local_now, delegates to nestquest_core.completion.complete_instance(actor_source="panel", actor_child_id=). Idempotence: CompletionResult.appended decides in core; route returns {"status": "done"} for both appended and no-op calls. Error mapping narrow: only the unknown-instance ValueError ("instance_id:" prefix) -> 404; everything else re-raises.
Integrity decision: complete_instance does NOT verify the tapped actor_child_id owns the instance, so the route guards it — one QuestInstancesDao.get_by_id read; mismatch -> 404 (not 403, so existence does not leak to a wrong-profile tap). Documented in the handler docstring.
api/nestquest_core.py: re-export core_completion (the core.completion submodule) like the other core submodules.
tests/test_api_panel.py: new tests on the app's SAME nestquest_core copy — first call writes actor_source='panel' / tapped actor_child_id / actor_user_id NULL and derives done; second call success with exactly one event; unknown instance 404; missing token 401; wrong child 404 with no event; OpenAPI documents the route and PanelCompleteRequest.
Test result
TZ=UTC uv run pytest -q tests/test_api_panel.py: 16 passed in 0.28s
TZ=UTC uv run pytest -q (full suite): 1605 passed in 7.44s (baseline 1599 + 6 new)
Closes the done-condition; not merged — awaiting independent review.
## Maestro task
1860ef76-e936-4d4f-a150-1cf7b721f519 — Panel plane: complete route (idempotent)
## Done-condition
POST /api/v1/panel/instances/{id}/complete (panel service token required) writes a completion as actor=panel with the tapped actor_child_id and returns success. A second call for the same instance is a no-op that still returns success (the core completion layer's appended flag decides, so no duplicate event). A request for a non-existent instance returns 404; a request without the panel token returns 401. Tests cover the first call, the idempotent second call, the 404, and the 401.
## Changes
- api/routes_panel.py: POST /instances/{instance_id}/complete on the existing panel router (inherits the ONE require_panel_token dependency — no second token check). Pydantic PanelCompleteRequest with a positive-int actor_child_id validator (documented, OpenAPI-visible). Thin handler: db off app.state.db, one tz-aware clock read via _local_now, delegates to nestquest_core.completion.complete_instance(actor_source="panel", actor_child_id=<body>). Idempotence: CompletionResult.appended decides in core; route returns {"status": "done"} for both appended and no-op calls. Error mapping narrow: only the unknown-instance ValueError ("instance_id:" prefix) -> 404; everything else re-raises.
- Integrity decision: complete_instance does NOT verify the tapped actor_child_id owns the instance, so the route guards it — one QuestInstancesDao.get_by_id read; mismatch -> 404 (not 403, so existence does not leak to a wrong-profile tap). Documented in the handler docstring.
- api/nestquest_core.py: re-export core_completion (the core.completion submodule) like the other core submodules.
- tests/test_api_panel.py: new tests on the app's SAME nestquest_core copy — first call writes actor_source='panel' / tapped actor_child_id / actor_user_id NULL and derives done; second call success with exactly one event; unknown instance 404; missing token 401; wrong child 404 with no event; OpenAPI documents the route and PanelCompleteRequest.
## Test result
- TZ=UTC uv run pytest -q tests/test_api_panel.py: 16 passed in 0.28s
- TZ=UTC uv run pytest -q (full suite): 1605 passed in 7.44s (baseline 1599 + 6 new)
Closes the done-condition; not merged — awaiting independent review.
POST /api/v1/panel/instances/{id}/complete delegates to the core
completion layer (nestquest_core.completion.complete_instance) with
actor_source='panel' and the tapped actor_child_id, so the append
lock, actor validation, and idempotence all live in core. The route
maps only the unknown-instance ValueError to 404, guards the
child-mismatch case (complete_instance does not check that the tapped
profile owns the instance) with a 404 that does not leak existence,
and returns success on both the first call and the already-done
no-op — the CompletionResult.appended flag decides, so a retried tap
never writes a second event. The router's single require_panel_token
dependency covers auth; tests cover first call, idempotent repeat,
404, 401, wrong-child 404, and OpenAPI documentation.
Task: 1860ef76-e936-4d4f-a150-1cf7b721f519
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
1860ef76-e936-4d4f-a150-1cf7b721f519 — Panel plane: complete route (idempotent)
Done-condition
POST /api/v1/panel/instances/{id}/complete (panel service token required) writes a completion as actor=panel with the tapped actor_child_id and returns success. A second call for the same instance is a no-op that still returns success (the core completion layer's appended flag decides, so no duplicate event). A request for a non-existent instance returns 404; a request without the panel token returns 401. Tests cover the first call, the idempotent second call, the 404, and the 401.
Changes
Test result
Closes the done-condition; not merged — awaiting independent review.
POST /api/v1/panel/instances/{id}/complete delegates to the core completion layer (nestquest_core.completion.complete_instance) with actor_source='panel' and the tapped actor_child_id, so the append lock, actor validation, and idempotence all live in core. The route maps only the unknown-instance ValueError to 404, guards the child-mismatch case (complete_instance does not check that the tapped profile owns the instance) with a 404 that does not leak existence, and returns success on both the first call and the already-done no-op — the CompletionResult.appended flag decides, so a retried tap never writes a second event. The router's single require_panel_token dependency covers auth; tests cover first call, idempotent repeat, 404, 401, wrong-child 404, and OpenAPI documentation. Task: 1860ef76-e936-4d4f-a150-1cf7b721f519Independent Claude Sonnet 5 review completed for this exact commit.