Branch
[FIX] viin_ai_website: blog_posts_by_author extract translatable author name (Runbot res.partner.name jsonb) res.partner.name is a translatable JSONB column when to_partner_multilang is installed (Runbot), so the raw 'rp.name AS author' returned {'en_US': ...} instead of a plain author name. Apply the shipped SSOT idiom COALESCE(to_jsonb(rp.name)->>%(lang)s, to_jsonb(rp.name)->>'en_US', rp.name::text) used by all other connectors; execute() injects params['lang']. Verified GREEN with res.partner.name jsonb.
[DOC] roadmap: reconcile Wave-B module counts (24 planned, 8 net-new) Post-review reconciliation of three doc-drift flags surfaced during Phase 7.4 Wave-B review: - Section 2 header said '(20 module)' but the v1 planning table now enumerates 24 module names (Wave-A/B rows appended without a bump) -> corrected to 24. - Section 4 dependency-graph box said 'Wave-A/B/C (8 module)' vs 9 module names in section 3 -> relabelled '(8 new)': stock is a pre-existing Phase-3.7 stub being extended, so 8 are net-new (matches the doc's established stub-dagger convention). ASCII box interior width preserved. - viin_ai_helpdesk 'depends viin_ai_chat' claim: investigated and confirmed the manifest ([viin_ai_agent, viin_helpdesk]) is correct - no chat symbol anywhere in the module, matches the connector norm (only viin_ai_crm pulls chat); the AGENTS.md text was already fixed. No code change - adding an unused dependency would be wrong.
[FIX] viin_brain_account_reports: keep backlink scroll on report embed The live-mount embed_report anchor dropped data-block-anchor, silently breaking the Backlink panel's click-to-scroll/highlight for account-report embeds (the panel's existing click handler queries a compound selector requiring that attribute on the embed block). Restored the attribute on the anchor payload and re-rendered it onto the embed component's own template root so the selector matches again. Added regression coverage in the existing embed-report tour: insert the embed, navigate away and back to force a real reload/remount, open the Backlink panel's Embeds tab, click the row, and assert the block highlights. Signed-off-by: David Tran <davidtran.hp@gmail.com>
[FIX] viin_brain: let a Brain Admin review AI content Approving or rejecting AI content required an explicit editor or admin member row on the vault, so a Brain Admin who had never been added to a vault was refused - even though the record rules already grant that group unconditional read and write on every page and vault, and even though the same group may already moderate comments there. Treat the group as a bypass, exactly as comment moderation does. This grants no power the record rules did not already grant; it removes an inconsistency. An explicit member row keeps its meaning, and a viewer, a commenter or a plain non-member is still refused.
[FIX] viin_ai_brain: never discard AI content silently When the caret was not inside the editable - which happens whenever the page does not end in a paragraph - the editor refused the insert and reported nothing. The dialog closed anyway and the generated text was lost: no exception, no notification, nothing in the document or the database. Place the caret at the end of the editable before inserting, and treat anything other than a real insert - a refusal, or a step the editor rolled back - as a failure. On failure the dialog stays open, the generated text remains available to copy, and an error is shown. Add a tour that drives the page shape which triggered the loss and asserts the generated content reaches the database, and assert server-side that approving an AI block really persists the review.
[FIX] viin_brain: rename property handle clearing pylint W0622 builtin-shadow The flake8 cleanup lowercased the env-model handle `Property` to `property` to satisfy N806, which shadows the Python builtin and trips the separate test_pylint gate (W0622 redefining-builtin) in four viin_brain scopes. Rename the handle to `property_model` - lowercase (keeps N806 clean) and not a builtin (clears W0622). Pure rename, behaviour unchanged; both lint gates and the viin_brain test suite pass.
[IMP] repo: resolve flake8 gate across all modules Clean the full test_flake8 gate backlog (913 violations, 42 modules) to 0: rename-all pep8-naming (N806/N802/N803 lowercased, incl. the `Model = self.env[...]` handles and unittest-mirror helpers + their call-sites), line-wraps (B950 <=96), exception chaining (B904 `from err`), f-string conversion (B907), W503/W504 by join/extract (never move-operator), B905 `strict=True`, B023 loop-var hoist, and `# noqa: B018` on the manifest literal dicts (Odoo requires a literal dict). Behavior-preserving: each module's full non-tour test suite verified green after its renames.
[FIX] viin_ai_rag: refuse keyless/misconfigured OCR as a clean config-gap A vision-capable but keyless/misconfigured provider is now refused at pre-flight (status=error + actionable message + INFO log, no vendor call) instead of failing deep with a WARNING+traceback; genuine vendor failures still WARNING. Adds a behavior test and drops the now-unnecessary log mutes.
[FIX] viin_ai_rag: mute expected OCR-failure log noise in tests The no-key negative-path tests (deliberate provider failure, audio-refused) and the FSM/embedding tests (which call the global _process_pending cron sweep that incidentally OCRs the committed demo image source with no key) emitted expected WARNING+traceback lines from viin.ai.media.process into the CI log. Wrap only those expected emissions in mute_logger on the exact logger (odoo.addons.viin_ai_rag.models.media): the model-side warning stays (prod must see OCR failures) and every assertion is unchanged. Test-only; no model/production change. Verified fresh-DB demo-on: viin_ai_rag 0 failed 0 error, target log noise gone.