|
|
|
merged
[IMP] viin_brain: Brain editor UI/UX polish (#44)
|
[IMP] viin_brain: Brain editor UI/UX polish - white canvas, AA contrast, inline icon, symmetric padding, mobile collapse, declutter chrome, a11y labels (#44)
Addresses issue #44 (P16 follow-up). Includes code-review cleanup: shadow token,
deduped chip styling, t-foreach overflow menu, centralised property a11y attrs,
structural header layout, and label-for only on input-bearing property types.
|
Failed
|
|
|
|
|
|
|
|
merged
[IMP] viin_brain: Brain editor UI/UX polish (#44)
|
[IMP] viin_brain: Brain editor UI/UX polish - white canvas, AA contrast, inline icon, symmetric padding, mobile collapse, declutter chrome, a11y labels (#44)
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[IMP] P16: Editor Extension Framework - modular Brain/AI editor (OdooEditor migration)
|
[DOC] docs,AGENTS: ADR-010 editor framework + reconcile docs with shipped P16
ADR-010 Editor Extension Framework + WI-007/WI-008; reconcile brain/ai
architecture, ai-bridge, ui-design, conventions, roadmap and decision index
with the shipped code; AGENTS module map adds viin_ai_editor + engine desc.
|
Killed
|
|
|
|
|
|
|
|
merged
[IMP] P16: Editor Extension Framework - modular Brain/AI editor (OdooEditor migration)
|
[IMP] P16 review supplement: fix review findings + Brain editor UX polish (#2)
* [FIX] viin_ai_ops: null-guard ListRenderer.setDefaultColumnWidths against intermittent DOM race
Intermittent TypeError "Cannot read properties of null (reading 'style')"
in core Odoo 17 ListRenderer.setDefaultColumnWidths() surfaces on the
Action Proposals list when the view re-renders with zero records (isEmpty
toggle races thead commit to DOM, querySelector returns null).
Adds a defensive monkey-patch in viin_ai_ops that overrides the method
with an identical but null-safe implementation: if headerEl is null the
column iteration step is skipped instead of crashing. This matches the
upstream fix strategy applied in Odoo 18 via useMagicColumnWidths.
BACKPORT FROM: Odoo 17 core list_renderer.js (upstream fixed in 18.0)
REMOVE WHEN UPGRADING TO: 18.0
* [FIX] viin_ai_editor: surface UserError message in AI block error state
Odoo 17 RPCError shape wraps the real UserError text in err.data.message
while err.message is always the opaque "Odoo Server Error" wrapper.
The catch in AiBlock._generate now reads err.data.message first so users
see the actionable UserError (e.g. "No AI agent configured") instead of
the unhelpful wrapper.
- ai_block.js: prioritise err.data.message || err.message || fallback _t()
- conventions.md: add JSON-RPC error handling convention (SSOT: chat_panel.js)
- test_ai_block_state_machine.js: add 2 cases covering RPCError shape and
plain-Error fallback
* [IMP] viin_web_editor,viin_brain,viin_ai_brain: dedup powerbox native commands (WI-007)
Introduce a generic suppress-native registry in viin_web_editor so downstream
modules can declaratively remove native OdooEditor/Wysiwyg powerbox commands that
they replace, without the engine hardcoding any consumer-specific names.
Changes:
- viin_web_editor/powerbox_registry.js: export new registry
viin_web_editor.powerbox_suppress_native with suppressor shape
{matcher, contextGate?}
- viin_web_editor/wysiwyg_patch.js: filter upCommands (native) through all
registered suppressors before concatenating extra commands; exceptions inside
suppressors are caught per-entry (fail-safe)
- viin_brain/slash_commands.js: register five suppressors (headings/quote/code/
bullet/numbered, all context-gated to .o_brain_editor_body_edit); fix
brain_bullet_list and brain_numbered_list to use toggleList (identical to
native, preserves toggle-off semantics)
- viin_ai_brain/powerbox_commands.js: register suppressor for native ChatGPT
(fa-magic) gated on bridge.available=true
- docs/decisions/wi-007-powerbox-suppress-native.md: new append-only WI,
Refines ADR-010
* [FIX] viin_ai_brain: multi-company filter agent lookup (B1)
Extract the Knowledge Base Assistant agent lookup into a single SSOT
helper `viin_ai_brain/controllers/common.py::find_kba_agent()` that
enforces `company_id in [env.company.id, False]` on every search.
Before this fix wysiwyg.py `_get_agent()` searched by name only
with no company filter, leaking cross-tenant agent records. ai_block.py
already had the filter but in a local function. Both controllers now
import `find_kba_agent` from `controllers/common.py`, eliminating the
duplicate logic and the security gap.
* [IMP] viin_ai_brain: topic-based agent lookup + CTA (MED-2)
Update `controllers/common.py::find_kba_agent` to resolve the KBA agent
via the `viin_ai_brain.topic_brain_assistant` topic xmlid instead of the
display name. This makes lookup name-independent: renaming the agent for
i18n or branding purposes does not break any controller path.
When no agent with the topic is found, raises UserError with an
actionable CTA pointing to AI > Configuration > Agents
(viin_ai_agent.action_viin_ai_agent).
Frontend changes:
- ai_bridge_service.js: pass topicXmlId instead of hard-coded topicName
for ChatPanel so the chat panel can resolve the topic server-side.
- ai_editor_backend_service.js: remove "Knowledge Base Assistant"
hard-coded fallback for agent_name; server is the SSOT (empty string
sentinel on RPC success avoids masking real lookup failures).
Tests (Odoo native TransactionCase, @tagged post_install):
- TestKbaAgentLookupMultiCompany: B1 isolation - company A env cannot
resolve company B's KBA agent; global agents (company_id=False) are
visible from any company; missing-company agent raises UserError+CTA.
- TestKbaAgentTopicLookup: rename-safe lookup (MED-2); detaching topic
raises UserError+CTA; wysiwyg endpoint surfaces CTA end-to-end.
- Updated TestBrainBridgeCommon to attach the KBA topic to the fixture
agent, and updated _ensure_brain_agent in tour tests likewise.
- Updated test_commit_attribution_raises_when_no_kba_agent: detach topic
(not rename) to simulate missing agent under topic-based lookup.
Verified: 0 failed, 0 error of 57 tests (wave_wid_test ephemeral DB).
* [IMP] viin_brain: fix AI badge wording to reflect installed-not-ready semantics
Badge text changed from "AI ready" to "AI features available" and tooltip
updated to "AI features are installed - configure a provider & agent to use
them." to avoid implying the AI is operational before a provider and agent
are configured (Option B wording-only fix, WI-E).
* [DOC] docs: WI-008 PR #43 deferred follow-ups tracking + roadmap corrections
Create docs/decisions/wi-008-pr43-deferred-followups.md (append-only WI):
- WI-F (MED-3): Provider Test Connection button -> viin_ai_base Layer 0 backlog
- WI-G: L2 active-default policy (needs ADR-011), L3 dashboard CTA (W12 P-OPS-4),
L4 admin empty-state guidance (Phase 5/onboarding)
- WI-007-onboarding: Phase 5 ADR + wizard (M4 gate); seed agent stub explicitly
excluded (model_id required+restrict constraint); CTA error already done in WI-D2
- B2: CRDT pitch guidance - interim relay is NOT OT real-time collab (ADR-002 intact)
- B3: SQL template count corrected to 15 (roadmap said 11 pre-viin_ai_crm baseline;
review cited 13; correct per CEO 2026-06-06 = 15)
Update docs/roadmap.md:
- Fix SQL template count 11 -> 15 with WI-008 B3 reference
- Add Layer 0 backlog note after M1 gate (WI-F, WI-G L2)
- Add AI Onboarding wizard item to Phase 5 W16-W19 (gated M4)
- Add WI-005, WI-006, WI-008 rows to ADR/WI cross-reference table
- Mark ADR-010 DONE 2026-06-04; add ADR-011 candidate for active-default policy
Update docs/decisions/README.md:
- Add wi-008 row to WI index
- Update date to 2026-06-06
* [FIX] viin_ai_brain,viin_ai_editor,viin_brain,viin_web_editor: eslint prettier formatting on WI changes
* [REF] viin_ai_brain: Command.* in WI-D tests + drop dead chat prop (review LOW)
* [FIX] viin_brain: powerbox suppressor context-gate timing (Phase V blocker)
Root cause: _getPowerboxOptions() runs synchronously inside startEdition().
The DOM markers (.o_brain_editor_body_edit[data-page-id]) are applied in the
startWysiwyg callback AFTER startEdition() resolves, so _isBrainContext()
always returned false during powerbox build - all 5 suppress-native rules
were no-ops, causing 7 native commands to appear twice (Heading 1/2/3,
Text, Numbered list, Quote, Link).
Fix: set wysiwyg.__brainPageContext = true on the wysiwyg instance BEFORE
calling startEdition() in page_editor.js. Update _isBrainContext() to check
this early-signal flag (Tier 1) before falling back to the DOM closest()
check (Tier 2). Flag is Brain-PageEditor-only; mail/website editors are
unaffected.
Bonus: deduplicate powerbox "AI" category registrations. Three WI phases
(WI-A1, WI-B3, WI-007) each registered a separate category key all named
"AI", producing three UI separators. Guard each registration behind a check
for all known AI category keys so only one "AI" category appears in the
powerbox when both viin_ai_editor and viin_ai_brain are installed.
* [FIX] viin_web_editor,viin_brain,viin_ai_brain: suppress native powerbox at merge point + Text/Link + AI category (Phase V)
- wysiwyg_patch.js: add Tier 2 powerboxFilters injection to suppress OdooEditor-level
commands (Heading 1/2/3, Bulleted list, Numbered list, Text) that bypass
_getPowerboxOptions and cannot be caught by Tier 1 alone. Use _viinCustom sentinel
to distinguish consumer commands from native ones so suppressors never accidentally
remove Brain's own commands that share the same fontawesome icon.
- slash_commands.js: add viin_brain.suppress_text (fa-paragraph) and
viin_brain.suppress_link (fa-link) suppressors for the native Text and
Link/Button commands that brain_paragraph and brain_external_link replace.
- brain_editor_registry_commands.js: document that key "brain_ai" is intentional
(viin_ai_editor uses .add("AI") unconditionally; using the same key here would
throw on duplicate). Powerbox.open() deduplicates categories by name at runtime.
- powerbox_commands.js (viin_ai_brain): same documentation for key "brain_ai_write".
- wi-007-powerbox-suppress-native.md: update to describe two-tier architecture,
add Text/Link suppressor table rows, add AI category safety analysis.
* [FIX] viin_ai_brain: group AI powerbox commands under single category (Phase V)
Root cause: _t("AI") at module-evaluation time returns LazyTranslatedString
*objects* (extends String). Different _t() calls across modules produce
different object instances never === each other. Powerbox._groupCommands()
matches command.category === category.name with strict equality, so the match
always fails for cross-module LazyTranslatedString instances. Each AI command
fell into the remaining-categories path and got its own "AI" header, rendering
3 separate "AI" separators instead of one.
Fix: use plain string literal "AI" (not _t("AI")) for both category name and
command category across all 3 AI-contributing modules. Plain strings compare
correctly with ===. "AI" is locale-invariant so no i18n value is lost.
All 3 modules now register under canonical key "AI":
- viin_ai_editor: add("AI", ..., {force:true}) - prevents DuplicatedKeyError
when brain/ai_brain registers first
- viin_ai_brain, viin_brain: guarded by !contains("AI") - avoid overwrite
warning when viin_ai_editor loads first
WI-007 decision doc updated to document the real root cause and the fix.
* [FIX] viin_brain: vault page resequence no longer clears the tree (P16 review)
Root cause: _reorderPage called _invalidateParent(null) after the ORM writes.
_invalidateParent deletes childrenByParent.get(parentId) then re-fetches only
when parentId is in expandedIds. The root parent (null) is never in
expandedIds, so the root list was permanently deleted and the sidebar showed
"No page in this vault" until the user navigated away or reloaded the page.
Server data was saved correctly — only the UI was affected.
Fix: optimistic reorder. Swap sequence values directly in the in-memory
pageCache before awaiting the ORM writes, re-sort the affected parent's child-id
array in childrenByParent using the updated sequences, then bump the flatRows
cache. The list is never cleared between the drop event and the server
response. A try/catch around the ORM writes restores consistency via
_invalidateParent + an explicit _loadRoots call only in the error path.
A new QUnit unit test (no-mount harness, same pattern as the WI-8 suite) pins
the regression: after _reorderPage the root list must still exist, contain both
page ids, and be re-sorted by the new sequences.
* [IMP] viin_brain: Brain editor UX polish - editor border, powerbox default style, placeholder hints, clickable breadcrumb (P16 review)
Issue 2 (editor border): fix specificity loss in .o_brain_editor_body_edit
note-editable border override. Add compound selector &.note-editable + !important
backstop so Brain wysiwyg canvas never shows the textarea-like border from
web_editor.backend_assets_wysiwyg regardless of stylesheet load order.
Issue 3 (powerbox style): remove Brain-specific powerbox SCSS overrides
(_powerbox.scss). The custom grey background / washed-out description text
diverged from Odoo default. Revert to Odoo default white-background crisp
powerbox. Command injection via wysiwyg_patch.js / powerboxCommandsRegistry
is completely unaffected - only visual overrides removed.
Issue 4 (placeholder hint): wire OdooEditor native placeholder option in
_buildWysiwygOptions() so empty pages show "Type / for commands, [[ to link
a page, @@ to mention" hint via .oe-hint::before. Also add
o_brain_editor_placeholder class to the editable in startWysiwyg() as CSS
fallback for :empty:not(:focus)::before defined in _app_shell.scss.
Issue 5 (breadcrumb + header lean): make vault breadcrumb segment clickable
(role=button, tabindex=0, cursor pointer, hover highlight, onBreadcrumbVaultClick
handler calls onVaultSelect prop bound from BrainApp.onVaultSelect). Tighten
header layout: margin-bottom 16px->10px, breadcrumb mb 8px->4px, sep padding
0 6px->0 4px, title font 28px->26px, icon 24px->22px, padding 4px->2px 0.
* [FIX] viin_brain: show editor placeholder hint (CSS cascade fix, P16 review)
Drag-handle rule `> p::before { content: "⋮⋮" }` was overriding
`.oe-hint::before { content: attr(placeholder) }` due to equal specificity
and source order. Fix: narrow the drag-handle selector to
`> p:not(.oe-hint)` so the hint paragraph is excluded from the drag-handle
block entirely. Also add explicit `content: attr(placeholder)` to the
`.oe-hint::before` rule to ensure it always wins regardless of cascade order,
and bump opacity from 0.38 to 0.45 for better readability.
* [FIX] viin_brain: eslint prettier formatting on P16 UI fixes
* [FIX] viin_brain,viin_ai_brain: scope Brain powerbox commands to Brain editor context (P16 review)
Brain-specific powerbox commands were registered globally and leaked into
every editor that loads the powerbox patch (mail templates, CRM description,
website builder, etc.), causing UI noise and potential runtime errors when
Brain-specific callbacks (wikilink, embed, database, record mention) fire
outside a Brain page.
Fix: add isDisabled(wysiwyg) => !isBrainContext(wysiwyg) to all Brain-specific
commands so they are hidden by the Powerbox engine outside a Brain editor.
isBrainContext uses a two-tier check (wysiwyg.__brainPageContext early-signal
flag + DOM .o_brain_editor_body_edit[data-page-id] fallback) identical to the
existing WI-C suppress-native contextGate.
SSOT: extract shared predicate to viin_brain/static/src/utils/brain_context.js
(web.assets_backend eager bundle, no @web_editor dep). Three consumers:
- slash_commands.js (wysiwyg lazy bundle) - all 14 Brain commands gated
- brain_editor_registry_commands.js (eager) - brain_ai_block stub gated
- viin_ai_brain/registries/powerbox_commands.js - brain_ai_write + brain_ai_chat
now require both Brain context AND bridge.available
brain_ai_write / brain_ai_chat: previous _isDisabledWhenNoBridge only checked
bridge availability; these commands also leaked into non-Brain editors when
bridge was active. New _isDisabledWhenNoBridgeOrOutsideBrain combines both.
Not gated: viin_ai_editor ai_block command is a general AI editor feature
(not Brain-specific); its isDisabled already gates on backend.available.
ai_block_conflict_dedup.js WI-C dedup mechanism preserved unchanged.
WI-007 suppress-native contextGate logic preserved unchanged.
* [FIX] viin_ai_editor: eslint prettier trailing comma
* [FIX] viin_ai_brain: address Codex review on PR #43 supplement
Two P2 findings from the automated Codex review:
1. find_kba_agent preferred global over company-specific agents.
order='company_id desc' sorts SQL NULLs (global, company_id=False)
FIRST under PostgreSQL DESC, so a company with its own KBA agent was
still routed through the shared global one. Replaced with a two-step
preference (company-specific search first, global fallback) - DB-agnostic
and provably correct. Added TestKbaAgentCompanyPreference regression tests
(company-specific wins; global still used as fallback).
2. Native ChatGPT (fa-magic) powerbox command was suppressed in EVERY
editor once viin_ai_brain was installed, because the suppressor gated only
on bridge.available (globally true). In non-Brain editors (mail/CRM/website)
the replacement brain_ai_write is itself disabled, leaving no AI write
command. Added isBrainContext(wysiwyg) to the suppression contextGate so
the native command is only replaced inside Brain editors.
|
Failed
|
|
|
|
|
|
|
|
merged
[IMP] P16: Editor Extension Framework - modular Brain/AI editor (OdooEditor migration)
|
[FIX] viin_brain: restore pylint-odoo consider-merging-classes-inherited disable
Reverts the removal in b93db89, which was based on a faulty reproduction
against VANILLA Odoo (no pylint-odoo loaded -> the inline disable read as
unknown-option-value W0012). The Viindoo Runbot lint is NOT Odoo-core
test_lint: it runs the tvtmaaddons test_pylint module, which loads
pylint_odoo and ENABLES consider-merging-classes-inherited (R8180). So the
disable is required: viin.brain.page is intentionally re-opened in both
viin_brain_page_tag.py and viin_brain_page_extension.py for readability, and
R8180 asks to merge them.
Verified against the period-correct Odoo 17 stack (pylint 2.15.10 /
astroid 2.13.5 / pylint-odoo 8.0.22) using the Runbot module's exact
ENABLED_CODES: with the disable restored, all 21 viin modules report 0
findings (R8180 suppressed). Runbot build 222101 failed on exactly this
one message (1 failed of 24 tests).
|
Killed
|
|
|
|
|
|
|
|
merged
[IMP] P16: Editor Extension Framework - modular Brain/AI editor (OdooEditor migration)
|
[FIX] viin_web_editor,viin_brain,viin_ai_editor,viin_ai_brain: Runbot test_lint/test_pylint green
Reproduced the Runbot failures locally against Odoo 17 test_lint (period-correct
pylint 3.3.7) instead of guessing, and fixed the real root causes:
- test_pylint (Python): viin_brain/models/viin_brain_page_tag.py carried
'# pylint: disable=consider-merging-classes-inherited'. That message belongs
to the pylint-odoo plugin, which Odoo core test_pylint does NOT load, so the
inline disable resolves to unknown-option-value (W0012) - not suppressed by
'--disable=all,useless-option-value' - making pylint exit non-zero. The
message is not even in test_pylint's enable list, so the disable was inert.
Removed it. Verified pylint exit 0 across all 21 viin modules.
- test_eslint (JS): 417 prettier/prettier errors across P16 JS under the repo
.eslintrc.json (extends plugin:prettier/recommended, tabWidth 4, printWidth
100). The husky hook only formats staged files, so P16 files committed
earlier were never formatted. Ran eslint --fix; verified 0 errors against
both the repo prettier config and Odoo core eslintrc.
- test_manifests: viin_ai_editor set 'auto_install': False, which equals the
default manifest value (assertNotEqual hard-fail). Removed the key.
No behavior change: JS edits are pure formatting; Python edits delete an inert
key and an inert lint pragma. package-lock.json kept at baseline.
|
Failed
|
|
|
|
|
|
|
|
merged
[IMP] P16: Editor Extension Framework - modular Brain/AI editor (OdooEditor migration)
|
[FIX] viin_web_editor,viin_brain,viin_ai_editor,viin_ai_brain: lint clean for Runbot (prettier/eslint format, unused vars, manifest, pot, flake8)
|
Failed
|
|
|
|
|
|
|
|
merged
[IMP] P16: Editor Extension Framework - modular Brain/AI editor (OdooEditor migration)
|
[FIX] viin_brain: repair slash-focus tour (AI category priority outranks Brain)
ROOT CAUSE (tour fragility, not product code regression):
viin_ai_editor registers an "AI" powerbox category at priority 95 and an
"AI block" command at priority 90. The "Brain" category is registered at
priority 90. The OdooEditor Powerbox sorts categories by priority desc then
name asc, so "AI" (p=95) renders before "Brain" (p=90). Pressing Enter
after a bare "/" selected the "AI block" command instead of "Heading 1",
and the assertion :has(h1,h2,h3) failed.
Evidence: failure screenshot showed the AI block component rendered (the
"AI block - Empty - type a prompt or pick a template" UI) instead of an h1.
The Brain heading-via-slash command itself works correctly when explicitly
selected; the tour was testing an incidental ordering assumption.
FIX (tour fix, not product code):
Changed the powerbox step to type "/heading" instead of bare "/" before
pressing Enter. After insertText("/heading") + a keyup event, Powerbox.
_onKeyup() runs patienceDiff and filters filteredCommands to only the three
Brain heading commands (Heading 1 p=100, Heading 2 p=90, Heading 3 p=80).
"Heading 1" is then the active/first command. Tightened the final assertion
from :has(h1,h2,h3) to :has(h1) since the filter + priority guarantee h1.
FAILURE 2 (TestBrainTimelineTour): already green in the worktree; 0 failed
on isolation re-run. No code change needed.
Wikilink tour: confirmed still green (3/3 pass, 0 errors).
|
Failed
|
|
|
|
|
|
|
|
merged
[IMP] P16: Editor Extension Framework - modular Brain/AI editor (OdooEditor migration)
|
[FIX] viin_ai_editor: valid XML comments in demo data (double-hyphen)
|
Failed
|
|
|
|
|
|
|
|
merged
[DOC] ADR-010 Editor Extension Framework (modular Brain/AI editor) — Proposed
|
[DOC] docs: ADR-010 Editor Extension Framework (modular Brain/AI editor)
Refine ADR-003 with the structural HOW for the OdooEditor migration (P16):
- Keep viin_web_editor as the neutral propagation base (patch HtmlField/
Wysiwyg once, driven by global registries) - it is the keystone that lets
Brain and AI editor plugins depend on it without coupling to each other
(AGENTS.md cam ky #2). Deleting it was considered and rejected.
- Move the AI block out of viin_brain into a new AI-cluster module
viin_ai_editor (generic, works in any html field); bridge viin_ai_brain
wires AI output to Brain attribution + resolves Brain x AI conflicts.
- Grounded in verified Odoo 17 web_editor seams (options.plugins,
_getPowerboxOptions, HtmlField.wysiwygOptions, no native NodeView,
setupCollaboration, '/'-only input rule).
Tracker odooeditor-migration-progress.md re-organised from 23-features-in-Brain
to module-split + a 4-phase wave plan (framework -> plugins -> bridge ->
verify+visual) with odoo-ui-reviewer visual gates. Status Proposed - awaiting
sign-off before execution.
|
Killed
|
Not started
|
Not finished
|
|
|
|
|
|
merged
[DOC] ADR-010 Editor Extension Framework (modular Brain/AI editor) — Proposed
|
[DOC] docs: ADR-010 Editor Extension Framework (modular Brain/AI editor)
Refine ADR-003 with the structural HOW for the OdooEditor migration (P16):
- Keep viin_web_editor as the neutral propagation base (patch HtmlField/
Wysiwyg once, driven by global registries) - it is the keystone that lets
Brain and AI editor plugins depend on it without coupling to each other
(AGENTS.md cam ky #2). Deleting it was considered and rejected.
- Move the AI block out of viin_brain into a new AI-cluster module
viin_ai_editor (generic, works in any html field); bridge viin_ai_brain
wires AI output to Brain attribution + resolves Brain x AI conflicts.
- Grounded in verified Odoo 17 web_editor seams (options.plugins,
_getPowerboxOptions, HtmlField.wysiwygOptions, no native NodeView,
setupCollaboration, '/'-only input rule).
Tracker odooeditor-migration-progress.md re-organised from 23-features-in-Brain
to module-split + a 4-phase wave plan (framework -> plugins -> bridge ->
verify+visual) with odoo-ui-reviewer visual gates. Status Proposed - awaiting
sign-off before execution.
|
Killed
|
|
Not finished
|
|
|
|
|
|
open
[DOC] Reconcile ADR-002 CRDT with shipped interim relay (OT-final after P16)
|
[DOC] docs: reconcile ADR-002 CRDT with shipped interim relay
Recon + Odoo native test (TestCollabStepBuffer/Controller +
TestBrainRealtimeCollab -> 0 failed of 15) cho thấy CRDT thực tế đã ship
~90% qua launch-hardening (WI-9 snapshot-relay + WI-12 presence +
WI-13 flag), KHÔNG phải 'sắp làm W7-W11' như roadmap/ADR-002 cũ.
- ADR-002: thêm §Implementation Status — interim snapshot-relay đã ship +
verified; Decision (Option 1 OdooEditor native OT) GIỮ NGUYÊN làm đích;
OT-final re-sequenced SAU P16 (cần PageEditor wrap OdooEditor). Ghi rõ
gap: relay step.html_snapshot, chưa gọi setupCollaboration, chưa set
collaborative=true.
- roadmap + brain/README: phản ánh interim DONE sớm, OT-final sau P16.
Docs-only; không đụng code. Khớp quyết định: giữ mục tiêu OT thật.
|
Killed
|
|
|
|
|
|
|
|
merged
[DOC][TEST] Brain stabilization: CRDT v1 + ADR-003 OdooEditor + WI-006 daily-note ACL deny
|
[TEST] viin_brain: close 7 residual daily-note ACL cells as deny (WI-006)
Convert 7 @_odoo_xfail conformance-gap tests to hard DENY assertions
(AccessError): viewer/commenter create (AC-9.3a/b, 14.3a), commenter
message_post (AC-14.5a/b), group-resolved editor write (AC-19.2b),
vault-admin unlink (AC-24.4) on own daily note. No production/security
change — deny-preserving per WI-006. Remove dead _odoo_xfail helper.
Verified: odoo-bin --test-enable on the 4 daily-note ACL classes ->
0 failed, 0 error of 45 tests.
|
Killed
|
|
|
|
|
|
|
|
merged
[FIX] viin_brain,viin_web_editor: Brain core launch-hardening (T-1/T-2/T-3)
|
[FIX] viin_brain,viin_web_editor: Brain core launch-hardening (T-1/T-2/T-3)
- T-2 privacy: daily notes can never be shared via link (ValidationError in
_check_page_shareable); share comment state no longer hardcoded (honors
comment_auto_approve).
- T-3 ACL: complete the 40-cell matrix in tests — non-owner (incl brain/vault
admin) cannot read/write another user's daily note; commenter cannot write
own daily body. No rule change (rule_page_daily_global already enforces).
- T-1 concurrency: deterministic version token (isoformat ms) + re-enable
first-save optimistic-concurrency check in page_editor (realtime untouched).
- Tests: viin_web_editor powerbox-registry QUnit, trigger-regex edge cases,
block_parser round-trip tour + wrapper.
- Docs: CHANGELOGs, brain/security.md ACL matrix, decisions/wi-005 (privacy-first).
|
Killed
|
|
|
|
|
|
|
|
merged
[FIX] viin_brain,viin_web_editor: Brain core launch-hardening (T-1/T-2/T-3)
|
[FIX] viin_brain,viin_web_editor: Brain core launch-hardening (T-1/T-2/T-3)
- T-2 privacy: daily notes can never be shared via link (ValidationError in
_check_page_shareable); share comment state no longer hardcoded (honors
comment_auto_approve).
- T-3 ACL: complete the 40-cell matrix in tests — non-owner (incl brain/vault
admin) cannot read/write another user's daily note; commenter cannot write
own daily body. No rule change (rule_page_daily_global already enforces).
- T-1 concurrency: deterministic version token (isoformat ms) + re-enable
first-save optimistic-concurrency check in page_editor (realtime untouched).
- Tests: viin_web_editor powerbox-registry QUnit, trigger-regex edge cases,
block_parser round-trip tour + wrapper.
- Docs: CHANGELOGs, brain/security.md ACL matrix, decisions/wi-005 (privacy-first).
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[FIX] viin_brain,viin_web_editor: Brain core launch-hardening (T-1/T-2/T-3)
|
[FIX] viin_brain,viin_web_editor: Brain core launch-hardening (T-1/T-2/T-3)
- T-2 privacy: daily notes can never be shared via link (ValidationError in
_check_page_shareable); share comment state no longer hardcoded (honors
comment_auto_approve).
- T-3 ACL: complete the 40-cell matrix in tests — non-owner (incl brain/vault
admin) cannot read/write another user's daily note; commenter cannot write
own daily body. No rule change (rule_page_daily_global already enforces).
- T-1 concurrency: deterministic version token (isoformat ms) + re-enable
first-save optimistic-concurrency check in page_editor (realtime untouched).
- Tests: viin_web_editor powerbox-registry QUnit, trigger-regex edge cases,
block_parser round-trip tour + wrapper.
- Docs: CHANGELOGs, brain/security.md ACL matrix, decisions/wi-005 (privacy-first).
|
Failed
|
|
|
|
|
|
|
|
merged
[DOC] root: AI agent onboarding guides (AGENTS/CLAUDE/GEMINI)
|
[DOC] root: add AI agent onboarding guides (AGENTS/CLAUDE/GEMINI)
SSOT in AGENTS.md (read directly by Codex); CLAUDE.md and GEMINI.md
@import it plus per-CLI notes. Positions ai17 as two independent
products bridged modularly: Viindoo Brain (knowledge OS — Notion for
humans + Obsidian-style second brain for AI agents) and Viindoo AI
(agentic orchestration OS). Captures must-read docs, core security
prohibitions, git/worktree discipline, Runbot test model.
Shared-repo hygiene: guides contain NO machine/personal-local info.
AGENTS.md adds prohibitions 18-21 — never commit absolute home paths,
internal infra, per-developer AI config (MCP/skills/local models), or
secrets; keep per-developer config outside the repo; self-check diffs
before PR. CLAUDE.md/GEMINI.md kept generic and portable.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[DOC] root: AI agent onboarding guides (AGENTS/CLAUDE/GEMINI)
|
[DOC] root: add AI agent onboarding guides (AGENTS/CLAUDE/GEMINI)
SSOT in AGENTS.md (read directly by Codex); CLAUDE.md and GEMINI.md
@import it plus per-CLI notes. Positions ai17 as two independent
products bridged modularly: Viindoo Brain (knowledge OS — Notion for
humans + Obsidian-style second brain for AI agents) and Viindoo AI
(agentic orchestration OS). Captures must-read docs, core security
prohibitions, git/worktree discipline, Runbot test model.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[IMP] viin_brain,viin_web_editor: Brain core UI polish wave — runtime bug + a11y/responsive
|
[IMP] viin_brain,viin_web_editor: Brain core UI/UX polish wave
Squashed integration of the Brain core polish wave (branch
wave/integration-brain-polish-w1): runtime bug fixes, theme repair,
a11y/responsive, the form-sidebar quick-create drawer reworked onto the
native web_editor editor, plus JS/behaviour tests and doc sync. Scope is
viin_brain + viin_web_editor only; rebased on origin/17.0.
Added
- Quick-create drawer note body now mounts the native web_editor Wysiwyg
(OdooEditor), lazy-loaded from web_editor.backend_assets_wysiwyg, working
on in-memory HTML (no persisted page) so the create-on-save/discard flow
is preserved; body saved into content_html. First surface delegated to the
audited editor per ADR-003 Path B (PageEditor migration stays P16).
- Responsive 3-pane layout (_responsive.scss): tablet 2-pane with the
context pane as an overlay; phone single-pane with a slide-in sidebar
drawer and ellipsised title.
- A11y: <main role="main"> landmark, page-title aria-label, disabled
"Related" tab aria-disabled + tabindex=-1; header hamburger toggles the
sidebar; timeline icon corrected.
Changed
- Form sidebar lists notes LINKED to a record, not only inline @-mentions:
_brain_mentioning_page_ids unions viin.brain.link record-mentions with
viin.brain.page.record.link rows, and _compute_brain_page_count uses the
same union via a distinct page-id set (no double count) with ACL filtering.
- "Link a record" replaced by a friendly two-step "Link a document" picker
(core ModelSelector + RecordSelector); model:id output contract unchanged.
- Filter pills compacted to a single scrolling row; panel/graph bars wrap
instead of overflowing in the ~300px context pane.
Fixed
- Theme token system repair (theme_tokens.scss): Odoo 17 emits no --bs-*
runtime properties and the prior self-referential shims formed a CSS
cycle resolving empty (theme-blind UI); redefined non-self-referentially
from live Odoo vars with literal fallbacks.
- OWL this-binding crashes in bare inline arrow handlers (drawer vault /
entry-kind clicks, calendar view, moderation panel).
- Form sidebar layout is resize-reactive: ui service useState-wrapped so
layoutMode recomputes on viewport change.
- Share dialog reveals the public URL on save + .bind toggle; template
dialog body moved to the Dialog default slot; sidebar label live-updates
on rename via a brain-page-renamed bus event; removed the hardcoded "P12"
badge; badges migrated to text-bg-secondary.
- viin_web_editor: removed the spike powerbox demo commands
(viin_spike_hello / viin_spike_block + brain category); registries ship
empty; embedded_component_host docstring repointed to native_commands.js.
Tests
- Drawer tour (crash guards + native Wysiwyg mount + Save) with a Python
runner asserting the server outcome; BrainFormSidebar layout + resize
reactivity unit tests; backend tests for the linked-notes union. Verified
on a clean pg16 DB: drawer tour green, backend suites green.
Docs
- CHANGELOG [Unreleased] entries (viin_brain + viin_web_editor); README
"what's new"/responsive/sidebar updates; contenteditable_policy.md
reconciled (Path-B delegated surface, status, change history).
|
Killed
|
|
|
|
|
|
|
|
merged
[IMP] viin_brain,viin_web_editor: Brain core UI polish wave — runtime bug + a11y/responsive
|
[IMP] viin_brain,viin_web_editor: Brain core UI/UX polish wave
Squashed integration of the Brain core polish wave (branch
wave/integration-brain-polish-w1): runtime bug fixes, theme repair,
a11y/responsive, the form-sidebar quick-create drawer reworked onto the
native web_editor editor, plus JS/behaviour tests and doc sync. Scope is
viin_brain + viin_web_editor only; rebased on origin/17.0.
Added
- Quick-create drawer note body now mounts the native web_editor Wysiwyg
(OdooEditor), lazy-loaded from web_editor.backend_assets_wysiwyg, working
on in-memory HTML (no persisted page) so the create-on-save/discard flow
is preserved; body saved into content_html. First surface delegated to the
audited editor per ADR-003 Path B (PageEditor migration stays P16).
- Responsive 3-pane layout (_responsive.scss): tablet 2-pane with the
context pane as an overlay; phone single-pane with a slide-in sidebar
drawer and ellipsised title.
- A11y: <main role="main"> landmark, page-title aria-label, disabled
"Related" tab aria-disabled + tabindex=-1; header hamburger toggles the
sidebar; timeline icon corrected.
Changed
- Form sidebar lists notes LINKED to a record, not only inline @-mentions:
_brain_mentioning_page_ids unions viin.brain.link record-mentions with
viin.brain.page.record.link rows, and _compute_brain_page_count uses the
same union via a distinct page-id set (no double count) with ACL filtering.
- "Link a record" replaced by a friendly two-step "Link a document" picker
(core ModelSelector + RecordSelector); model:id output contract unchanged.
- Filter pills compacted to a single scrolling row; panel/graph bars wrap
instead of overflowing in the ~300px context pane.
Fixed
- Theme token system repair (theme_tokens.scss): Odoo 17 emits no --bs-*
runtime properties and the prior self-referential shims formed a CSS
cycle resolving empty (theme-blind UI); redefined non-self-referentially
from live Odoo vars with literal fallbacks.
- OWL this-binding crashes in bare inline arrow handlers (drawer vault /
entry-kind clicks, calendar view, moderation panel).
- Form sidebar layout is resize-reactive: ui service useState-wrapped so
layoutMode recomputes on viewport change.
- Share dialog reveals the public URL on save + .bind toggle; template
dialog body moved to the Dialog default slot; sidebar label live-updates
on rename via a brain-page-renamed bus event; removed the hardcoded "P12"
badge; badges migrated to text-bg-secondary.
- viin_web_editor: removed the spike powerbox demo commands
(viin_spike_hello / viin_spike_block + brain category); registries ship
empty; embedded_component_host docstring repointed to native_commands.js.
Tests
- Drawer tour (crash guards + native Wysiwyg mount + Save) with a Python
runner asserting the server outcome; BrainFormSidebar layout + resize
reactivity unit tests; backend tests for the linked-notes union. Verified
on a clean pg16 DB: drawer tour green, backend suites green.
Docs
- CHANGELOG [Unreleased] entries (viin_brain + viin_web_editor); README
"what's new"/responsive/sidebar updates; contenteditable_policy.md
reconciled (Path-B delegated surface, status, change history).
|
Failed
|
|
|
|
|
|
|
|
merged
[IMP] viin_brain,viin_web_editor: Brain core UI polish wave — runtime bug + a11y/responsive
|
[DOC] viin_brain,viin_web_editor: sync docs with the UI/UX polish wave
The wave shipped substantial behaviour/architecture changes with no doc
updates (except a partial contenteditable_policy edit). Two read-only drift
analyses (one per module) mapped the gaps; this commit closes them. No code
changes - docs only.
viin_brain/CHANGELOG.md
- New "## [Unreleased]" section (Keep-a-Changelog; PR not merged so no
version fabricated - manifest stays 0.3). Added/Changed/Fixed covering:
native web_editor Wysiwyg in the quick-create drawer (ADR-003 Path B),
responsive 3-pane layout, a11y landmarks, sidebar lists linked notes
(mention + record-link union), friendly "Link a document" picker, theme
token cycle repair, resize-reactive layout, this-binding crash fixes,
share-dialog/template-dialog fixes, P12 badge removal.
viin_brain/README.md
- "What's new - UI/UX polish wave (unreleased)" subsection; responsive note
in the OWL client-action description; ChatterAware sidebar bullet expanded
(resize-reactive + linked-notes union); record-mention section notes
explicit record links surface in the form sidebar; one sentence on the
runtime --bs-* shim mechanism under Theme Compatibility.
viin_brain/docs/brain/contenteditable_policy.md
- Reconciled internal inconsistencies introduced when the drawer Path-B note
was added earlier in this PR: bumped "Last verified at" to SHA e45c3a4 /
2026-06-02 (and re-confirmed the section-5 rogue grep is empty), added a
dated section-8 status bullet recording the first Path-B surface, and
reordered the section-7 change history to newest-last.
viin_web_editor/CHANGELOG.md
- New "## [Unreleased]" section: Removed the spike powerbox demo commands
(viin_spike_hello / viin_spike_block + brain category); Changed the
embedded_component_host docstring to point at native_commands.js (T-5).
Known pre-existing nit (out of scope, anchors still resolve): the section-3
audit table line numbers in contenteditable_policy.md have drifted from the
source; the durable grep anchors remain valid.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[IMP] viin_brain,viin_web_editor: Brain core UI polish wave — runtime bug + a11y/responsive
|
[TEST] viin_brain: JS smoke + behaviour tests for the UI/UX polish wave
The wave reworked the form-sidebar quick-create flow heavily but left the JS
surfaces unguarded. Adds protection at two altitudes (ETHOS §11 - guard the
behaviour, not the click):
1. Quick-create drawer tour (brain_form_sidebar_drawer.js + Python runner):
open the drawer, click a vault item and an entry-kind button (both crashed
pre-fix via the bare-arrow "this" loss), assert the native web_editor
Wysiwyg mounts, type a rich body, Save & Close. The runner then asserts the
server outcome - page created, title + rich body persisted, entry_kind
applied (so the crash fix produced an *effect*, not just "did not throw"),
and the note surfaces on the record via the sidebar union (#1).
Verified on a clean DB: 12/12 steps + all assertions green.
2. BrainFormSidebar layout unit tests (brain_form_sidebar_layout_tests.js):
panel/rail/strip mapping + the resize-reactive guard - mutating the
viewport size after mount must re-render the layout. This fails if the
useState(useService('ui')) wrap regresses to a bare useService (the #3
bug). All assertions pass (verified in isolation: 4/4).
Registers both in __manifest__.py (web.assets_tests / web.qunit_suite_tests)
and the runner in tests/__init__.py.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[IMP] viin_brain,viin_web_editor: Brain core UI polish wave — runtime bug + a11y/responsive
|
[TEST] viin_brain: JS smoke + behaviour tests for the UI/UX polish wave
The wave reworked the form-sidebar quick-create flow heavily but left the JS
surfaces unguarded. Adds protection at two altitudes (ETHOS §11 - guard the
behaviour, not the click):
1. Quick-create drawer tour (brain_form_sidebar_drawer.js + Python runner):
open the drawer, click a vault item and an entry-kind button (both crashed
pre-fix via the bare-arrow "this" loss), assert the native web_editor
Wysiwyg mounts, type a rich body, Save & Close. The runner then asserts the
server outcome - page created, title + rich body persisted, entry_kind
applied (so the crash fix produced an *effect*, not just "did not throw"),
and the note surfaces on the record via the sidebar union (#1).
Verified on a clean DB: 12/12 steps + all assertions green.
2. BrainFormSidebar layout unit tests (brain_form_sidebar_layout_tests.js):
panel/rail/strip mapping + the resize-reactive guard - mutating the
viewport size after mount must re-render the layout. This fails if the
useState(useService('ui')) wrap regresses to a bare useService (the #3
bug). All assertions pass (verified in isolation: 4/4).
Registers both in __manifest__.py (web.assets_tests / web.qunit_suite_tests)
and the runner in tests/__init__.py.
|
Failed
|
|
|
|
|
|
|
|
merged
[IMP] viin_brain,viin_web_editor: Brain core UI polish wave — runtime bug + a11y/responsive
|
[TEST] viin_brain: JS smoke + behaviour tests for the UI/UX polish wave
The wave reworked the form-sidebar quick-create flow heavily but left the JS
surfaces unguarded. Adds protection at two altitudes (ETHOS §11 — guard the
behaviour, not the click):
1. Quick-create drawer tour (brain_form_sidebar_drawer.js + Python runner):
open the drawer, click a vault item and an entry-kind button (both crashed
pre-fix via the bare-arrow loss), assert the native web_editor
Wysiwyg mounts, type a rich body, Save & Close. The runner then asserts the
server outcome — page created, title + rich body persisted, entry_kind
applied (so the crash fix produced an *effect*, not just "did not throw"),
and the note surfaces on the record via the sidebar union (#1).
Verified on a clean DB: 12/12 steps + all assertions green.
2. BrainFormSidebar layout unit tests (brain_form_sidebar_layout_tests.js):
panel/rail/strip mapping + the resize-reactive guard — mutating the
viewport size after mount must re-render the layout. This fails if the
useState(useService('ui')) wrap regresses to a bare useService (the #3
bug). All assertions pass (verified in isolation: 4/4).
Registers both in __manifest__.py (web.assets_tests / web.qunit_suite_tests)
and the runner in tests/__init__.py.
|
Killed
|
Not started
|
Not finished
|
|
|
|
|
|
merged
[IMP] viin_brain,viin_web_editor: Brain core UI polish wave — runtime bug + a11y/responsive
|
[IMP] viin_brain: drawer note body uses native web_editor Wysiwyg (ADR-003 Path B)
Replaces the quick-create drawer's hand-rolled contenteditable + execCommand
toolbar with the native web_editor Wysiwyg (OdooEditor) — the editor the
project's own ADR-003 mandates (TipTap rejected; PageEditor migration is P16).
Why the previous approach was wrong:
- It declared a raw, static contenteditable="true" in Brain markup, which
is a rogue editable surface under the normative contenteditable_policy.md
(two-surface invariant §2). The §5 rogue-detection lint went non-empty.
Why native Wysiwyg fits here (and PageEditor does not):
- Wysiwyg edits in-memory HTML and exposes getValue(), so it needs no
persisted page — the drawer's create-on-save / discard flow is preserved.
PageEditor requires an existing pageId and brings full-pane chrome +
autosave/collab machinery, unsuitable for a quick-capture drawer.
- viin_web_editor already patches Wysiwyg._getPowerboxOptions, so the Brain
powerbox/slash commands appear automatically (parity, no extra code).
Implementation (OSM-grounded, mirrors web_editor HtmlField + the in-org
viin_customizer HTMLEditor pattern):
- Lazy-load web_editor.backend_assets_wysiwyg in onWillStart, mount Wysiwyg as
a dynamic <t t-component>; capture the instance via the startWysiwyg prop.
- Build the Wysiwyg props ONCE (stable identity) so the dynamic component does
not remount and drop the captured instance.
- _bodyHtml() reads getValue(); empty sentinel <p><br></p> => no content.
- The editable's contenteditable lives inside web_editor (audited), so Brain
declares none: contenteditable_policy.md §5 grep is empty again. Policy §2
+ change history updated to document the delegated surface.
Verified live (sale.order form, pg16): drawer mounts exactly one Wysiwyg,
rich content persists (content_html=<p><b>…</b></p>), §5 lint empty, module
upgrade compiles assets clean.
|
Failed
|
|
|
|
|
|
|
|
merged
[IMP] viin_brain,viin_web_editor: Brain core UI polish wave — runtime bug + a11y/responsive
|
[FIX] viin_brain: sidebar lists linked notes + rich-text drawer + resize-reactive layout
Three review findings on the form-view Brain sidebar (sale.order):
1. A note created via the quick-create drawer links the record on the
Brain side but never appeared on the record's own sidebar/stat count.
Root cause: the sidebar query (_brain_mentioning_page_ids) and the
stat compute (_compute_brain_page_count) only looked at record_mention
links, while the drawer writes an explicit viin.brain.page.record.link
(linked_record_ids) — two distinct mechanisms. Union both so a note
'about this record' shows on the record. Page-level ACL preserved
(count filtered through an ACL-applied page search; list filtered by
the downstream page read). Behaviour tests added (fail pre-fix).
2. Drawer body was a plain textarea, so notes could not be formatted.
Replaced with a contenteditable rich-text region + a static toolbar
(bold / italic / heading / bulleted list) emitting semantic tags;
innerHTML is saved as content_html (mirrors PageEditor).
3. The form sidebar layout (panel/rail/strip) did not react to browser
resize. Root cause: uiService was read via bare useService('ui'), so
reads of ui.size did not subscribe the component. Wrapped with
useState (canonical Odoo 17 pattern) so layoutMode recomputes on
resize.
|
Failed
|
|
|
|
|
|
|
|
merged
[IMP] viin_brain,viin_web_editor: Brain core UI polish wave — runtime bug + a11y/responsive
|
[FIX] viin_brain: form-sidebar drawer crashes + inline note body + header padding
Review of the Brain form sidebar (sale.order / res.partner) surfaced three issues:
1. Create-drawer crashes (same unbound-`this` class as the share-dialog fix):
`t-on-click="() => onVaultSelect(vault.id)"` / `() => onEntryKindSelect(...)`
call a BARE method inside an inline arrow, so OWL invokes it without the
component as `this` → "Cannot read properties of undefined (reading 'state')"
when picking a vault or an entry kind. Swept the whole class: every
`=> onXxx(` arrow handler now uses `=> this.onXxx(` (9 sites across
brain_create_drawer, brain_form_sidebar [onOpenPage — would crash on a linked
page click], moderation_panel, database_calendar_view drag/drop).
2. Header "too close to the left": the form-sidebar header rendered with NO
padding. Root cause — `padding: … calc(map-get($spacers, 2) * 0.75)`: SCSS
does NOT evaluate `map-get()` inside a CSS `calc()`, so it compiled to the
literal `calc(map-get($spacers, 2) * 0.75)` — invalid, which makes the
browser drop the ENTIRE padding declaration → 0 padding. Same class as the
earlier LibSass `min()` regression. Fixed by letting Sass compute the value
directly: `(map-get($spacers, 2) * 0.75)` → `padding: 8px 16px 6px`.
3. Note body not editable inline (had to leave the form for the Brain app — a
UX failure when the note must reference the open record): the create drawer
now has an inline body textarea; its content is saved as `content_html` on
creation (the page model syncs content_plain/blocks), so the user writes the
note while the record stays on screen. "Save & Open in Brain" still available
for rich editing.
Verified live (res.partner form): vault + entry-kind selection no longer crash;
note created with body content + entry_kind + record link; header padding renders.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[IMP] viin_brain,viin_web_editor: Brain core UI polish wave — runtime bug + a11y/responsive
|
[FIX] viin_brain: share dialog crash/no-link + friendly link-to-record picker
Two UX/runtime issues found in review:
1. Share dialog (share_dialog):
- Toggling "Public link" threw "Cannot read properties of undefined
(reading 'state')" — the CheckBox onChange was wired as
`onChange="(val) => onToggleLink(val)"`, an unbound inline arrow that
OWL invoked without the component as `this`. Use `onChange.bind` so the
handler keeps its component binding.
- "Save settings" created the link but the public URL never appeared: the
URL block is gated on `state.enabled`, which onSave never set. Saving a
share link now sets `state.enabled = true` so the URL + Copy show.
2. Link-to-record (link_record_dialog): replaced the raw "model:id" text box
(e.g. "res.partner:42" — meaningless to end users) with Odoo's native
friendly pickers — ModelSelector ("Document type", model display names) +
RecordSelector ("Document", record-name autocomplete). Optional admin
whitelist via _brain_default_linkable_models; empty = all models (Odoo
reference-field default). Output contract unchanged (onConfirm "model:id").
Handlers passed with `.bind` to avoid the same unbound-`this` class of bug.
Verified in a live Odoo 17 instance: share toggle no longer crashes and the
URL shows after save; the link dialog renders the model→record friendly chain;
the Brain form-sidebar (Brain Pages stat + Brain Note + BRAIN rail) renders
correctly on chatter forms (res.partner) — issue #3 was blocked by #2, not a bug.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[IMP] viin_brain,viin_web_editor: Brain core UI polish wave — runtime bug + a11y/responsive
|
[IMP] viin_brain: filter pills compact single row (mockup parity) + note SSOT dup
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[IMP] viin_brain,viin_web_editor: Brain core UI polish wave — runtime bug + a11y/responsive
|
[FIX] viin_brain: repair theme token system — Brain UI was theme-blind (off-theme)
Root cause (CEO-reported "rất xấu, không khớp theme Odoo"; PRE-EXISTING, not the
polish wave): theme_tokens.scss Block 1 shimmed Bootstrap-5.3 CSS custom props
with a SELF-REFERENTIAL pattern `--bs-x: var(--bs-x, fallback)`. A custom
property referencing itself is a CSS dependency cycle → computes to the
guaranteed-invalid (empty) value; the fallback is never reached. Odoo 17 ships
Bootstrap 5.1.3 and emits NONE of --bs-* at runtime (verified empty at :root on
the live instance), so every --o-brain-* token that chained into --bs-* (page /
sidebar / context backgrounds, borders, muted text, badge text, trust + AI
surfaces) resolved to empty → panes rendered transparent with no borders, text
fell back to gray, badges lost their fill. The whole app looked flat/off-theme.
Fix: rewrite Block 1 as PLAIN (non-self-referential) assignments anchored to
Odoo-17 runtime vars that DO carry values (--gray-100..900, --primary,
--secondary, --success/-warning/-danger/-info, --body-*), each with a literal
fallback. This single change resolves the entire downstream token chain
(Block 2 surfaces/borders/text, Block 3 trust/AI) — verified in-browser:
--o-brain-bg-sidebar #f8f9fa, --o-brain-border #dee2e6, badge text #fff, header
white + 1px borders. Odoo consumes no runtime --bs-*, so global :root definition
is side-effect-free; a themed deploy that emits real --bs-* overrides by cascade.
Also: vault badge reverted to brand accent fill with white text (earlier wave
darkening treated the wrong cause — gray text came from the empty --bs-white,
not the fill); badge radius 6px→4px and page title 2.1rem→2rem (mockup/spec).
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[IMP] viin_brain,viin_web_editor: Brain core UI polish wave — runtime bug + a11y/responsive
|
[IMP] viin_brain,viin_web_editor: Brain core UI polish wave — runtime bug + a11y/responsive
Khảo sát UI live cụm Brain core phát hiện loạt defect runtime + a11y/responsive
mà tour test chưa phủ. Wave này đun cạn nhóm bug + polish (low-risk, không chạm
kiến trúc; tách khỏi P16 OdooEditor / P14 CRDT còn gated).
Runtime bug:
- C-1: dialog "+ New page" body rỗng — web.Dialog (Odoo 17) render body qua
DEFAULT slot, không phải named "body" slot; bỏ <t t-set-slot="body"> (khớp
MovePageDialog). Reproduce bằng click thật + thêm tour brain_new_page_tour.
- C-2: context-pane tab bar overflow ở desktop — 4/6 tab bị giấu; flex-wrap để
cả 6 tab truy cập được (root cause hiệu lực ở _misc.scss override, fix cả 2).
a11y:
- H-1: bỏ badge dev "P12" lộ ra UI; nhãn tab trung tính.
- H-3: contrast badge vault initials 2.33:1 -> 5.09:1 (token color-mix riêng).
- H-4: thêm landmark <main> bọc center pane (Lighthouse landmark-one-main).
- M-2/M-3/M-4/M-7: badge class nhất quán, aria-disabled tab, aria-label title động.
responsive (H-2):
- _responsive.scss: <768px single-pane + sidebar drawer (hamburger header toggle),
768-1024px 2-pane, title ellipsis; escape LibSass min() (mixed px/vw).
khác:
- M-1: đồng bộ nhãn sidebar khi rename page (env.bus brain-page-renamed).
- M-6/L-1/L-2/L-5: graph filter wrap, icon timeline calendar, collapsed sidebar
clip, dedupe message daily-note.
- T-5: gỡ powerbox spike demo command (viin_web_editor) + scrub comment chết.
Verified: cài sạch trên Odoo 17 community + pg16; web.assets_backend compile sạch;
UI re-review 9/9 PASS; Lighthouse a11y 76 -> 77.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[ADD] viin_ai_ops: Operating Layer foundation (P-OPS-1)
|
[FIX] viin_ai_agent: stub contract test robust to framework-injected x_ fields
The post_install tests test_schedule_stub_has_no_rogue_fields and
test_approval_chain_stub_has_no_rogue_fields failed on runbot because
viin_workflow_automation grafts x_workflow_* computed fields onto base
(every concrete model) in the full Viindoo suite. Those framework-owned
fields are outside the P-OPS-1 stub contract, which governs only
viin_ai-declared fields. Exclude x_-prefixed fields from the comparison;
viin_ai never declares x_-prefixed fields, so the contract's protection
is preserved.
|
Killed
|
|
|
|
|
|
|
|
merged
[ADD] viin_ai_ops: Operating Layer foundation (P-OPS-1)
|
[IMP] viin_ai_ops,viin_ai_agent,viin_ai_chat: address PR #36 review findings
Fixes from OSM-grounded (Viindoo 17.0) code review of this PR.
viin_ai_ops (HIGH):
- control_policy._match_proposal: scope policy search by the proposal's
company_id ('|' company_id=False / =proposal.company) — ir.rule only
scopes to the *current user's* allowed companies, not the proposal's,
so explicit filtering is mandatory for multi-company isolation. Drop
the redundant ('active','=',True) term (auto-filtered by _active_name).
- goal / work_item / agent_ext / trace_ext compute counts: replace
search_count() inside a for-loop (N+1) with a single read_group();
add @api.depends('work_item_ids' / 'proposal_ids') where a reverse
O2M exists (goal, work_item). agent_ext/trace_ext have no reverse O2M
yet → empty @api.depends() documented, deferred to P-OPS-2.
viin_ai_ops (MED):
- action_proposal.action_route: gate behind base.group_system so a
regular internal user (perm_write) cannot self-route a proposal to
'approved' via a low-risk auto_execute policy. Flow-lock unchanged.
Documented assumption: swap for a dedicated approval group in P-OPS-2.
- control_policy.model_id: documented as not-yet-matched (proposal has
no res_model field this phase) with an explicit P-OPS-2 TODO instead
of a silent no-op.
- trace form: insert the stat button_box before the first <sheet> group
(was position='inside' → rendered at sheet bottom); rename the div to
the canonical name='button_box'.
viin_ai_agent (HIGH/MED):
- test_stub_deprecation_contract: replace dead assertIsNotNone(env[...])
(env[] raises KeyError before the assert → ERROR not FAIL) with
assertIn(name, env.registry.models); add rogue-field contract tests so
the suite turns RED if a deprecated stub ever accumulates a field.
- stubs.py: docstring 'AbstractModels' → 'concrete Models'.
- pre-migrate 17.0.0.2.1: document the idempotency contract for future
SQL (no behaviour change).
viin_ai_chat (LOW):
- wrap the NONCE_MISMATCH content string in _() for i18n parity with
the NO_PENDING branch.
|
Failed
|
|
|
|
|
|
|
|
merged
[ADD] viin_ai_ops: Operating Layer foundation (P-OPS-1)
|
[FIX] viin_ai_chat: absent pending tool call → NO_PENDING, not a server warning
Root cause (not the symptom): /viin_ai/chat/tool_confirm raised
UserError('No pending tool call: %s') whenever _load_pending_tool
returned None. But an absent pending state is a NORMAL, designed-in
client condition — the proposal was already handled, the single-use
nonce was burned, the session-stored state expired, or the user
double-submitted the dialog. Raising UserError from a JSON route makes
Odoo log it at WARNING on the odoo.http logger, so this fired for
ordinary user behaviour in production (and surfaced on runbot as
'Subbuild #...: No pending tool call: wi15-nonce-burn-001').
The sibling NONCE_MISMATCH condition already returns a structured,
FE-renderable error instead of raising. Make the no-pending branch do
the same: return {status:'error', error_code:'NO_PENDING'}. The agent
loop is never entered, nothing is logged, the FE renders a friendly
recovery message via its existing status==='error' branch.
This supersedes the earlier change that merely @mute_logger'd the
AC10 test — that hid the test warning while production logs kept
filling. Tests now assert the corrected contract (intent, not output):
- AC06: unknown/expired id → NO_PENDING + tool never executed
- AC10: post-burn retry → NO_PENDING (was: tolerate UserError)
docs/ai/security.md documents the NO_PENDING contract beside NONCE_MISMATCH.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[ADD] viin_ai_ops: Operating Layer foundation (P-OPS-1)
|
[FIX] viin_ai_chat: mute odoo.http in AC10 nonce-burn test
test_AC10_nonce_burned_after_mismatch intentionally triggers the
'No pending tool call' UserError on its step-2 retry (the pending
state was burned after the prior NONCE_MISMATCH). Odoo's JSON
dispatcher logs that UserError at WARNING on the odoo.http logger,
which runbot surfaces as a build warning (Subbuild #384049:
'No pending tool call: wi15-nonce-burn-001').
The expected-error sibling tests in this class (AC01/02/03/06)
already carry @mute_logger('odoo.http', ...); AC10 was added in
M1 W5 without it. Apply the same decorator so the deliberately
provoked warning no longer pollutes the log. No behavior or
assertion change.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[ADD] viin_ai_ops: Operating Layer foundation (P-OPS-1)
|
[ADD] viin_ai_ops: Operating Layer foundation (P-OPS-1)
New Community-compatible module sitting between the AI capability layer
(viin_ai_agent) and external approval/workflow systems. Provides four
operating objects and the side-effect flow lock from operating-model.md §3.
Models:
- viin.ai.goal: typed operational goal (KPI / initiative / routine / risk_control)
- viin.ai.work.item: authoritative task state (draft -> in_progress -> done/cancelled)
- viin.ai.action.proposal: a side-effecting action awaiting governance routing
- viin.ai.control.policy: sequence-ordered rule routing proposals to
auto_execute / require_approval / block
Side-effect flow lock: medium/high risk proposals can never auto-execute;
action_route() forces them to 'pending' regardless of policy, and an
unmatched proposal defaults to 'pending' (safe default).
Smart buttons: Work Items count on viin.ai.agent; Proposals count on
viin.ai.trace. Multi-company ir.rule on all four models; control.policy is
read-only for standard users.
Also marks the viin.ai.schedule / viin.ai.approval.chain stub anchors in
viin_ai_agent as deprecated (module + class docstrings, removal checklist,
"deprecated" in _description) and adds a non-destructive migration scaffold.
Deprecation is documentation-only by design: no per-registry-load warning,
which would flood server/CI logs on every startup. FK anchors are kept
intact until the relations are migrated in a later phase.
Tests: 46 (viin_ai_ops) + stub-deprecation contract; full suite green
(0 failed, 0 error of 104 across viin_ai_ops + viin_ai_agent).
|
Killed
|
|
|
|
|
|
|
|
merged
[ADD] viin_ai_ops: Operating Layer foundation (P-OPS-1)
|
[ADD] viin_ai_ops: Operating Layer foundation (P-OPS-1)
New Community-compatible module sitting between the AI capability layer
(viin_ai_agent) and external approval/workflow systems. Provides four
operating objects and the side-effect flow lock from operating-model.md §3.
Models:
- viin.ai.goal: typed operational goal (KPI / initiative / routine / risk_control)
- viin.ai.work.item: authoritative task state (draft -> in_progress -> done/cancelled)
- viin.ai.action.proposal: a side-effecting action awaiting governance routing
- viin.ai.control.policy: sequence-ordered rule routing proposals to
auto_execute / require_approval / block
Side-effect flow lock: medium/high risk proposals can never auto-execute;
action_route() forces them to 'pending' regardless of policy, and an
unmatched proposal defaults to 'pending' (safe default).
Smart buttons: Work Items count on viin.ai.agent; Proposals count on
viin.ai.trace. Multi-company ir.rule on all four models; control.policy is
read-only for standard users.
Also marks the viin.ai.schedule / viin.ai.approval.chain stub anchors in
viin_ai_agent as deprecated (docstring + registry warning) and adds a
non-destructive migration scaffold. FK anchors are intentionally kept intact
until the relations are migrated in a later phase.
Tests: 46 (viin_ai_ops) + stub-deprecation contract; full suite green.
|
Failed
|
|
|
|
|
|
|
|
merged
[DOC] docs,ci: close M1 — SQL template ACL ✅ + Runbot-only CI model + local pre-commit hook
|
[REM] docs: purge viinForge residue — deprecated product
viinForge (orchestration platform/wrapper) đã bị loại bỏ. Gỡ toàn bộ 13 reference
trên 9 file:
- CONTRIBUTING.md: thay wrapper ~/viinforge/ops/scripts/run_{tests,odoo}.sh + venv rỗng
viindoo17 bằng lệnh odoo-bin native (venv odoo17 + addons-path deps) — giải luôn gốc
'không biết cách chạy test'.
- conventions.md, brain/architecture.md, brain/ui-design.md: gỡ con trỏ tới
viinForge workspace/knowledge/reuse-map, giữ quy tắc cứng inline.
- decisions/wi-001, wi-003, history/release-notes-v1.3, contenteditable_policy: gỡ con trỏ
audit/pattern/worktree tới viinforge repo (dead pointer), giữ nội dung coherent.
- brain/mockups/v3/ui_design_v3.html: gỡ checklist item 'viinForge Monitor TUI' (demo data).
Verify: grep -rIni viinforge + git grep = 0 match. Append-only decision records chỉ gỡ
dead pointer, không sửa nội dung quyết định.
|
Killed
|
|
|
|
|
|
|
|
merged
[DOC] docs,ci: close M1 — SQL template ACL ✅ + Runbot-only CI model + local pre-commit hook
|
[DOC] docs,ci: close M1 — SQL template ACL ✅ + Runbot-only CI model + local pre-commit hook
Phase 3.7 carry-over / Gate M1 closure:
- roadmap §5 + security.md checklist: tick SQL template ACL (evidence: sql_template.py
_check_sql_safety + ACL CTE __acl_allowed + group_ids allowlist + JSON Schema params;
test_sql_template.py ma trận ACL/multi-company all pass).
- roadmap §6: định nghĩa CI gate = Viindoo Runbot 'CI/runbot' (Runbot-only theo chuẩn org,
tvtmaaddons cũng zero GitHub Actions); gỡ giả định P15 tour gate khóa bởi Chrome runner
GitHub Actions — tour chạy trên Runbot. Đồng bộ M1 condition + Track B refs + REPO_LAYOUT.
- tooling: thêm husky + mở rộng lint-staged (ruff check/format cho *.py) làm lớp verify local;
CONTRIBUTING hướng dẫn chạy test qua odoo-bin --test-tags (không phải pytest thuần).
Why: PR #34 chủ động drop CI repo-specific để dùng Runbot external; docs cần khớp thực tế
để M1 đóng sạch và dev có lớp verify nhanh trước push.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
M1 Wave 1: 3 security HIGH gap closure (trace ir.rule + nl_query sanitize + confirmation nonce)
|
[REMOVE] ci: drop all repo-specific CI — workflows + scripts (PR #34)
Stop maintaining ai17's own GitHub Actions CI; rely on the external Viindoo Runbot check instead.
Deleted: .github/workflows/{brain-tours,ci,preflight-pgvector}.yml; ops/ci/* (5 files); tools/brain_v3_ci_check.py; docs/brain/ci-tour-gate.md (runbook for the removed gate).
Docs cleaned (no history/changelog touched): README.md drops CI Tour Gate link; pgvector_install.md replaces check_pgvector.sh with inline psql, removes the CI section, renumbers §6-§8; tools/README.md drops the brain_v3_ci_check.py section + pre-push runbook.
Part of Wave 1 — PR #34.
|
Killed
|
|
|
|
|