feat: resolve Review items via the API (PATCH + bulk) + a Refresh button#492
Merged
Merged
Conversation
…ew tab
The Review tab is the one stateful surface with no way to act on it from
outside the WebView and no way to re-sync once loaded:
- The API could read review items (GET .../reviews) but not resolve them.
- Unlike the Lint tab (which has a re-run), the Review tab never re-read
review.json after the initial load, so an external change stayed
invisible until the project was reopened.
What this adds:
- PATCH /api/v1/projects/{id}/reviews/{reviewId} — partial update of one
item: body { resolved?, action? } (resolved defaults to true; pass
false to reopen). 404 on unknown id. Mark-only — it does not replicate
the WebView's resolve side effects (page creation, deep research).
- POST /api/v1/projects/{id}/reviews/resolve — bulk resolve: body
{ ids, action? }. Partial success is normal, so returns 200 with
{ resolved, notFound, count }. Reads review.json once and writes once.
- Both edit the RAW review.json array, preserving unsanitized fields on
write-back (verified by tests).
- PATCH is allowed through the method gate + CORS (tiny_http 0.12 parses
it into Method::Patch).
- Review tab gains a Refresh button (reload review.json from disk) — the
counterpart to lint's re-run and the way an external resolve surfaces.
- Endpoints list in Settings → API Server, README, and en/zh notes
updated. Rust unit tests cover PATCH + bulk paths.
Possible follow-ups (not here): live auto-reload via app.emit → WebView
listener; an mcp-server bulk-resolve tool mirroring llm_wiki_reviews.
3d6701b to
68800ee
Compare
AndrewDongminYoo
added a commit
to AndrewDongminYoo/llm_wiki
that referenced
this pull request
Jun 27, 2026
Review ids were a module-level incrementing counter (review-N). Any queue rebuild (e.g. shrinking the ingest queue) re-ran review generation, which re-numbered every review and — because addItems only deduped against *pending* items — discarded resolved state. A resolved review came back as a brand-new pending one, and the resolve API's id-targeting pointed at a number that no longer meant the same thing. Make the id content-derived and stable: - reviewIdFor(item) = FNV-1a(type + normalizeReviewTitle(title)). Same logical review → same id across regeneration, file moves, reloads. Deliberately excludes sourcePath (mutable — a rename would re-id the review, the very instability being removed). - addItems dedups on the stable id against ALL items incl. resolved (resolved wins, array fields merged) — this is the actual fix for resolved state being dropped on re-surface. - setItems migrates on load: remaps old counter ids to stable ids and collapses duplicates (resolved wins, union pages/queries, earliest createdAt). Idempotent — id computed from content, no version flag. - addItem dedups by identity too (no revive of a resolved item). Removes the counter machinery. Tests rewritten for stable-id behaviour, incl. acceptance cases: resolve-then-reingest keeps id+resolved, and two counter-id rows with identical content collapse to one resolved item on load. Makes PR nashsu#492's id-based resolve actually reliable.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The Review tab is the one stateful surface with no way to act on it from outside the WebView, and no way to re-sync once loaded:
GET .../reviews) but not resolve them.review.jsonafter the initial load — an external change stays invisible until the project is reopened.This matters for headless / scripted wiki maintenance: you can export the review queue but can't close items programmatically, and you can't see changes made outside the running app.
What this adds
1. Single-item update —
PATCH /api/v1/projects/{id}/reviews/{reviewId}Partial update of one item.
resolveddefaults totrue(the common case); passfalseto reopen.actionis an optional label. Mark-only — it deliberately does not replicate the WebView's resolve side effects (page creation, deep research, save-to-wiki). 404 on unknown id.PATCH (rather than a
POST .../resolveaction path) is the REST-correct shape for a resource state change. It needed the server's method gate + CORSAllow-Methodsopened to PATCH — verifiedtiny_http0.12 parsesPATCHintoMethod::Patchso the route actually fires.2. Bulk resolve —
POST /api/v1/projects/{id}/reviews/resolvePartial success is the norm for bulk, so this returns
200withresolved/notFoundrather than 404. Readsreview.jsononce and writes once (no per-id race window from looping the single-item path).Both write paths edit the raw
review.jsonarray rather than the sanitizing reader used byGET, so fields the sanitizer drops (e.g.internalSecret) survive write-back. Covered by tests.3. Refresh button on the Review tab
Reloads
review.jsonfrom disk. The missing counterpart to lint's re-run, and the intended way an external resolve surfaces: API writes disk → user hits Refresh → store reloads. No bespoke event channel for v1.Tests
Rust unit tests: PATCH resolve + unsanitized-field preservation, reopen via
resolved:false, unknown id, missing file; bulk resolve +notFoundreporting, missing file. (Also hardened the sharedtest_project_dir()helper with an atomic sequence — nanosecond-only temp dirs collided under parallelcargo test.)Docs / i18n
Endpoints list in Settings → API Server,
README.md, anden/zhlocale notes updated. Other locale READMEs left to the maintainer.Possible follow-ups (not in this PR)
Live auto-reload via a Rust
app.emit→ WebView listener if manual refresh proves insufficient; anmcp-serverbulk-resolve tool mirroring the existingllm_wiki_reviewstool.