Pending: 0 Building: 0 Running: 9 Failed: 178
Created Date Type Name Commit Description State Age Up Time Life Time Action
merged [ADD] viin_ai_helpdesk, viin_ai_crm: Phase 4 AI connectors (helpdesk seed + CRM advisory tools) [REM] docs, viin_ai_account, viin_ai_skill, viin_brain_account_reports: drop superseded TT200 accounting refs TT200 (Thong tu 200/2014/TT-BTC) is superseded by TT99 (Thong tu 99/2025). TT133 (Thong tu 133/2016, for SMEs) and TT99 both remain in effect and are kept. - Replace every TT200 reference with its successor TT99: docs (roadmap, architecture diagram, data-models), README, the account connector manifest + seed comment, the accounting skill pack, and the brain account-reports bridge. The architecture box-drawing diagram keeps its column alignment. - Keep TT133 and TT99 (both current): the accounting skill pack teaches TT99 (enterprises) / TT133 (SMEs). - Attribute receivable-provision rates to the current doubtful-debt provisioning regulation generically, not to the accounting regime. The "200h" effort figure in adr-008 is hours, not a circular, and is left untouched (ADRs are append-only). Succeed
merged [ADD] viin_ai_helpdesk, viin_ai_crm: Phase 4 AI connectors (helpdesk seed + CRM advisory tools) [IMP] viin_ai_agent, viin_ai_crm, viin_ai_helpdesk: strip HTML from AI tool payloads Connector tools fed raw Html-field values (lead/ticket description, activity note, message body) straight into the payload the LLM reads - HTML tags are token noise and can confuse the model. - viin_ai_agent: add _tool_plain_text on ir.actions.server, a sibling of _tool_result that runs a value through html2plaintext ('' for falsy), callable from a server-action code body (safe_eval). Mirrors the existing viin_ai_chat plain-text helper; no viin_brain coupling. - viin_ai_crm: strip crm.lead.description and mail.activity.note. - viin_ai_helpdesk: strip viin.helpdesk.ticket.description and mail.message.body (strip first, then truncate so the cap applies to plain text). - Surveyed the other connectors (account/sale/sale_crm/stock) and the AI/Brain tool surface: no other tool returns a raw Html field (Brain already returns the content_plain T1 representation). - Tests: red-green behaviour tests assert the payload value carries no '<' and preserves the readable text, driven through a real ir.actions.server.run() as the tool's low-privilege group. - docs: note the _tool_plain_text sibling next to _tool_result in ai/architecture.md so future connectors strip Html-field values. Killed Not finished
merged [ADD] viin_ai_helpdesk, viin_ai_crm: Phase 4 AI connectors (helpdesk seed + CRM advisory tools) [IMP] viin_ai_agent, viin_ai_crm, viin_ai_helpdesk: strip HTML from AI tool payloads Connector tools fed raw Html-field values (lead/ticket description, activity note, message body) straight into the payload the LLM reads - HTML tags are token noise and can confuse the model. - viin_ai_agent: add _tool_plain_text on ir.actions.server, a sibling of _tool_result that runs a value through html2plaintext ('' for falsy), callable from a server-action code body (safe_eval). Mirrors the existing viin_ai_chat plain-text helper; no viin_brain coupling. - viin_ai_crm: strip crm.lead.description and mail.activity.note. - viin_ai_helpdesk: strip viin.helpdesk.ticket.description and mail.message.body (strip first, then truncate so the cap applies to plain text). - Surveyed the other connectors (account/sale/sale_crm/stock) and the AI/Brain tool surface: no other tool returns a raw Html field (Brain already returns the content_plain T1 representation). - Tests: red-green behaviour tests assert the payload value carries no '<' and preserves the readable text, driven through a real ir.actions.server.run() as the tool's low-privilege group. Killed Not finished
merged [ADD] viin_ai_helpdesk, viin_ai_crm: Phase 4 AI connectors (helpdesk seed + CRM advisory tools) [ADD] viin_ai_helpdesk, viin_ai_crm: Phase 4 AI connectors (helpdesk seed + CRM advisory tools) Phase 4 (Track C) per-app AI connectors, unlocked by M3: - viin_ai_helpdesk (NEW): AI connector for viin_helpdesk. Seeds 1 agent (Helpdesk Assistant) + 1 topic (Helpdesk Triage) + 3 READ-only tools on viin.helpdesk.ticket: triage_ticket, suggest_canned_response, generate_faq_from_thread. Data-XML, zero Python (Layer-3 convention). - viin_ai_crm (IMP): adds 3 advisory READ tools (qualify_lead, next_action_suggest, email_draft_for_lead) wired into the Lead Qualification topic. - All tools: requires_confirmation=False (read-only), runs_as_sudo=False, group_ids ACL-gated; model_id write-gate satisfied (group has perm_write on the target model). run()-driven boundary tests prove the safe_eval + write-gate path. - docs: AGENTS.md + docs/roadmap.md reflect Phase 4 progress; reconcile a pre-existing roadmap drift (helpdesk dependency named the Odoo-EE 'helpdesk' module instead of Viindoo 'viin_helpdesk'). Killed Not finished
merged [IMP] viin_ai_base, viin_ai_agent: bidirectional data-driven message adapter (close #64 #62 #63) [IMP] viin_ai_base, viin_ai_agent: bidirectional data-driven message adapter (close #64 #62 #63) Symmetric follow-up to the outgoing adapter (#59): make the INCOMING response normalizer data-driven via viin.ai.message.adapter, add a per-vendor empty-content knob, and route the streaming path through the adapter with a contract guard. - #64: viin.ai.message.adapter gains a `direction` discriminator (outgoing|incoming) + incoming response-map knobs; viin.ai.provider._normalize_completion_response is rewired to dispatch data-driven incoming primitives (output byte-stable for the 3 seeded protocols). An admin can hotfix a vendor response-key rename with no release. - #62: per-vendor `empty_content_repr` knob (empty_string|null|omit), default empty_string (byte-identical to prior behaviour) for strict gateways (Azure OpenAI). - #63: viin_ai_agent._do_llm_stream redacts-then-adapts before transport + a contract guard test that fails if a future streaming multi-turn path bypasses the adapter (also patches a latent streaming PII-redaction gap). - Hardening (post code-review): incoming rows must declare all leaf-key paths (finish/usage/text/tool); incoming rows for an unsupported protocol are rejected at write time (no silent mis-parse); clearer parse-error diagnostics; tightened guard tests (per-location PII, value-switch data-driven proof, assertRaises). - ADR-018 (bidirectional data-driven message adapter); docs/roadmap/module-map reconcile (32 modules, PR #57/#59/#60 history, ADR ledger, adr-015/016 status sync). Tests: Odoo native, no-API-key; 302 non-tour tests green (normalize/wire byte-stable non-regression + incoming data-driven proof + #62 matrix + #63 guard + multiturn loop + new validation guards). flake8 clean on the Runbot lint gate. Pre-existing HttpCase browser tours need a live http server and fail identically on base 17.0 under --no-http. Claude-Session: https://claude.ai/code/session_01MpghN1mdjfEzHd1yuSqdut Succeed
merged [IMP] viin_ai_base, viin_ai_agent: bidirectional data-driven message adapter (close #64 #62 #63) [IMP] viin_ai_base, viin_ai_agent: bidirectional data-driven message adapter (close #64 #62 #63) Symmetric follow-up to the outgoing adapter (#59): make the INCOMING response normalizer data-driven via viin.ai.message.adapter, add a per-vendor empty-content knob, and route the streaming path through the adapter with a contract guard. - #64: viin.ai.message.adapter gains a `direction` discriminator (outgoing|incoming) + incoming response-map knobs; viin.ai.provider._normalize_completion_response is rewired to dispatch data-driven incoming primitives (output byte-stable for the 3 seeded protocols). An admin can hotfix a vendor response-key rename with no release. - #62: per-vendor `empty_content_repr` knob (empty_string|null|omit), default empty_string (byte-identical to prior behaviour) for strict gateways (Azure OpenAI). - #63: viin_ai_agent._do_llm_stream redacts-then-adapts before transport + a contract guard test that fails if a future streaming multi-turn path bypasses the adapter (also patches a latent streaming PII-redaction gap). - Hardening (post code-review): incoming rows must declare all leaf-key paths (finish/usage/text/tool); incoming rows for an unsupported protocol are rejected at write time (no silent mis-parse); clearer parse-error diagnostics; tightened guard tests (per-location PII, value-switch data-driven proof, assertRaises). - ADR-018 (bidirectional data-driven message adapter); docs/roadmap/module-map reconcile (32 modules, PR #57/#59/#60 history, ADR ledger, adr-015/016 status sync). Tests: Odoo native, no-API-key; 302 non-tour tests green (normalize/wire byte-stable non-regression + incoming data-driven proof + #62 matrix + #63 guard + multiturn loop + new validation guards). Pre-existing HttpCase browser tours need a live http server and fail identically on base 17.0 under --no-http (not part of this change). Claude-Session: https://claude.ai/code/session_01MpghN1mdjfEzHd1yuSqdut Failed
merged [IMP] viin_ai_base, viin_ai_agent: bidirectional data-driven message adapter (close #64 #62 #63) [IMP] viin_ai_base, viin_ai_agent: bidirectional data-driven message adapter (close #64 #62 #63) Symmetric follow-up to the outgoing adapter (#59): make the INCOMING response normalizer data-driven via viin.ai.message.adapter, add a per-vendor empty-content knob, and route the streaming path through the adapter with a contract guard. - #64: viin.ai.message.adapter gains a `direction` discriminator (outgoing|incoming) + incoming response-map knobs; viin.ai.provider._normalize_completion_response is rewired to dispatch data-driven incoming primitives (output byte-stable for the 3 seeded protocols). An admin can hotfix a vendor response-key rename with no release. - #62: per-vendor `empty_content_repr` knob (empty_string|null|omit), default empty_string (byte-identical to prior behaviour) for strict gateways (Azure OpenAI). - #63: viin_ai_agent._do_llm_stream redacts-then-adapts before transport + a contract guard test that fails if a future streaming multi-turn path bypasses the adapter (also patches a latent streaming PII-redaction gap). - ADR-018 (bidirectional data-driven message adapter); docs/roadmap/module-map reconcile (32 modules, PR #57/#59/#60 history, ADR ledger, adr-015/016 status sync). Tests: Odoo native, no-API-key; 74-class net green (normalize/wire byte-stable non-regression + incoming data-driven proof + #62 matrix + #63 guard + multiturn loop). Claude-Session: https://claude.ai/code/session_01MpghN1mdjfEzHd1yuSqdut Failed
merged [ADD] KG temporal Wave-2 (ADR-006): _as_of reconstruction + live KG tools + memory-brain bridge [FIX] viin_ai_brain: revert record_historian as-of boundary to inclusive-at-ts + lock-in test verify2 (review iter-2) flagged a silent, undocumented boundary flip in record_historian._as_of_record Step-4: the tracking-message revert filter had been changed from ('date','>',ts) to '>=', making a change stamped exactly at ts get reverted (exclusive-at-ts) - the opposite of the whole-codebase convention (viin_brain._find_active_at inclusive-at-ts; memory recall <= as_of; the Step-2 create_date boundary in the same method). "State as of ts" must reflect a change effective at ts, so revert only strictly-after-ts changes. - record_historian.py: ('date','>=',ts) -> ('date','>',ts) + convention comment. - test_record_historian.py: add boundary-exact lock-in test H7 (RED under '>=' AssertionError partner_before reverted; GREEN under '>'). Claude-Session: https://claude.ai/code/session_01EVjERtZ9dnk5fhZHns8vLv Succeed
merged [ADD] KG temporal Wave-2 (ADR-006): _as_of reconstruction + live KG tools + memory-brain bridge [FIX] viin_ai: PR #60 review-2 - W8150 lint blocker + record_state_as_of ACL guard + temporal test hardening - viin_ai_brain: fix Runbot test_pylint W8150 (module-level relative import in test_record_historian.py); add caller-env ACL gate (check_access_rights/rule) before record_state_as_of reconstruction, return {} on AccessError; RED-GREEN test as low_priv; perm_write=1 lock-in test + rationale. - viin_ai_memory: neutralize-proof UUID tokens in as-of recall tests; defensive str as_of normalize via fields.Datetime.to_datetime + cross-module contract comment. - viin_brain: boundary-exact active-at fixtures (valid_to==ts / valid_from==ts) guarding the inclusive operator; expression.AND for temporal domain combine. - viin_ai_memory_brain: explicit required=False/default=False on vault_id. - docs: propagate _search_active_at -> _find_active_at; correct stale 2-condition active-at domain to the shipped 3-condition predicate (ADR-006 append-only Wave-2 note). Claude-Session: https://claude.ai/code/session_01EVjERtZ9dnk5fhZHns8vLv Killed Not finished
merged [ADD] KG temporal Wave-2 (ADR-006): _as_of reconstruction + live KG tools + memory-brain bridge [FIX] viin_ai: PR #60 review-2 - W8150 lint blocker + record_state_as_of ACL guard + temporal test hardening - viin_ai_brain: fix Runbot test_pylint W8150 (module-level relative import in test_record_historian.py); add caller-env ACL gate (check_access_rights/rule) before record_state_as_of reconstruction, return {} on AccessError; RED-GREEN test as low_priv; perm_write=1 lock-in test + rationale. - viin_ai_memory: neutralize-proof UUID tokens in as-of recall tests; defensive str as_of normalize via fields.Datetime.to_datetime + cross-module contract comment. - viin_brain: boundary-exact active-at fixtures (valid_to==ts / valid_from==ts) guarding the inclusive operator; expression.AND for temporal domain combine. - viin_ai_memory_brain: explicit required=False/default=False on vault_id. - docs: propagate _search_active_at -> _find_active_at; correct stale 2-condition active-at domain to the shipped 3-condition predicate (ADR-006 append-only Wave-2 note). Claude-Session: https://claude.ai/code/session_01EVjERtZ9dnk5fhZHns8vLv Killed Not finished
merged [FIX] viin_ai_base: provider-native outgoing message adapter (#59) [FIX] viin_ai_base: provider-native outgoing message adapter (#59) The agentic tool loop shipped turn-2 canonical history (assistant tool_calls, tool role tool_results) to the wire verbatim - no outgoing adapter existed, only an incoming normalizer. A real tool-using turn returned HTTP 400 on anthropic_native/openai_compat and silently dropped the tool result on google_native. The same payload builder dropped the system prompt on all 3 protocols, and the PII redactor skipped tool-turn free-text (tool_calls arguments, tool_results result/error). Add a data-driven, UI-editable viin.ai.message.adapter (protocol-keyed with an optional vendor override, noupdate seed) that reshapes the canonical history into each provider's native wire format via vetted code primitives - zero eval/template on the BYOK egress path. A write-time coherence constraint rejects incoherent (protocol, shape/strategy) admin edits. System-prompt placement is handled per protocol (anthropic top-level system, google systemInstruction, openai role=system) with the system kwarg as the single source of truth, which also fixes viin_ai_search Path B on anthropic/google. PII redaction is extended to tool-turn free-text and runs before the adapt step (redact-then-adapt). Add honest tests that stub only the transport seam and assert the built outgoing payload per protocol (the seam #59 slipped through), plus data-driven, coherence-gate, and PII-redaction tests. Claude-Session: https://claude.ai/code/session_0128GTfwboHEZqJZ5xgfkxoL Succeed
merged [ADD] KG temporal Wave-2 (ADR-006): _as_of reconstruction + live KG tools + memory-brain bridge [FIX] viin_ai: address PR #60 review (temporal_diff ACL, BFS N+1, as-of test gaps, conventions) H-A: add ACL post-filter in tool_brain_temporal_diff - batch-search readable page ids after edge set-diff, drop edges where either endpoint is unreadable (same D6 fix class applied to graph_traverse in Wave-1). H-B: batch the fallback BFS _acl_filter call - collect all candidate to_page ids per BFS step BEFORE the ACL check (single Page.search per step, not one per link, mirroring the primary delegate path). H-C: add test_observation_recall_as_of_filters - protects the third temporal leg (created_at <= as_of on observations); RED-then-GREEN verified. M1: rename _search_active_at -> _find_active_at in viin.brain.link and all call sites in brain_tools.py and tests (avoids ORM _search_<field> prefix collision, no active_at field exists). M2: fix assertIsNotNone(link_a.superseded_at) -> assertTrue (Odoo Datetime null is False, not None; assertIsNotNone(False) passes silently). M3: replace (6, 0, [...]) legacy tuples with Command.set([...]) in viin_ai_memory_brain/tests/test_vault_id_isolation.py (AGENTS.md #7). M4: move imports (datetime, fields.Datetime/Date) from inside function body to module top-level in brain_tools.py (python.md imports rule). Claude-Session: https://claude.ai/code/session_01JTYAzu8ndxtrQGJeTNa3pf Failed
merged [ADD] KG temporal Wave-2 (ADR-006): _as_of reconstruction + live KG tools + memory-brain bridge [ADD] viin_ai_memory_brain: AI memory <-> Brain vault bridge (KG temporal Wave-2) New auto-install bridge (depends viin_ai_memory + viin_brain). Adds an optional vault_id Many2one('viin.brain.vault') to viin.ai.memory so a memory can be scoped to a Brain vault, plus a GLOBAL ir.rule that restricts memory visibility to vaults the user can access (member or access-group), while vault-less memories remain governed by the existing per-owner rule. The vault rule is GLOBAL (AND-combines = restricts), not non-global: a non-global rule with an OR(vault_id=False, ...) disjunct would OR-union with the owner rule and leak every vault-less memory across owners. Tests cover cross-vault, cross-company, owner-only vault-less, and the cross-owner vault-less regression. Part of ADR-006 KG temporal Wave-2. Claude-Session: https://claude.ai/code/session_01JTYAzu8ndxtrQGJeTNa3pf Failed
merged [REF] viin_ai: standardize AI tool server-action return envelope as ir.actions.client [REF] viin_ai_stock: return AI tool results via the standard envelope helper The 3 stock tools (get_stock_level, get_low_stock_products, get_product_moves) now use env['ir.actions.server']._tool_result(payload); tests assert the ir.actions.client envelope (tag viin_ai_tool_result) under params['data']. Claude-Session: https://claude.ai/code/session_0128GTfwboHEZqJZ5xgfkxoL Killed Not finished
merged [FIX] viin_ai_account/stock: ADR-012 seed runtime crashes + review-driven hardening [IMP] viin_brain: reconcile linkable docstring with ADR-012 carrier reality The viin_brain_linkable docstring still claimed the per-app bridges "explicitly inherit this mixin" (their concrete models were deleted) and that "WI-7 will set installable:False" (contradicted by the ADR-012 revive). Correct both claims; the _brain_form_sidebar flag and all logic are untouched. Claude-Session: https://claude.ai/code/session_01JTYAzu8ndxtrQGJeTNa3pf Succeed
merged [REF] viin_ai: standardize AI tool server-action return envelope as ir.actions.client [REF] viin_ai_search: tidy stale arguments wording in web_query tool comment/docstring Replace residual 'tool_web_query(arguments)' prose (which implied 'arguments' is a safe_eval dispatch-scope variable) with 'tool_web_query(...)'. The bare 'arguments' is the method parameter name, not a server-action context variable. Comment/docstring only - no behavior change. Claude-Session: https://claude.ai/code/session_0128GTfwboHEZqJZ5xgfkxoL Killed
merged [REF] viin_ai: standardize AI tool server-action return envelope as ir.actions.client [REF] viin_ai_search: return web_query result via the standard envelope helper Rewrite the web_query server-action body to build its result through `env['ir.actions.server']._tool_result(...)`, returning a valid ir.actions.client action instead of the non-standard `tool_result` dict. Update the header comment and the server-action test to assert the new envelope while preserving the domain/fallback_used assertions. Killed Not finished
merged [IMP] ADR-012: per-app seed master data - 9 modules (AI connectors + Brain bridges) [DOC] AGENTS,docs,README: reconcile full ADR-012 per-app seed (9 modules) + correct tool names + viin_brain_sale rename Succeed
merged [IMP] ADR-012: per-app seed master data - 9 modules (AI connectors + Brain bridges) [DOC] AGENTS,docs,README: reconcile ADR-012 CRM+Sale seed state + viin_brain_sale rename across docs Killed Not finished
merged [FIX] viin_ai_brain: tool server-actions read args from context (fix NameError through agent loop) [FIX] viin_ai_search: correct stale tool-args injection comment The comment claimed `viin_ai_tool_args` is mapped to `arguments` via a `_get_eval_context` override; no such override exists. The server-action already reads its arguments via env.context.get('viin_ai_tool_args', {}). Comment only - no code change. Killed Not finished
merged [IMP] ADR-012: per-app seed master data - 9 modules (AI connectors + Brain bridges) [DOC] AGENTS,docs,README: reconcile ADR-012 CRM+Sale seed state + viin_brain_sale rename across docs Revoked
merged [IMP] ADR-012: per-app seed master data - 9 modules (AI connectors + Brain bridges) [DOC] AGENTS,docs,viin_brain: rename viin_brain_sale_management -> viin_brain_sale references + ADR-012 addendum Killed Not finished
merged [IMP] ADR-012: per-app seed master data - 9 modules (AI connectors + Brain bridges) [IMP] viin_ai_sale_crm: cross-domain handoff topic + targeted auto_install (ADR-012) Killed Not finished
merged [ADD] AI Cognitive Wave-1: Memory L0-L3 + Skill runtime + Pulse cost gate + KG temporal (supersedes #53) [FIX] viin_ai: clear Runbot errors and warnings (PR #54) Resolve every Runbot build error and warning on the Cognitive Wave-1 branch without masking any behavior. Each fix is root-cause proven and verified by a real Odoo 17 test run on a fresh DB (--skip-auto-install). - viin_ai_chat: fix stale mock signature in test_user_error_passed_through. _user_err now matches _collect_allowed_tools(self, applied_skills=None), which viin_ai_skill's _resolve_run_skills override calls positionally. This was the only build-breaking failure. - viin_ai_base: guard the _record_error_usage separate-cursor write so a provider_id/model_id not yet visible to that cursor is set NULL instead of raising a ForeignKeyViolation. Removes the odoo.sql_db ERROR noise while preserving the trace-durability invariant. Add a regression test that verifies via a fresh cursor (the row is committed by a separate cursor, invisible to the REPEATABLE READ test cursor). - viin_ai_approval: remove the dead advisory tool ACL-gap warning in _advisory_agent_has_tools (unreachable - every caller runs sudo, so it could never detect a real gap). Assert the H8 executable-tools refusal warning via assertLogs. Add mail_notify_force_send=False to TestAdvisoryInfra.setUpClass. - viin_ai_memory: align the promotion.log / observation owner ir.rule perms with ir.model.access.csv (promotion.log read-only, observation append-only), clearing the to_base record-rule validator warning. Add ACL regression tests driven via with_user. Downgrade the BYOK no-provider summarize-skip log from WARNING to INFO (expected steady-state when no key is configured), and keep the cron test asserting its real contract (zero external call) rather than a data-state-dependent log. - viin_ai_agent: assert the sudo-escalation refusal security warning via assertLogs so the expected signal is captured, not leaked to the log. - viin_ai_ops / viin_ai_ops_brain / viin_ai_approval_sale: add the mail_notify_force_send=False test-context guard (OpsBaseTestCase and the evidence/advisory-sale test classes) so confirm/route/notify flows do not attempt a synchronous mail send that fails on a runner with no mail.catchall ICP. These tests assert routing/state, not email delivery. Succeed
merged [ADD] AI Cognitive Wave-1: Memory L0-L3 + Skill runtime + Pulse cost gate + KG temporal (supersedes #53) [FIX] viin_ai: clear Runbot errors and warnings (PR #54) Resolve every Runbot build error and warning on the Cognitive Wave-1 branch without masking any behavior. Each fix is root-cause proven and verified by a real Odoo 17 test run on a fresh DB (--skip-auto-install). - viin_ai_chat: fix stale mock signature in test_user_error_passed_through. _user_err now matches _collect_allowed_tools(self, applied_skills=None), which viin_ai_skill's _resolve_run_skills override calls positionally. This was the only build-breaking failure. - viin_ai_base: guard the _record_error_usage separate-cursor write so a provider_id/model_id not yet visible to that cursor is set NULL instead of raising a ForeignKeyViolation. Removes the odoo.sql_db ERROR noise while preserving the trace-durability invariant (the error usage.log is still committed independently). Add a regression test that verifies via a fresh cursor. - viin_ai_approval: remove the dead advisory tool ACL-gap warning in _advisory_agent_has_tools (unreachable - every caller runs sudo, so it could never detect a real gap); this clears the false-positive log. Assert the H8 executable-tools refusal warning via assertLogs instead of letting it leak. Add mail_notify_force_send=False to TestAdvisoryInfra.setUpClass to stop the mail.catchall send ERROR. - viin_ai_memory: align the promotion.log / observation owner ir.rule perms with ir.model.access.csv (promotion.log read-only, observation append-only), clearing the to_base record-rule validator warning. Assert the BYOK no-provider skip warning via assertLogs. Add ACL regression tests driven via with_user. - viin_ai_agent: assert the sudo-escalation refusal security warning via assertLogs so the expected signal is captured, not leaked to the log. Failed
merged [ADD] AI Cognitive Wave-1: Memory L0-L3 + Skill runtime + Pulse cost gate + KG temporal (supersedes #53) [FIX] viin_ai: clear Runbot errors and warnings (PR #54) Resolve every Runbot build error and warning on the Cognitive Wave-1 branch without masking any behavior. Each fix is root-cause proven and verified by a real Odoo 17 test run on a fresh DB (--skip-auto-install). - viin_ai_chat: fix stale mock signature in test_user_error_passed_through. _user_err now matches _collect_allowed_tools(self, applied_skills=None), which viin_ai_skill's _resolve_run_skills override calls positionally. This was the only build-breaking failure. - viin_ai_base: guard the _record_error_usage separate-cursor write so a provider_id/model_id not yet visible to that cursor is set NULL instead of raising a ForeignKeyViolation. Removes the odoo.sql_db ERROR noise while preserving the trace-durability invariant (the error usage.log is still committed independently). Add a regression test that verifies via a fresh cursor. - viin_ai_approval: remove the dead advisory tool ACL-gap warning in _advisory_agent_has_tools (unreachable - every caller runs sudo, so it could never detect a real gap); this clears the false-positive log. Assert the H8 executable-tools refusal warning via assertLogs instead of letting it leak. Add mail_notify_force_send=False to TestAdvisoryInfra.setUpClass to stop the mail.catchall send ERROR. - viin_ai_memory: align the promotion.log / observation owner ir.rule perms with ir.model.access.csv (promotion.log read-only, observation append-only), clearing the to_base record-rule validator warning. Assert the BYOK no-provider skip warning via assertLogs. Add ACL regression tests driven via with_user. - viin_ai_agent: assert the sudo-escalation refusal security warning via assertLogs so the expected signal is captured, not leaked to the log. Failed
merged [ADD] AI Cognitive Wave-1: Memory L0-L3 + Skill runtime + Pulse cost gate + KG temporal (supersedes #53) [DOC] docs,AGENTS: reconcile Cognitive Wave-1 + ADR status drift ADR-009 ratified; module maps + roadmap + AGENTS.md status; ADR-015/016 append-only Accepted; testing.md verify cmd. Failed
merged [ADD] AI Cognitive Wave-1: Memory L0-L3 + Skill runtime + Pulse cost gate + KG temporal (supersedes #53) [DOC] docs,AGENTS: reconcile Cognitive Wave-1 + ADR status drift ADR-009 ratified; module maps + roadmap + AGENTS.md status; ADR-015/016 append-only Accepted; testing.md verify cmd. (= PR #53 docs reconcile.) Killed Not finished
closed [ADD] Phase 3.9 Cognitive Wave-1: Memory + Skill + KG temporal + Pulse + CoWork bundle [DOC] docs,AGENTS: reconcile Cognitive Wave-1 + ADR status drift (PR #53) PR #53 (Cognitive Wave-1) shipped viin_ai_memory/skill/pulse + KG temporal signature + CoWork 5-pack and ratified ADR-009, but the living status docs stayed stale. Reconcile across the count-bearing + map docs: - AGENTS.md §3: Track A now reflects M3 achieved (PR #50) + P-OPS-3/4 shipped; Phase 3.9 Cognitive Wave-1 marked shipped (PR #53), not future. - AGENTS.md §2 + docs/ai/architecture.md: add viin_ai_memory/skill/pulse (+ viin_ai_workflow) to the module / dependency maps with real depends. - docs/roadmap.md: ADR-009 footnotes/open-list resolved (3 separate modules, Accepted PR #53); W13-W17 drift-note (Wave-1 code shipped); ADR counts reconciled to 16 ADR (14 accepted + 2 proposed). - docs/decisions/README.md: ADR-015/016 status proposed -> accepted (PR #47); index date bumped. - adr-015/adr-016: append-only Status update line (Accepted, PR #47). - docs/ai/testing.md: add the 3 cognitive modules to the no-key verify command. Docs-only, no code touched. Killed Not finished
closed [ADD] Phase 3.9 Cognitive Wave-1: Memory + Skill + KG temporal + Pulse + CoWork bundle [FIX] viin_ai_memory,viin_ai_skill: Runbot lint (default manifest key + W391) - viin_ai_memory/__manifest__.py: remove auto_install=False (equals the default; the Viindoo manifest linter test_manifests rejects redundant default keys). - viin_ai_skill/tests/test_cowork_bundle.py: strip trailing blank line (flake8 W391). Killed Not finished
closed [ADD] Phase 3.9 Cognitive Wave-1: Memory + Skill + KG temporal + Pulse + CoWork bundle [FIX] viin_ai cluster,viin_brain: full-width text fields across form views (colspan sweep) Repo-wide sweep of the same layout bug fixed earlier: a free-text/tag field that is the sole (or last) child of a default two-column <group> renders squeezed into a one-character-wide column. The canonical Odoo idiom is colspan="2" (core sale_order_views.xml note field). Added it to 8 more fields: - viin_ai_agent: system_prompt (agent form), ai_prompt (ir.actions.server form) - viin_ai_base: final_response (trace form) - viin_ai_ops: payload (action proposal form), description (goal form) - viin_ai_approval: prompt_snapshot (action proposal form) - viin_brain: ambiguous_candidate_ids (link form) - viin_ai_skill: description (skill pack form) Pure view-XML (no SCSS/JS). Found by a form-view scan, pre-checked for layout edge cases (all default col=2 groups; no sibling-field regression), and verified full-width by live render after -u. Failed
closed [ADD] Phase 3.9 Cognitive Wave-1: Memory + Skill + KG temporal + Pulse + CoWork bundle [FIX] viin_ai_skill,viin_ai_memory,viin_ai_pulse: full-width text fields in form views The Level-1/Level-2 description fields (skill), the summary (memory) and the signal payload/evidence fields rendered squeezed into a one-character-wide column. A nolabel field inside a default two-column <group> lands in the narrow label slot; the canonical Odoo idiom (e.g. sale.order note, sale_order_views.xml) is to add colspan="2" so the field spans the full group width. - viin_ai_skill: description, manifest_text - viin_ai_memory: summary, access_group_ids - viin_ai_pulse: signal payload, evidence_refs Pure view-XML fix (no SCSS/JS). Verified full-width by live render. Failed
closed [ADD] Phase 3.9 Cognitive Wave-1: Memory + Skill + KG temporal + Pulse + CoWork bundle [FIX] viin_ai_base,viin_ai_agent,viin_ai_memory: Cognitive Wave-1 agent-loop remediation Independent review of the cluster surfaced 1 install blocker plus agent-loop defects that left Wave-1 tool/skill execution non-functional in production. - viin_ai_memory: declare the missing viin_ai_agent dependency. Standalone install crashed at Python import (the module imports prompt_safety and _inherits viin.ai.agent); it only worked when co-installed with skill/pulse. - viin_ai_base: _normalize_completion_response now emits stop_reason in all three provider branches (anthropic/google/openai). Without it the agent loop terminated after iteration 1 and no tool ever executed in production. - viin_ai_base: normalize tool_call shape to canonical {id,name,arguments} for anthropic (input->arguments) and openai (hoist function.name/arguments) so tools dispatch end-to-end on every provider, not just google_native. - viin_ai_agent: _execute_tool_call injects viin_ai_tool_args into the action context (tools were receiving empty args) and surfaces the tool_result data payload instead of a hardcoded 'executed'. Each fix ships with a RED-first test that drives the real production path (real normalizer, transport-only stub), closing the prior harness blind spot where mocks injected the missing stop_reason and masked the defect. Failed
closed [ADD] Phase 3.9 Cognitive Wave-1: Memory + Skill + KG temporal + Pulse + CoWork bundle [FIX] viin_ai cluster: Cognitive Wave-1 review remediation (10 HIGH + MED) End-of-wave code review (6 modules) found 0 CRITICAL, 10 HIGH, 14 MED. This pass fixes all 10 HIGH plus the cheap in-file MED, verified by a real Odoo native test run (all fix-target tests RED-before-green, now GREEN). Runtime / security HIGH: - viin_ai_memory: _bump_reference iterated to fix "Expected singleton" crash on a multi-record (L0|L1) recall set. - viin_ai_memory: L0 identity text now wrapped via wrap_untrusted_context() before system-prompt injection (prompt-injection surface, AGENTS.md #10). - viin_ai_base: google_native response normalizer now extracts functionCall parts (Gemini tool calls were silently dropped despite WI-H sending tools). Test-integrity HIGH (fixed in-scope, not deferred): - viin_ai_base: replaced tautological constraint-embed assertion with a real '[constraints:' check; removed a vendor-free test that never called production code and relocated it to viin_ai_agent calling the real _tool_schema(); restored the real copy-before-normalize mutate-guard (3 protocols, self-contained). - viin_ai_skill: removed TestCoworkBundleWebQueryToolExists (always-FAIL CI gate not controlled by the module under test). - viin_brain: added TestBrainLinkTemporalSupersession covering create/write auto-stamp + idempotency. Perf / seam HIGH: - viin_brain: batched _stamp_superseded_at (was N+1 writes). - viin_ai_agent: _recall_memory gains trace_id param (passed at both run / run_streaming call sites) so Memory stamps observations under the run trace without a second agent.py touch; removed a hasattr() presence probe on a hard-dependency method. MED: model-ordering (_sql_constraints before fields in skill/pulse), memory cron noupdate + child_of->in company_ids + promotion.log owner rule + decay date pre-filter, skill group_ids now enforced in the resolver (+test), pulse @api.depends + PASS-branch sync comment, google_native combiner stripping, brain partial index columns (from_page_id, valid_from). Failed
merged [FIX] AI cluster Wave-1: pre-existing security remediation (4 CRITICAL + 13 HIGH) [FIX] viin_ai_base,viin_ai_agent,viin_ai_approval,viin_ai_workflow: PR #52 review fixes (CI lint + findings) CI blocker (was the red Runbot flake8 + an error-log trip): - viin_ai_approval/tests/test_advisory_acl.py: add mail_notify_force_send=False to setUpClass (mirrors test_approval_adapter) so confirmed-request tests no longer trip Runbot had_error_log; drop the unused `req` local (flake8 F841). Security (RED-test-first, behavior tests with independent oracles): - viin_ai_approval: _build_advisory_messages now routes the untrusted block through the SSOT wrap_untrusted_context() instead of a hand-rolled fence, so an attacker </untrusted_context> close tag / jailbreak phrase in a display_name or signal can no longer escape the sandbox. New test_advisory_prompt_safety. - viin_ai_workflow: _scan_target company isolation uses child_of (the routine company + its branch descendants), not parent_of (ancestors). parent_of both dropped the routine's own branch rows (false negative) and pulled the parent company's rows into scope (upward cross-tenant leak). New branch-hierarchy test. Robustness / accuracy: - viin_ai_base usage_log: action_replay fallback narrowed by (id > pre_max) so a concurrent replay of the same source cannot resolve the wrong log. New test. - viin_ai_base provider: call_embedding decodes response.json() once; _record_error_usage comment corrected to match the real flush ordering. - viin_ai_approval: advisory cron `limit` docstring (per company per tick); diagnostic warning when an advisory agent's tools read empty (ACL gap). - viin_ai_agent: _wrap_tool_result docstring corrected (args is FE-display-only on the pending-confirmation early return, not "trusted"); fix the stale test name in the finding-to-test map. Verified: odoo-bin --test-enable on a fresh pg14 DB - 0 failed / 0 error of 291 tests across viin_ai_base/agent/approval/workflow; flake8 (Runbot config) clean on all changed lines (F841 resolved). Killed Not finished
merged [FIX] AI cluster Wave-1: pre-existing security remediation (4 CRITICAL + 13 HIGH) [FIX] viin_ai_approval,viin_ai_base: end-of-wave review fixes (H7 effective scoping, error-log savepoint) Independent end-of-wave review caught two HIGH issues in the wave-1 fixes: - H7 RESIDUAL: the WI-3 advisory-cron company scope used ('company_id','in', self.env.companies.ids), but in the cron context env.companies = ALL companies (cron user has no allowed_company_ids) -> the scope was a no-op = cross-tenant processing leak, and its test was tautological (it pre-set allowed_company_ids). Fixed: pin the advisory cron user_id, and restructure _cron_run_ai_advisory to iterate companies explicitly with a literal ('company_id','=',company.id) filter under with_company(company) - isolation no longer depends on env.companies. The test now runs from the real all-company env and goes RED on the old code (scan saw companies [1,2,3,4]) -> GREEN. - error-log savepoint: _record_error_usage now wraps the separate-cursor create in env.cr.savepoint(), matching the canonical api_request_mixin.log_request pattern. 132/132 viin_ai_base + 95/95 viin_ai_ops/approval tests pass on real odoo-bin. Failed
merged Fix dashes Fix dashes Killed Not finished
merged [ADD] P-OPS-4 Ops Cockpit + AI Operations Manager governance (opens M3) [FIX] viin_ai_base: add base.user_admin to demo company B (multi-company invariant) The viin_ai_base DEMO creates a second res.company ('AI Ops Demo Co B') for the cross-tenant isolation demo, but a static demo <record> does NOT add the acting user to the new company the way a real res.company.create does - so it stranded base.user_admin (Mitchell Admin) outside that company. On the shared Runbot post-install DB this broke an UNRELATED Viindoo-Standard test with zero static code coupling: viin_helpdesk auto-creates one 'General' team per company; 'AI Ops Demo Co B' sorts before 'YourCompany' (res.company _order='sequence,name', both seq=0) so its team is created first and gets the lowest id; viin_helpdesk_project's TestAccessTicket.test_user_who_following_task_or_project_read_ticket does team.search([], limit=1) AS base.user_admin and the GLOBAL helpdesk_team_company_rule (no groups -> applies to admin) denies read of a team in a company admin is not a member of -> AccessError at helpdesk_ticket.py:336 (team.stage_ids). Fix: add base.user_admin to company_ai_ops_demo_b.company_ids, restoring the invariant that the admin/superuser setup account is a member of every company present in the install. The ops-manager demo user stays main-company-only, so the cross-tenant isolation demo is unaffected. Proven by confirm-by-toggle on a live DB (adding admin -> the helpdesk test runs green; removing the demo company -> green) and by an A/B install (helpdesk_project alone passes; helpdesk_project + viin_ai_base fails). Guarded by a new red-before- green regression test TestDemoCompanyMembership (RED without the fix: '1 failed'). Verified: helpdesk test 0 failed/0 error of 1; full viin_ai_base suite 0 failed/0 error of 122 tests (166 total). Succeed
merged [ADD] P-OPS-4 Ops Cockpit + AI Operations Manager governance (opens M3) [FIX] viin_ai_approval,viin_ai_ops: suppress force-sent approver mail in routing tests Runbot marks a build FAILED when the captured test log contains any ERROR record (odoo lower_logging -> result.had_error_log), even with 0 assertion failures. Three new test classes drive the real approval-routing path (action_route -> _submit_to_approval -> request.action_confirm), which message_post()s an approver notification that is force-sent synchronously. The tests create fresh res.company / users with no email and run without a mail.catchall.domain ICP, so the send has no sender address and mail_mail logs 'failed sending mail ... mail.catchall.domain' at ERROR (20 records), tripping the Runbot gate although every assertion passes. These tests assert routing / state / counts / multi-company isolation, NOT email delivery, so set mail_notify_force_send=False on the class env in setUpClass: the notification mail stays queued (never synchronously sent in the test transaction) so no spurious error is logged. Inherited by every sub-env (all build from self.env.context), so it reaches the message_post inside action_route(). Affected: TestCockpitPendingApprovalsProvider, TestMultiCompanyApprovalRouting (viin_ai_approval), TestOpsCockpitAggregation (viin_ai_ops). Verified locally (fresh DB, no catchall): 20 -> 0 'failed sending mail' ERROR records, 0 ERROR-level logs, full viin_ai_approval+viin_ai_ops suite 0 failed / 0 error of 85 tests. Failed
merged [ADD] P-OPS-4 Ops Cockpit + AI Operations Manager governance (opens M3) [FIX] viin_ai_ops: prettier-format cockpit JS for eslint web/tooling check Runbot test_pylint/test_eslint (eslint --no-eslintrc -c web/tooling/_eslintrc.json, prettier 2.8.x, trailingComma es5) flagged 8 prettier/prettier violations on the new cockpit JS: - ops_cockpit.js: collapse a single-string _t() call to one line; drop two es5 trailing commas in doAction() argument lists. - cockpit_tour.js: reformat an assignment ternary (break after '=', deepen indent). Formatting only, no logic change. Verified clean with prettier 2.8.8 (--tab-width 4 --print-width 100 --trailing-comma es5). Failed