fix(api): R14 — promote route translates V1-0 ValueError to 400

Squash-merge of branch claude/r14-promote-400 (3888db9), approved by
Codex (no findings, targeted suite 15 passed).

POST /entities/{id}/promote now wraps promote_entity in
try/except ValueError → HTTPException(400). Previously the V1-0
provenance re-check raised ValueError that the route didn't catch,
so legacy no-provenance candidates promoted via the API surfaced
as 500 instead of 400. Matches the existing ValueError → 400
handling on POST /entities (routes.py:1490).

New regression test test_api_promote_returns_400_on_legacy_no_provenance
inserts a pre-V1-0 candidate directly, POSTs promote, asserts 400
with the expected detail, asserts the row stays candidate.

Also adds .obsidian/, .vscode/, .idea/ to .gitignore so editor
state doesn't sneak into future commits.

Test count: 547 → 548. Closes R14.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 11:20:15 -04:00
parent 98bc848184
commit 0989fed9ee
4 changed files with 53 additions and 7 deletions

View File

@@ -2187,12 +2187,17 @@ def api_promote_entity(
from atocore.engineering.service import promote_entity
target_project = req.target_project if req is not None else None
note = req.note if req is not None else ""
success = promote_entity(
entity_id,
actor="api-http",
note=note,
target_project=target_project,
)
try:
success = promote_entity(
entity_id,
actor="api-http",
note=note,
target_project=target_project,
)
except ValueError as e:
# V1-0 F-8 re-check raises ValueError for no-provenance candidates
# (see service.promote_entity). Surface as 400, not 500.
raise HTTPException(status_code=400, detail=str(e))
if not success:
raise HTTPException(status_code=404, detail=f"Entity not found or not a candidate: {entity_id}")
result = {"status": "promoted", "id": entity_id}