Pending: 0 Building: 0 Running: 5 Failed: 223
Created Date Type Name Commit Description State Age Up Time Life Time Action
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
merged M1 Wave 1: 3 security HIGH gap closure (trace ir.rule + nl_query sanitize + confirmation nonce) [REVERT] .github: back out CI runner switch — return to GitHub-hosted (PR #34) Per decision to abandon the self-hosted Viindoo Runbot switch (M1 W4/W6): - ci.yaml: restore all 5 jobs to runs-on: ubuntu-latest + setup-python steps - preflight-pgvector.yml: restore ubuntu-22.04 + original branch/path triggers Both files now match origin/17.0 byte-for-byte. Security fixes (trace ir.rule, nl_query sanitize, confirmation nonce) untouched. ai17 returns to GitHub-hosted runners; quota strategy handled separately. Part of Wave 1 — PR #34. Killed Not finished
merged M1 Wave 1: 3 security HIGH gap closure (trace ir.rule + nl_query sanitize + confirmation nonce) [FIX] viin_ai_*: root-cause boil-the-lake of /code-review findings — preflight self-hosted + SSOT + dedupe + aggregation fixture + nonce burn (M1 W6) Root-cause fix all 5 findings from /code-review of PR #34. Per boil-the-lake, no shortcuts. 1. preflight-pgvector.yml: switch runs-on from ubuntu-22.04 to [self-hosted, "Viindoo Runbot"]. Root cause of "preflight dead" was org-Free-tier GH Actions quota exhaustion, not workflow code (proven by run 26076812824 which succeeded in 38s before quota hit). Self-hosted Runbot has identical pgvector/pgvector:0.8.2-pg14 service container confirmed via brain-tours.yml. 2. viin_ai_search/controllers/search_controller.py: error message now uses MAX_NL_QUERY_CHARS constant via %d interpolation (SSOT — CLAUDE.md Rule 10). 3. viin_ai_base/tests/test_trace_acl.py: remove duplicate .browse().read() call in test_user_cannot_read_other_users_trace — the first call's result was discarded, dead code. 4. viin_ai_search/tests/test_search_controller.py: replace TODO stub with full integration test for the aggregation path — seeds topic+template+ provider, mocks call_completion, asserts <untrusted_context> wrap in the captured user message of the aggregation LLM call. 5. viin_ai_chat/controllers/chat_controller.py: nonce now single-use — _clear_pending_tool called on NONCE_MISMATCH to burn pending state. Adds AC10 test. Updates security.md §3.1 to document single-use semantics. Part of Wave 1 — PR #34. Killed Not started Not finished
merged [REF] all modules: switch to short version scheme (strip 17.0. prefix) [REF] all modules: switch to short version scheme (strip 17.0. prefix) Odoo recommends short version format (x.y.z) over full (17.0.x.y.z) so that porting to a new Odoo series requires no manifest version bump. - 11 __manifest__.py: strip 17.0. prefix - docs/ai/README.md, roadmap.md, conventions.md, migration.md: update version references and remove stale note about viin_ai_rag alignment - viin_ai_rag/migrations/17.0.0.1.0/pre-migrate.py: update docstring - viin_brain/tests: update migration version reference in comment Migration folder 17.0.0.1.0/ kept as-is (must match what Odoo stored in DB on existing installs). Killed
merged [REF] all modules: switch to short version scheme (strip 17.0. prefix) [REF] all modules: switch to short version scheme (strip 17.0. prefix) Odoo recommends short version format (x.y.z) over full (17.0.x.y.z) so that porting to a new Odoo series requires no manifest version bump. - 11 __manifest__.py: strip 17.0. prefix - docs/ai/README.md, roadmap.md, conventions.md, migration.md: update version references and remove stale note about viin_ai_rag alignment - viin_ai_rag/migrations/17.0.0.1.0/pre-migrate.py: update docstring - viin_brain/tests: update migration version reference in comment Migration folder 17.0.0.1.0/ kept as-is (must match what Odoo stored in DB on existing installs). Killed Not finished
merged [Phase 3.7] Gate M1 carry-over: SQL templates, aggregation router, AI Draft smart button, Brain bridge deprecate [FIX] viin_brain: remove orphan test_ir_http.py — ir_http.py deleted in f815901 (TipTap rejected) Commit f815901 deleted viin_brain/models/ir_http.py and its __init__ import when TipTap editor was rejected in favour of OdooEditor (ADR-003 update). The companion test file viin_brain/tests/test_ir_http.py was not removed, leaving 2 HttpCase tests (test_session_info_includes_editor_backend_flag, test_session_info_defaults_to_odoo_when_invalid) that assert on viin_web_editor_editor_backend in session_info — a key that no longer exists because the injector (ir_http.py) was deleted. Runbot build 381692 (rb-84f5047-221719) caught both failures: AssertionError: None != 'tiptap' AssertionError: None != 'odoo' Fix: delete test_ir_http.py and remove its import from tests/__init__.py. No functional change — the WI-11 classmethod regression guard is no longer needed since the override that triggered it no longer exists. Killed
merged [Phase 3.7] Gate M1 carry-over: SQL templates, aggregation router, AI Draft smart button, Brain bridge deprecate [REF] viin_ai_account,viin_ai_sale,viin_ai_stock: remove <delete> migration guards — no existing installs to upgrade Failed
merged [Phase 3.7] Gate M1 carry-over: SQL templates, aggregation router, AI Draft smart button, Brain bridge deprecate [FIX] viin_ai_account,viin_ai_sale,viin_ai_stock: add <delete> to remove legacy viin_ai_agent placeholder records on install (UniqueViolation fix) Killed Not started Not finished