# Decisions Append-only log. Format: date, title, 1-2 line rationale. Newest first. ## 2026-07-23 — Made Research Assistant and transliteration actually work properly (real bugs found by testing live), added real-time transliteration suggestions, then audited everything live via Playwright User asked for Research Assistant and transliteration to genuinely work, not just exist - tested both live against the deployed model rather than assuming, and found two real bugs. **Research Assistant relevance bug**: `search_entries()`'s scoring had no floor - any single query word matching anywhere was enough. Live-tested the UI's own default example query, "డెంగ్యూ వ్యాక్సిన్" (dengue vaccine) - it returned a completely unrelated Ebola article, solely because "vaccine" matched. Fixed by requiring a true majority of the query's distinct words (`len(query_words) // 2 + 1`) to appear in an article before it's even a scoring candidate - re-verified live: the broken case now returns nothing (honest), broad single/multi-word topical queries (Telangana, cricket, police, rain) still return solid matches. Also swapped the default example away from a disease name (inherently tied to today's news cycle, not evergreen) to "తెలంగాణ", and made the empty-result copy explain the live-feeds-only scope instead of reading as broken. **Known residual limitation, not fixed**: word-bag matching still can't tell "a Sharma who plays cricket" from "Rohit Sharma specifically" for a multi-word proper-noun query - true entity/phrase-awareness was judged over-engineering for this POC. **Transliteration was actually fine on the backend** (live-tested directly via HTTP: `repu`→రేపు, `cheppadu`→చెప్పాడు, all correct, ~1.7s) **but unreachable from Compose**, the primary writing surface - only Quick Tools had it. Added a "Transliterate selection" button to Compose's Grammar-on-Roman-script result (reusing the already-fixed `input_script` detection from the previous session's badge fix), and a genuinely new capability: **real-time suggestions while typing** - a debounced check on the Latin word under the caret (only when there's no active selection, so it doesn't collide with the Grammarly-style floating toolbar) calls the same `/api/transliterate` endpoint for just that one word and shows up to 3 candidates in a small popover, reusing the floating toolbar's existing mirror-div caret-positioning code rather than inventing new positioning logic. Click or Tab accepts; typing on, moving the caret, or Escape dismisses; a request-sequence counter discards stale responses if the user kept typing before a round-trip returned. **Deliberately word-at-a-time on a pause, not a true per-keystroke phonetic IME** - real IME libraries are a much bigger, riskier build than this POC needs, and this gets "suggestions while you type" without it. **Screenshot-audited all of it against live production via real Playwright browser sessions**, not just API calls - first hit a real blocker: the private Space's full browser navigation 404'd every time even though the same Bearer token worked fine for plain `requests` calls (an HF auth/routing quirk specific to `Sec-Fetch-Mode: navigate` requests on private Spaces, not something worth reverse-engineering further). Worked around it by testing against the local server first (confirms everything renders and degrades gracefully when transliteration is unavailable, as it correctly is on native Windows dev) - then the user independently switched the Space to public mid-session, which incidentally resolved the auth quirk entirely, letting the same Playwright script run directly against production. Confirmed live: Roman-script Grammar's honest badge, the new Transliterate-selection button (verified it actually replaced text, not just that the button appeared), Research Assistant's results, Submit-for-review's status pill, Editor Queue listing a submission with its travelled-along flags, Approve updating status, and the new real-time suggestion popover (typed "repu" → shows రేపు/రెపు/రేపూ → Tab accepts → re-triggers on the next word → a direct chip-click also works → typing an already-Telugu word correctly shows nothing). Zero console errors throughout. One real bug caught was in the *test script itself*, not the app - an early wait-condition matched a loading placeholder that happened to share a CSS class with the real result, so it passed instantly instead of waiting; fixed by waiting for a result-action button instead, which only appears once real data comes back. ## 2026-07-23 — Built a reporter→editor review-queue POC (closing the "no autonomous publishing" gap) + a full-product vision doc Asked, given a free hand to redesign from a newsroom-writer/editor lens, what a proper editorial tool would need. The dock has always stated "no autonomous publishing... editor approval required" with nothing behind it - built the actual mechanism as a real, working POC, then separately documented the gap to a full product using the PRD's existing Reporter/Editor/Senior-Editor persona table (`Rachana_News_PRD.md` lines 44-49) as the lens. Planned via Plan mode first (multi-file, several real architecture choices) - plan file: `~/.claude/plans/recursive-tinkering-twilight.md`. **Storage: an append-only JSONL event log (`data/processed/review_events.jsonl`), not a mutable JSON file.** Matches the project's only other server-side-persistence precedent (`usage_feedback.jsonl`) instead of inventing a new pattern. Current status per draft is derived by folding events in file order (latest wins) - this also eliminates any separate "dedup an open record" logic: resubmitting a draft after "changes requested" is just another `submitted` event, folding naturally makes it current. **Identity: reused the frontend's existing client-generated `draft_id`** (`ui/rachana-desk.html`'s `newDraftId()`) as the only key end to end, rather than inventing a second server-side record id - one less ID-mapping layer for no real payoff at this scale. **New `server/review.py`** (mirrors the `research.py`/`guardrails.py` "own module, imported into app.py" convention): `submit_draft`/`list_queue`/`get_status`/`approve`/`send_back`, a `threading.Lock` around read-fold-validate-append (approve/send-back need to check current status before acting - a stricter standard than `usage_feedback.jsonl`'s unlocked blind appends, justified because these are read-then-write, not blind appends). 5 new endpoints in `server/app.py`, none touching the model/GPU at all. **UI: a third peer view, "Editor Queue," next to Compose/Quick Tools** - not a real login-gated role, since this project deliberately has no auth/accounts anywhere. Reused the existing `switchView()` toggle pattern (additive, didn't touch the existing two-view logic) and the existing sidebar-list/main-pane/dock triad exactly as-is (queue items in the sidebar list, read-only detail in the main pane, Approve/Send-back actions in the dock) rather than inventing a new layout convention. Compose mode gained a "Submit for review" button + a status pill (polls `/api/review/status/{draft_id}`). Guardrail signals already produced elsewhere (`needs_review`, `facts_preserved: false`, unsupported-script no-ops) now accumulate onto the active draft as flags (`pushDraftFlag()`, capped at 20) and ride along on submit, so the editor sees them without regenerating anything. **Scope explicitly stops at human-in-the-loop** - submit/approve/send-back only. No real auth, no CMS write, no autonomous publishing - all flagged as full-product territory in the companion doc, not attempted here. This is not a re-litigation of the PRD's already-separately-scoped Phase 3 (agentic/CMS layer, PRD line 129) - deliberately stays on this side of it. **Full-product vision doc, both formats per explicit request**: `docs/editorial-workflow-vision.md` (source of truth, versioned) and a published Claude Artifact (https://claude.ai/code/artifact/cdd2a6a1-2ab8-4c1f-af37-25393a57d934) from the same content - covers real RBAC, the CMS-write boundary, a real database, multi-user editing/locking, notifications, audit trail/versioning (and its convergence with the existing `/api/feedback` flywheel data), per-publication style config, ops analytics (distinct from but related to the PRD's existing training-flywheel-convergence metric, line 150), and an explicit note that this is a different system from Rachana Data Studio (PRD line 14/186 - that tool curates training data, this queue moves live drafts). **Verification, and its real limit**: no GPU or browser access this session. Verified what's actually checkable that way - `server/review.py`'s logic end-to-end via a standalone scratch script (submit→queue→approve, and submit→queue→send_back→resubmit→queue, scratch event file so the real data file was never touched), `server.app` import-safety (confirms `load_model()` only fires on the FastAPI startup event, not at import time, so route registration is checkable without loading the 4B model), a Node syntax check of the updated `