feat(review): mark-reviewed action + §7 catalog unreviewed filter (§22.4c)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-06-03 23:56:58 -07:00
parent 2fe2a719ac
commit 76207bbb62
3 changed files with 152 additions and 3 deletions
+44 -3
View File
@@ -611,13 +611,16 @@ def make_router(
# ---------------------------------------------------------------
@router.get("/api/rfcs")
async def list_rfcs(request: Request) -> dict[str, Any]:
async def list_rfcs(request: Request, unreviewed: str | None = None) -> dict[str, Any]:
"""§7's left pane data.
The chip-filter / sort / search combinatorics live on the
client — the server returns the full set and lets the chips
narrow it. The set is small (hundreds, not thousands) for the
foreseeable future, so paginating here would buy nothing.
§22.4c: pass `?unreviewed=true` to narrow to active entries
still awaiting owner review.
"""
viewer = auth.current_user(request)
viewer_id = viewer.user_id if viewer else None
@@ -627,6 +630,10 @@ def make_router(
if not visible:
return {"items": []}
placeholders = ",".join("?" for _ in visible)
params = list(visible)
unreviewed_clause = ""
if unreviewed is not None and unreviewed.lower() in ("1", "true", "yes"):
unreviewed_clause = " AND unreviewed = 1 AND state = 'active'"
rows = db.conn().execute(
f"""
SELECT slug, title, state, rfc_id, repo,
@@ -634,10 +641,10 @@ def make_router(
last_main_commit_at, last_entry_commit_at, updated_at
FROM cached_rfcs
WHERE state IN ('super-draft', 'active')
AND project_id IN ({placeholders})
AND project_id IN ({placeholders}){unreviewed_clause}
ORDER BY COALESCE(last_main_commit_at, last_entry_commit_at) DESC
""",
visible,
params,
).fetchall()
starred = set()
@@ -700,6 +707,40 @@ def make_router(
payload["proposed_use_case"] = uc["use_case"] if uc else None
return payload
# ---------------------------------------------------------------
# §22.4c: mark-reviewed — clear an active entry's `unreviewed` flag
# ---------------------------------------------------------------
@router.post("/api/projects/{project_id}/rfcs/{slug}/mark-reviewed")
async def mark_reviewed(project_id: str, slug: str, request: Request) -> dict[str, Any]:
"""§22.4c — clear an active entry's `unreviewed` flag. Authority is the
§22.7 project superuser (project_admin or deployment owner/admin)."""
viewer = auth.require_user(request)
auth.require_project_readable(viewer, project_id)
if not auth.is_project_superuser(viewer, project_id):
raise HTTPException(403, "Only a project owner/admin can mark an entry reviewed")
row = db.conn().execute(
"SELECT state, unreviewed FROM cached_rfcs WHERE slug = ? AND project_id = ?",
(slug, project_id),
).fetchone()
if row is None:
raise HTTPException(404, "Not found")
if row["state"] != "active" or not row["unreviewed"]:
raise HTTPException(409, "Entry is not an unreviewed active entry")
try:
await bot.mark_entry_reviewed(
viewer.as_actor(),
org=config.gitea_org,
meta_repo=projects_mod.default_content_repo(config),
slug=slug,
reviewed_by=viewer.gitea_login,
reviewed_at=entry_mod.today(),
)
except GiteaError as e:
raise HTTPException(502, f"Gitea: {e.detail}")
await cache.refresh_meta_repo(config, gitea)
return {"ok": True}
# ---------------------------------------------------------------
# §7.3 / §9.3: pending ideas
# ---------------------------------------------------------------