## Context This change addresses unresolved regressions introduced by recent UX and content pipeline updates. The affected areas cross frontend interaction patterns (`frontend/index.html`), policy content delivery (`backend/main.py`), translation generation (`backend/news_service.py`), and admin image maintenance operations (`backend/cli.py`). Current behavior still allows low-quality translations and repeated or weakly related refetched images, and policy links still rely on full-page navigation. Key constraints: - Maintain existing OpenSpec capability contracts where possible and ship mostly as requirement deltas. - Preserve permalink identity for machine/admin targeting, while removing redundant user-facing permalink chrome on hero. - Keep deterministic fallback behavior (AI-themed image fallback and English fallback for failed translations). ## Goals / Non-Goals **Goals:** - Deliver modal-based Terms and Attribution access with keyboard-safe behavior. - Remove visible hero permalink affordance while preserving internal permalink targeting. - Improve Tamil/Malayalam hero readability on desktop and mobile. - Switch copy and back-to-top controls to icon-first interaction while preserving accessibility labels. - Add translation quality validation gates (wrong-language/gibberish rejection and deterministic fallback). - Extend refetch-images to support permalink targeting and enforce alternative relevant image selection (non-repeat, relevance/safety filters, AI fallback when uncertain). **Non-Goals:** - Replacing the existing rendering framework or routing model. - Introducing new external image providers in this change. - Reworking full article ingestion architecture beyond targeted translation/image guardrails. ## Decisions ### Decision 1: Policy disclosure moves to modal-first UI with deep-link-safe state Use in-page modals for Terms and Attribution content while supporting deterministic open/close state from URL params or equivalent state synchronization. Alternatives considered: - Keep route pages only: rejected due to UX friction and context switch from main feed. - Embed truncated inline footer text: rejected due to disclosure readability and legal clarity risks. ### Decision 2: Hero permalink identity remains internal, not a primary visual affordance Retain permalink generation/parsing helpers for deep links and admin targeting, but remove visible hero permalink action from hero chrome. Alternatives considered: - Remove permalink behavior entirely: rejected because admin targeting and deep-link tooling need stable identifiers. - Keep visible permalink: rejected because current hero action density and readability are degraded. ### Decision 3: Locale-aware hero readability profile for Tamil/Malayalam Apply stronger readability guardrails for non-English hero text: increased contrast treatment, safer line-wrapping, language-specific typography tuning. Alternatives considered: - Single style for all languages: rejected; longer glyph clusters and script density degrade legibility. - Separate locale-specific templates: rejected as too heavy for this stabilization change. ### Decision 4: Icon-only controls must remain explicitly accessible Use icon-based copy and back-to-top controls with accessible names, keyboard operation, and stable tap targets. Alternatives considered: - Text-only controls: rejected by updated UX requirement. - Icon-only without explicit labels: rejected due to WCAG and discoverability concerns. ### Decision 5: Translation quality gate before persistence/serving Add post-generation validation for expected language/script sanity and basic gibberish detection. If validation fails, mark translation unavailable and serve deterministic English fallback. Alternatives considered: - Blindly accept model output: rejected due to current wrong-language/gibberish incidents. - Human moderation for all translations: rejected for runtime latency and operational overhead. ### Decision 6: Refetch image selection becomes candidate-based and non-repeat For refetch operations, generate contextual query candidates, filter against relevance/safety constraints, reject current/previously used image matches for the same article, and fall back to AI-themed image when confidence is low or candidates are exhausted. Alternatives considered: - First-provider-first-image approach: rejected because it often repeats or returns weakly related imagery. - Hard blocklist only: rejected; insufficient for relevance and dedupe guarantees. ### Decision 7: Admin refetch supports permalink-targeted execution Extend admin command contract to accept a permalink input, resolve to article identity, and run the same queue/refetch pipeline with targeted scope. Alternatives considered: - Keep limit-based batch-only refetch: rejected; operators need deterministic single-article correction path. - Add separate ad hoc script: rejected due to fragmented operational surface. ## Risks / Trade-offs - **[Risk] Modal policy flow may regress keyboard/focus behavior** -> Mitigation: explicit focus trap, escape-close, and focus-return requirements. - **[Risk] Translation validation may over-reject valid short outputs** -> Mitigation: bounded heuristic thresholds plus deterministic English fallback and logging for tuning. - **[Risk] Stronger image filtering can reduce image diversity** -> Mitigation: multi-candidate ranking and fallback to AI-themed image instead of unrelated imagery. - **[Risk] Permalink-targeted admin flow may fail on malformed or stale links** -> Mitigation: strict permalink parser and clear operator error summaries. ## Migration Plan 1. Introduce modal policy behavior and hero/readability/icon control updates in frontend. 2. Add translation quality gate logic and fallback outcomes in backend translation path. 3. Extend image refetch pipeline with candidate ranking, dedupe, relevance/safety filters, and fallback. 4. Add permalink-targeted admin refetch argument and resolution path. 5. Validate through existing/manual verification paths and update OpenSpec tasks. Rollback: - Revert p16 frontend modal/icon/readability changes. - Revert translation gate checks to prior translation acceptance path. - Revert permalink-targeted and dedupe-aware refetch logic to prior batch refetch behavior. ## Open Questions - Should policy modals update canonical URL state (`?modal=terms`) for shareable deep links, or stay ephemeral-only? - What minimum confidence/sanity threshold should trigger translation rejection for short headlines? - Should image dedupe history persist only per article current/previous image, or maintain wider history window?