|
|
|
merged
[ADD] viin_brain, viin_ai_brain: KG-temporal Brain UI - ADR-006 UI-layer (C-gamma scrubber)
|
[FIX] viin_brain: graph canvas off-frame/resize/clip + playhead overshoot + template icon
A deep visual-QA sweep (rect/coordinate-measured) found visual defects that
pass DOM-presence checks but render wrong:
- Graph Canvas force layout centered nodes at the 600px default coordinate
space, so in the ~298px context pane 2 of 3 nodes rendered OFF the right edge
(DOM-present, invisible). Measure the real container size BEFORE the force
layout; re-run the layout (debounced 150ms) on pane resize so nodes re-center
within the measured width (verified cx within [0,width] at 213/298/498px).
- Canvas SVG was a hard 400px in a non-scrollable pane -> clipped ~6px below the
viewport. Size the wrapper to the available pane height (flex).
- Validity-strip playhead overshot the track by its own 2px width at max scrub
(left:100% + 2px marker). Offset by translateX(-pct*0.02px) so the marker
stays flush at 100% and is unchanged mid-track.
- "Meeting Notes" template shipped icon "notebook", which is not a Font Awesome
4.7 glyph (Odoo 17 ships FA4.7) -> empty icon box. Use the valid "sticky-note-o".
|
Killed
|
|
|
|
|
|
|
|
merged
[ADD] viin_brain, viin_ai_brain: KG-temporal Brain UI - ADR-006 UI-layer (C-gamma scrubber)
|
[IMP] viin_brain: mobile/tablet responsive - frozen-backdrop fix + context bottom-sheet
Mobile/tablet defects found by a live Playwright survey (phone 390 + tablet 768):
- BLOCKER "frozen UI" on phone: a global `.o_brain_drawer_backdrop` rule in
brain_create_drawer.scss (pointer-events:all, z-index:1040) collided with the
mobile sidebar backdrop of the same class, so the always-present phone backdrop
swallowed every tap. Rename the create-drawer backdrop to a private class.
- Phone: page context menu clamped on-screen (was off the left edge); header
actions wrap; sidebar containing-block fix; sidebar auto-closes after page nav.
- Tablet: "More" overflow menu + temporal slider + context-pane toggle handle
kept inside the viewport.
- NEW: phone context bottom-sheet - the context pane (Graph / temporal scrubber /
Fact History / Properties / Backlinks) was display:none on phone; now a
bottom-sheet (FAB trigger + backdrop + drag-dismiss) makes it reachable.
All changes gated to phone/tablet media queries + isMobileViewport; desktop
(>=1024px) byte-identical.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[ADD] viin_brain, viin_ai_brain: KG-temporal Brain UI - ADR-006 UI-layer (C-gamma scrubber)
|
[FIX] viin_brain: temporal tours to tabWidth=4 (real Viindoo Runbot eslint gate)
The previous prettier pass used standalone prettier 2.8.8 (tabWidth=2 default)
and was verified against odoo/addons/test_lint/tests/eslintrc - which carries NO
prettier rule, so it reported a false CLEAN. Viindoo Runbot actually runs the
tvtmaaddons17/test_pylint fork of test_eslint, which points at
web/tooling/_eslintrc.json (extends plugin:prettier/recommended, tabWidth=4,
printWidth=100, semi). Under that real gate the 3 new tour files were 359
prettier/prettier errors (2-space vs required 4-space). Reformat them with
`eslint --fix -c web/tooling/_eslintrc.json`. No behavior change (tours stay
green). The pre-existing graph_panel tour already used 4-space, hence it passed.
|
Killed
|
|
|
|
|
|
|
|
merged
[ADD] viin_brain, viin_ai_brain: KG-temporal Brain UI - ADR-006 UI-layer (C-gamma scrubber)
|
[FIX] viin_brain: prettier 2.8.8 on temporal tours + drop unused import (Runbot lint)
Runbot test_pylint gate was red: TestESLint flagged 12 prettier/prettier errors
across the 3 new temporal tour files, and TestFlake8 flagged F401 unused
`from odoo import fields` in test_brain_snapshot_as_of.py (the clock is patched
via the string path `patch('odoo.fields.Datetime.now', ...)`, never the symbol).
Reformat the tours with the pinned prettier 2.8.8 (es5) and remove the dead
import. No logic change.
|
Failed
|
|
|
|
|
|
|
|
merged
[ADD] viin_brain, viin_ai_brain: KG-temporal Brain UI - ADR-006 UI-layer (C-gamma scrubber)
|
[IMP] viin_ai_brain: Fact-history panel + ai_bridge recordStateAsOf (ADR-006)
Bridge the ADR-006 record-state-as-of tool into the Brain UI.
- FactHistoryPanel: reconstruct an ERP record's tracked-field state at a
chosen instant, mounted in BacklinkPanel via the ai_bridge seam and gated
on ai_bridge.available so viin_brain still installs standalone (cluster
decoupling, AGENTS.md section 4 #2).
- ai_bridge_service.recordStateAsOf(model, resId, asOf) -> orm.call
tool_brain_record_state_as_of. READ path: the tool self-guards via
check_access_rights + check_access_rule as the calling user (no sudo,
no T5 attribution needed for a read/display surface).
- ACL no-leak test (a user without access gets {state:{}}, not a sudo
bypass) + fact-history tour.
|
Failed
|
|
|
|
|
|
|
|
merged
[IMP] viin_ai_brain, viin_brain, docs: Brain Standard-17 closeout (11 tools + native OT + drift reconcile)
|
[IMP] viin_brain: harden CW2 collab tests per code-review
Post-review hardening for PR #68 (review report
.odoo-ai/reviews/pr68-2026-06-22/viin_brain.md):
- test_brain_collab_activation_tour.py: make the native-collab precondition
deterministic so the tour's channel assertion actually runs instead of being
silently skipped - set the admin notification_type to 'inbox' in setUpClass
(drives session.notification_type truthy -> _hasICEServers ->
_isCollaborationEnabled). The assertion still FAILS when wiring is wrong
(collaborationChannel/busService remain required). Add tearDownClass to
restore notification_type and reset realtime_collab=False (no state leak).
- test_brain_collab_block_roundtrip.py: exercise the real ORM path
(write content_html/blocks -> re-read) instead of calling the private
_derive_from_* helpers directly, keeping the suite red-able.
- test_brain_realtime_collab.py: assertEqual(stored, 'True') (exact) instead
of a permissive membership check.
- brain_collab_activation_tour.js: keep the canonical
odoo.loader.modules.get(...) pattern (matches Odoo 17 core rte.js; require()
is not a global in tour run callbacks) - add a comment to prevent re-flagging.
Review finding F-1 (switch to require) was a false positive.
- CHANGELOG.md: link ADR-002 for traceability.
Browser tour verified by Runbot (headless tour not runnable locally).
|
Killed
|
|
|
|
|
|
|
|
merged
[IMP] viin_ai_agent, viin_ai_base: streaming trace header lifecycle + provider Test Connection
|
[TEST] viin_ai_account, viin_ai_approval_account: capture expected deny-path WARNINGs
The negative ACL tests and the C1 confused-deputy guard test exercise deny paths
that legitimately log a WARNING (core ir.actions "Forbidden server action"; the
match_payment C1 "lacks write authority ... refusing to reconcile"). Wrap each
in assertLogs so the warning is both asserted (the guard fired) and captured (no
longer leaking to the runbot log). Strengthens the tests; no assertion removed.
|
Killed
|
|
|
|
|
|
|
|
merged
[IMP] viin_ai_brain, viin_brain, docs: Brain Standard-17 closeout (11 tools + native OT + drift reconcile)
|
[IMP] viin_brain: harden CW2 collab tests per code-review
Post-review hardening for PR #68 (review report
.odoo-ai/reviews/pr68-2026-06-22/viin_brain.md):
- test_brain_collab_activation_tour.py: make the native-collab precondition
deterministic so the tour's channel assertion actually runs instead of being
silently skipped - set the admin notification_type to 'inbox' in setUpClass
(drives session.notification_type truthy -> _hasICEServers ->
_isCollaborationEnabled). The assertion still FAILS when wiring is wrong
(collaborationChannel/busService remain required). Add tearDownClass to
restore notification_type and reset realtime_collab=False (no state leak).
- test_brain_collab_block_roundtrip.py: exercise the real ORM path
(write content_html/blocks -> re-read) instead of calling the private
_derive_from_* helpers directly, keeping the suite red-able.
- test_brain_realtime_collab.py: assertEqual(stored, 'True') (exact) instead
of a permissive membership check.
- brain_collab_activation_tour.js: keep the canonical
odoo.loader.modules.get(...) pattern (matches Odoo 17 core rte.js; require()
is not a global in tour run callbacks) - add a comment to prevent re-flagging.
Review finding F-1 (switch to require) was a false positive.
- CHANGELOG.md: link ADR-002 for traceability.
Browser tour verified by Runbot (headless tour not runnable locally).
|
Killed
|
|
|
|
|
|
|
|
merged
[IMP] viin_ai_brain, viin_brain, docs: Brain Standard-17 closeout (11 tools + native OT + drift reconcile)
|
[FIX] viin_ai_brain, viin_brain: prettier/prettier in QUnit tests (test_eslint gate)
test_pylint/tests/test_eslint::TestESLint.test_eslint failed with 84
prettier/prettier errors across 3 JS test files. The gate runs eslint with
web/tooling/_eslintrc.json, which sets no trailingComma and therefore follows
the bundled prettier 2.8.8 default (es5): no trailing comma after the last
function-call argument, and short multiline calls collapsed.
Fix applied via `eslint --no-eslintrc -c web/tooling/_eslintrc.json --fix`
resolved against prettier 2.8.8 (NOT prettier 3.x, whose trailingComma=all
default produces the opposite and would keep the gate red), bound strictly to:
- viin_ai_brain/static/tests/components/ai_block_tests.js (47)
- viin_ai_brain/static/tests/registries/powerbox_commands_tests.js (32)
- viin_brain/static/tests/tours/brain_collab_activation_tour.js (5)
Formatting only (remove trailing commas, join short multiline calls); no test
logic, assertion, or message string changed. Post-fix: gate exit 0; full
118-file eslint sweep of both modules exit 0.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[IMP] viin_ai_brain, viin_brain, docs: Brain Standard-17 closeout (11 tools + native OT + drift reconcile)
|
[IMP] viin_ai_brain, viin_brain, docs: Brain Standard-17 closeout (11 tools + native OT + drift reconcile)
PR #68 - Brain cluster closeout on Viindoo Standard 17, grounded in a code-level
deep-survey of the cluster.
CW1 (viin_ai_brain): wire the 2 Wave-2 KG-temporal tools (brain_temporal_diff,
brain_record_state_as_of) to the Knowledge Base Assistant topic so the agent reaches
all 11 tools; version 0.1.0 -> 0.1.1 (noupdate=0 re-seed on -u); membership-asserting
RED-GREEN test; CHANGELOG.
CW2 (viin_brain): native OdooEditor WebRTC OT collaboration (ADR-002 Option 1).
_buildWysiwygOptions passes collaborationChannel {model,field,resId} + collaborativeTrigger
to the standalone Wysiwyg, gated on the realtime_collab flag + canEdit; OdooEditor's own
WebRTC P2P + ICE (mail.ice.server) handles OT - zero OT logic reimplemented. The interim
snapshot-relay (collab.py, collab_step_service.js, viin.brain.collab.step) is retired.
Flag-OFF path (autosave + version-token + conflict dialog) unchanged. block_parser
roundtrip test for native-serialized HTML; activation tour + flag tests adapted. Live
2-peer WebRTC convergence is a manual staging check (not headless-CI-testable).
CW3 (viin_ai_brain): QUnit for the AIBlock 5-state machine + powerbox Brain-context gate;
new web.qunit_suite_tests bundle.
Docs (reconcile SSOT to shipped code): adr-003 Accepted (P16 shipped); adr-002 closeout
(native OT shipped, relay retired); security.md (WI-005 declined / WI-006 closed);
roadmap Track B (P16 date, W18-22 panels shipped, OT-final native DONE); "9 tool" -> "11
tool"; adr-001 ADR-012 supersede note; new docs/brain/install-tiers.md (Standard vs
Enterprise).
|
Failed
|
|
|
|
|
|
|
|
merged
[IMP] viin_ai_agent, viin_ai_base: streaming trace header lifecycle + provider Test Connection
|
[IMP] viin_ai_agent, viin_ai_base: streaming trace header lifecycle + provider Test Connection
WI-A (viin_ai_agent): run_streaming() now materializes + closes the viin.ai.trace
header symmetric to run(); run() also closes its header (end_time/status/final_response);
_ensure_trace_header stamps agent_id. Closes the ADR-019 deferred follow-up - streamed
sessions are now observable in the P-OPS-4 cockpit, retention, and proposal.trace_id.
Header create-to-loop span is guarded so an error still closes the header (AGENTS.md #7).
WI-B (viin_ai_base): provider "Test Connection" button (WI-008 WI-F) - a BYOK
reachability/auth probe routed through call_completion (satisfies AGENTS.md #7/#11/#12),
returns {status, latency_ms, model_list} via the built-in display_notification. Vendor
error text is redacted (_redact_secrets) so a BYOK api_key can never leak to the surface.
Docs: roadmap, AGENTS status, observability, wi-008 updated to reflect both changes.
Tests: 333 Odoo-native tests (viin_ai_agent 181 + viin_ai_base 318), 0 failed / 0 error.
|
Failed
|
|
|
|
|
|
|
|
merged
[IMP] viin_ai_brain, viin_brain, docs: Brain Standard-17 closeout (11 tools + native OT + drift reconcile)
|
[IMP] viin_ai_brain, docs: Brain Standard-17 closeout - wire 11 tools to KBA + doc-drift reconcile
CW1: wire the 2 Wave-2 KG-temporal tools (brain_temporal_diff, brain_record_state_as_of)
to the Knowledge Base Assistant topic so the agent can reach all 11 tools; bump
viin_ai_brain 0.1.0 -> 0.1.1 (noupdate=0 re-seed on -u); add membership-asserting
RED-GREEN test (135 viin_ai_brain tests pass, 0 failed); add viin_ai_brain CHANGELOG.
Docs (current-state reconciliation to shipped code):
- adr-003 Status active -> Accepted (P16 shipped 2026-06-07) + checklist
- adr-002 correct false 'collaborative=True already wired' claim (post-P16)
- security.md: admin audit-read DECLINED (WI-005); 7 daily-note cells closed hard-DENY (WI-006)
- roadmap Track B: P16 date, W18-22 panels shipped, P14/P15 header current
- '9 tool' -> '11 tool' across module + docs + AGENTS
- adr-001 inline ADR-012 supersede note on bridge removal clause
- add docs/brain/install-tiers.md (Standard-vs-Enterprise install matrix)
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[ADD] Phase 4 Buoc 2: viin_ai accounting write-tools (create_invoice_draft / match_payment / tax_lookup)
|
[DOC] AGENTS, roadmap, decisions, observability: reflect Phase 4 Buoc 2 account write-tools (ADR-019, PR #67)
|
Killed
|
|
|
|
|
|
|
|
merged
[ADD] Phase 4 Buoc 2: viin_ai accounting write-tools (create_invoice_draft / match_payment / tax_lookup)
|
[DOC] AGENTS, roadmap, decisions, observability: reflect Phase 4 Buoc 2 account write-tools (ADR-019, PR #67)
|
Failed
|
|
|
|
|
|
|
|
merged
[ADD] Phase 4 Buoc 2: viin_ai accounting write-tools (create_invoice_draft / match_payment / tax_lookup)
|
[ADD] l10n_vn_viin_ai_account: VN accounting AI guidance (TT99/TT133 additive topic)
|
Killed
|
|
Not finished
|
|
|
|
|
|
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).
|
Killed
|
|
|
|
|
|
|
|
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
|
Killed
|
|
|
|
|
|
|
|
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
|
Killed
|
|
|
|
|
|
|
|
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
|
Killed
|
|
|
|
|
|
|
|
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
|
Killed
|
|
|
|
|
|
|
|
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
|
Killed
|
|
|
|
|
|
|
|
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.
|
Killed
|
|
|
|
|
|
|
|
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
|
|
|
|
|