[IMP] viin_brain: P7 H4 fix + WI-4 universal injector wire (round 3 sprint 3)
P7 H4 — content_html sanitize_attributes=False
- Brain embed blocks use data-snapshot-id, data-snapshot-kind,
data-block-anchor, data-is-stale, data-mode attributes that are NOT
in Odoo's safe_attrs whitelist (odoo/tools/mail.py:44-55).
- With default sanitize_attributes=True, html_sanitize() with
safe_attrs_only=True strips all data-snapshot-* on every WRITE,
breaking embed chrome render after save→reload.
- Fix: explicit sanitize_attributes=False preserves data-* while
sanitize_tags=True (implicit) still blocks script/iframe/object
tags so XSS posture intact.
- Verified via JSON-RPC ORM write of 950-byte embed HTML →
read back returns 906 bytes with data-snapshot-kind +
o_brain_snapshot_chrome--snapshot class preserved.
WI-4 (ADR-001) Universal Brain sidebar injection — restored
- Original __manifest__.py:270-271 declares "WI-4 (ADR-001) — universal
hook injection for mail.thread descendants" but stage 1 (function
definition) shipped without stage 2 (caller wiring) per git
archaeology of commit 26f2190.
- Per ETHOS §4.1.1 boil-the-lake: the function intent was clear, only
the wiring was missing; restore (wire) rather than delete.
- Rewrote brain_form_sidebar_universal.js to patch FormCompiler
.prototype.compile() (not FormController) and mutate source arch
this.templates[key] BEFORE super.compile(). Detection: presence of
<div class="oe_chatter"> proxies for mail.thread descendant (= has
brain_page_count via mail_thread.py inheritance). Guards against
double-inject (existing .o_brain_form_sidebar_hook from bridge XML)
+ WeakSet on processed templates.
- Verified on 2 models with NO bridge XML:
* project.project (Office Design ID=1) — BRAIN rail sidebar auto-renders
* crm.team (Sales ID=1) — BRAIN rail sidebar auto-renders
- Existing 5 bridges (res.partner, crm.lead, hr.employee, project.task,
sale.order) continue to work unchanged — hookAlreadyPresent guard
prevents double injection.
NOTE — separate page_editor render race exists: bodyRef.el.innerHTML
stays empty despite state.page.content_html containing 906 bytes after
ORM write. JSON-RPC verifies content delivery; OWL render does not
materialize. Investigation deferred — not introduced by this commit;
unrelated to H4 sanitization fix.
NOTE — universal injector stat button (Brain Pages count) appears on
forms with <div name="button_box"> but the inserted <field name=
"brain_page_count" widget="statinfo"> child does not visually render in
project.project capture. Sidebar (primary deliverable) works on both
test models. Stat button compilation pipeline interaction worth
follow-up; sidebar functionality unblocked.
AC-1 hex grep PASS, brain_v3_ci_check.py --strict 8 PASS / 1 pre-existing
WARN (rule2 contenteditable baseline = 14 unchanged).
Refs PR #27.
| Subbuilds | Name | State | Detail |
|---|---|---|---|
| Build 381500 | Check the quality with Pylint: from test_lint to test_pylint 19m | Failed | Log |
| Build 381501 | At install tests: from account to website_twitter 135m | Succeed | Log |
| Build 381502 | Post install tests: step install all modules 69m | Succeed | Log |
| Build 381503 | Post install tests: from account to payment_alipay 163m | Succeed | Log |
| Build 381504 | Post install tests: from payment_aps to test_viin_web_cohort 199m | Succeed | Log |
| Build 381505 | Post install tests: from test_website to to_website_apps_store 79m | Succeed | Log |
| Build 381506 | Post install tests: from to_website_apps_store_loc to viin_estimate_sale 88m | Succeed | Log |
| Build 381507 | Post install tests: from viin_event_checkin_website to viin_project_gantt 44m | Succeed | Log |
| Build 381508 | Post install tests: from viin_project_progress to website_twitter 54m | Succeed | Log |
| Build 381509 | Test install all modules without demo data: from account to website_twitter 54m | Succeed | Log |
| Create Date | Level | Message |
|---|---|---|
| 05/03/2026 21:30:22 | INFO |
Updated repository Viindoo-odoo
|
| 05/03/2026 21:30:22 | INFO |
Updated repository Viindoo-tvtmaaddons
|
| 05/03/2026 21:30:22 | INFO |
Updated repository Viindoo-erponline-enterprise
|
| 05/03/2026 21:30:22 | INFO |
Updated repository Viindoo-branding
|
| 05/03/2026 21:30:22 | INFO |
Cloned repository Viindoo-ai
|
| 05/03/2026 21:50:12 | ERROR |
Subbuild # 381500:
odoo.addons.test_pylint.tests.test_eslint: FAIL: TestESLint.test_eslint
Traceback (most recent call last):
File "/data/build/Viindoo-tvtmaaddons-17.0/test_pylint/tests/test_eslint.py", line 67, in test_eslint
self.assertEqual(process.returncode, 0, msg=f"""
AssertionError: 1 != 0 :
stdout:
/data/build/rb-6d7ec7c-221701/viin_brain/static/src/components/share_dialog/share_dialog.js
56:35 error Replace `⏎············?·_t("Share·%s",·this.state.pageName)⏎···········` with `·?·_t("Share·%s",·this.state.pageName)` prettier/prettier
/data/build/rb-6d7ec7c-221701/viin_brain/static/src/views/form/brain_form_sidebar_universal.js
64:53 error Replace `'div.oe_chatter,·chatter'` with `"div.oe_chatter,·chatter"` prettier/prettier
69:40 error Replace `⏎········archEl.querySelector("div.o_brain_form_sidebar_hook")⏎····` with `archEl.querySelector("div.o_brain_form_sidebar_hook")` prettier/prettier
/data/build/rb-6d7ec7c-221701/viin_brain/static/tests/seams/v2_extension_seams_tests.js
34:19 error Replace `⏎············mount.classList.contains("d-none"),⏎············"canvas·mount·is·hidden·by·default·(d-none)"⏎········` with `mount.classList.contains("d-none"),·"canvas·mount·is·hidden·by·default·(d-none)"` prettier/prettier
41:13 error Replace `"canvas·mount·declares·t-ref=\"canvasMount\""` with `'canvas·mount·declares·t-ref="canvasMount"'` prettier/prettier
56:16 error Replace `"Seam·4:·ShareDialog·template·has·empty·<t·t-slot=\"collabSection\"/>"` with `'Seam·4:·ShareDialog·template·has·empty·<t·t-slot="collabSection"/>'` prettier/prettier
60:45 error Replace `"exactly·one·<t·t-slot=\"collabSection\"/>·declared"` with `'exactly·one·<t·t-slot="collabSection"/>·declared'` prettier/prettier
83:16 error Replace `"Seam·6:·AI·block·HTML·output·carries·additive·o_brain_ai_state_<status>·class",` with `⏎········"Seam·6:·AI·block·HTML·output·carries·additive·o_brain_ai_state_<status>·class",⏎·······` prettier/prettier
84:1 error Replace `········` with `············` prettier/prettier
85:9 error Insert `····` prettier/prettier
86:1 error Replace `········` with `············` prettier/prettier
87:9 error Insert `····` prettier/prettier
88:1 error Insert `····` prettier/prettier
89:9 error Insert `····` prettier/prettier
90:1 error Insert `····` prettier/prettier
91:1 error Replace `········` with `············` prettier/prettier
92:1 error Insert `····` prettier/prettier
93:1 error Replace `············` with `················` prettier/prettier
94:9 error Insert `····` prettier/prettier
95:1 error Replace `········` with `············` prettier/prettier
96:9 error Insert `····` prettier/prettier
97:1 error Insert `····` prettier/prettier
98:13 error Insert `····` prettier/prettier
99:9 error Insert `····` prettier/prettier
100:1 error Replace `····}` with `········}⏎····` prettier/prettier
/data/build/rb-6d7ec7c-221701/viin_brain/static/tests/tours/brain_ux_batch_a_tour.js
126:21 error Insert `⏎···············` prettier/prettier
✖ 26 problems (26 errors, 0 warnings)
26 errors and 0 warnings potentially fixable with the `--fix` option.
Perhaps you might benefit from installing the tooling found at:
https://github.com/odoo/odoo/wiki/Javascript-coding-guidelines#use-a-linter
stderr:
|
| 05/03/2026 21:50:12 | ERROR |
Subbuild # 381500:
odoo.modules.loading: Module test_pylint: 1 failures, 0 errors of 7 tests
|
| 05/03/2026 21:50:12 | ERROR |
Subbuild # 381500:
odoo.modules.loading: At least one test failed when loading the modules.
|
| 05/03/2026 21:50:12 | ERROR |
Subbuild # 381500:
odoo.tests.result: 1 failed, 0 error(s) of 24 tests when loading database 'rb-6d7ec7c-221701-381500'
|