|
|
|
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).
|
Killed
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
merged
[ADD] P-OPS-4 Ops Cockpit + AI Operations Manager governance (opens M3)
|
[FIX] viin_ai_ops_brain: sudo Brain reads in approval-rationale evidence write
The 'Generate Approval Rationale' button is callable by any user with write on
the proposal (no group on the view) and writes an internal audit page under sudo.
But the Brain-config reads on that path ran as the calling user: the rationale
template content_html, the evidence vault auto_approve flag, and the originating
viin.ai.trace business id. A non-Brain (or non-author) user hit AccessError on
the vault/template, and the T5 trace attribution silently degraded to the
no-trace sentinel under the trace own-records rule. Read all three under sudo
(consistent with the already-sudo'd page + link create) so evidence is written
with full-fidelity attribution regardless of who triggers it. Adds a red-before-
green test: a non-author non-manager trigger records the REAL trace_id.
|
Failed
|
|
|
|
|
|
|
|
merged
[ADD] P-OPS-4 Ops Cockpit + AI Operations Manager governance (opens M3)
|
[ADD] viin_ai_ops: P-OPS-4 Ops Cockpit dashboard + ops-manager governance gate
OWL client action 'Ops Cockpit' under AI > Reporting: a COO/CFO governance
pane-of-glass with 6 ACL-respecting metric tiles (pending_approvals,
stuck_workflow, fail_rate, cost_per_goal, stale_evidence, ai_action_backlog) via
retrieve_ops_cockpit() running as the user (read_group/search_count, no sudo, no
raw SQL). Cross-module metrics flow through a decoupling provider hook so
viin_ai_ops gains zero dependency edges. Company-wide view gated on
group_ai_ops_manager (not base.group_system); tiles drill into the same records
within the user's scope. Semantic v17 design tokens (no hardcoded color), AA
contrast, responsive.
|
Failed
|
|
|
|
|
|
|
|
merged
[ADD] viin_ai_workflow: P-OPS-3 Operating Layer workflow adapter + heartbeat routines
|
[FIX] viin_ai_workflow,viin_ai_workflow_automation: capture expected error-path logs in tests
Runbot marks a build FAILED when the captured test log contains any
WARNING/ERROR record (odoo lower_logging -> result.had_error_log), even with
0 assertion failures. Two tests exercise deliberate error-paths whose
operator-facing logs leaked into the captured log:
- test_cron_savepoint_isolates_a_failing_routine (WF-8): the poisoned routine
raises, _cron_run_routines logs its traceback at ERROR (fault-isolation
signal).
- test_execute_approved_no_published_version_stays_approved: the unpublished
workflow logs two WARNINGs (graceful no-fake-execute signal).
Wrap each triggering call in self.assertLogs() so the expected records are
captured (propagate=False -> no leak to Runbot) AND asserted to have fired -
stronger than mute_logger: the test now proves the except-branch / degradation
path actually ran. Production logging is unchanged (the logs are correct and
valuable in operation; only the tests own their expected output now).
Verified locally (no API key): 0 failed / 18 tests, 0 leaked WARNING/ERROR.
|
Killed
|
|
|
|
|
|
|
|
merged
[ADD] viin_ai_workflow: P-OPS-3 Operating Layer workflow adapter + heartbeat routines
|
[DOC] docs,decisions: P-OPS-3 shipped + ADR-017 two-module split
- roadmap.md: P-OPS-3 marked done (PR #49), module inventory 38 -> 39
(base viin_ai_workflow + bridge viin_ai_workflow_automation), M3 status
(P-OPS-1/2/3 done; remaining P-OPS-4 cockpit + 3 heartbeat), ADR table.
- operating-model.md 4.5: split tiering (Community base + EE bridge) refines
the 4.3 single-module sketch.
- ADR-017 (Accepted): two-module split rationale + super() parity invariant +
no-exact-pin + Reports menu root owned by viin_ai_base. Runtime-verified
Community 11 + Enterprise 18 tests, no API key.
|
Failed
|
|
|
|
|
|
|
|
merged
[ADD] viin_ai_workflow: P-OPS-3 Operating Layer workflow adapter + heartbeat routines
|
[FIX] viin_ai_workflow,viin_ai_workflow_automation: runtime install + test fixes
Found by a full Community + Enterprise install/test run (static review missed these):
- work_routine_data.xml: drop name_vi_VN from viin.ai.agent / viin.ai.goal
records - those models have no name_vi_VN field (ValueError at load).
Routine names (viin.ai.work.routine.name is translate=True) are translated
via i18n/vi.po instead; agent/goal names stay English per cluster precedent.
- work_routine.py: the scan-domain safe_eval was passed the raw 'datetime'
module, which safe_eval forbids. Use the wrapped datetime from
odoo.tools.safe_eval (the core ir_filters domain-eval idiom).
- viin_ai_workflow_automation form views: add <field workflow_instance_id
invisible='1'/> so the stat-button 'invisible' modifier can resolve it
(Odoo 17 requires modifier fields present in the view).
- test_work_routine WF-3: the base-no-override check used a prefix that also
matched the bridge package (viin_ai_workflow vs viin_ai_workflow_automation);
anchor it with a trailing dot so the bridge's legitimate override is allowed.
Verified: Community 0 failed/11 tests; Enterprise 0 failed/18 tests (no API key).
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[ADD] viin_ai_workflow: P-OPS-3 Operating Layer workflow adapter + heartbeat routines
|
[ADD] viin_ai_workflow,viin_ai_workflow_automation: P-OPS-3 workflow adapter
Operating Layer P-OPS-3 - wires decide -> orchestrate.
viin_ai_workflow (Community base):
- viin.ai.work.routine model + heartbeat cron (ar_overdue_review_daily,
stock_anomaly_daily, cashflow_summary_weekly): scan -> work_item ->
agent analysis -> Brain evidence hook -> optional governed proposal.
- Adds routine_id on viin.ai.work.item. Multi-company (with_company,
record rule); cron uses savepoint-per-routine, no manual commit.
- Work Routines config menu + Routine Activity report; demo data with
time-relative dates; vi_VN translations.
viin_ai_workflow_automation (Enterprise auto_install bridge):
- Adds workflow_instance_id (work_item/proposal) + control_policy.workflow_id.
- Overrides viin.ai.action.proposal._execute_approved: route to a
workflow.instance when a policy targets one, else super() to the
P-OPS-2 single-step path (Community parity preserved).
- workflow_id constrained to workflows targeting viin.ai.action.proposal.
- depends viin_workflow_automation without an exact version pin.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[ADD] viin_ai_workflow: P-OPS-3 Operating Layer workflow adapter + heartbeat routines
|
[ADD] viin_ai_workflow,viin_ai_workflow_automation: P-OPS-3 workflow adapter
Operating Layer P-OPS-3 - wires decide -> orchestrate.
viin_ai_workflow (Community base):
- viin.ai.work.routine model + heartbeat cron (ar_overdue_review_daily,
stock_anomaly_daily, cashflow_summary_weekly): scan -> work_item ->
agent analysis -> Brain evidence hook -> optional governed proposal.
- Adds routine_id on viin.ai.work.item. Multi-company (with_company,
record rule); cron uses savepoint-per-routine, no manual commit.
- Work Routines config menu + Routine Activity report; demo data with
time-relative dates; vi_VN translations.
viin_ai_workflow_automation (Enterprise auto_install bridge):
- Adds workflow_instance_id (work_item/proposal) + control_policy.workflow_id.
- Overrides viin.ai.action.proposal._execute_approved: route to a
workflow.instance when a policy targets one, else super() to the
P-OPS-2 single-step path (Community parity preserved).
- workflow_id constrained to workflows targeting viin.ai.action.proposal.
- depends viin_workflow_automation without an exact version pin.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[ADD] P-OPS-2 approval governance + no-key AI test framework
|
[FIX] viin_ai_approval: mute expected ai_prompt warning in adapter test
test_executes_when_action_runs creates an ir.actions.server with state='ai_prompt' but no ai_model_id/ai_prompt/ai_target_field_id (only to pass the executable-action filter); the spy calls through to run(), which logs the expected missing-fields warning. Mute that logger for the test, as test_server_action_ai_prompt already does.
|
Killed
|
|
|
|
|
|
|
|
merged
[ADD] P-OPS-2 approval governance + no-key AI test framework
|
[FIX] viin_ai_approval_sale: pin QUOTE type to order company in e2e test
test_end_to_end_confirm_cron_advisory_posted raised UserError 'Incompatible
companies' on a multi-company DB: _make_quote_request searched the QUOTE
approval type with limit=1 unfiltered, so it could pick another company's
seeded type and fail _check_company at request create. Filter by the order's
company (allowing company-less) for FIRST-deterministic behavior regardless of
how many companies exist. Verified: 0 failed, 0 error(s) on a full demo DB.
|
Failed
|
|
|
|
|
|
|
|
merged
[ADD] P-OPS-2 approval governance + no-key AI test framework
|
[FIX] viin_ai_approval: P-OPS-2 review fixes (XSS/PII/multi-company/hasattr)
Post-review fixes for the P-OPS-2 approval governance + no-key test PR.
CRITICAL
- viin_ai_approval: stored XSS in _format_advisory_html - LLM content was
interpolated into HTML then wrapped in Markup() at the end (no escaping).
Rebuilt all 3 paths with Markup(template) %% arg so every LLM-sourced value
(summary/recommendation/signal label+value, fallback raw) is auto-escaped.
HIGH
- viin_ai_approval: advisory cron processed requests cross-company; sudo()
bypasses the company ir.rule. Run each request via with_company(company_id)
so its own agent/provider/BYOK key resolves.
- viin_ai_approval: ir_cron_advisory.xml missing noupdate=1 - upgrades reset
the cron and silently re-enable LLM calls. Added noupdate=1.
- viin_ai_approval_sale: payment_term_id.name (free-text PII) was sent to the
LLM. Replaced with a PII-free payment_term_category derived from term lines.
- viin_ai_ops_brain: evidence page landed ai_review_status='n_a' instead of
honouring vault.auto_approve_ai_content; and page/link create lacked sudo()
(AccessError for non-Brain-editor users). Set status explicitly + sudo().
hasattr existence-probe cleanup (ref Viindoo/odoo-mcp-client#63)
- viin_ai_approval_sale: commercial_partner_id does not exist on sale.order -
hasattr was always False, so credit-headroom always used the wrong partner.
Fixed to partner_id.commercial_partner_id (real hidden bug).
- account credit fields are guaranteed in the closure -> direct access.
- margin/margin_percent (sale_margin soft dep, not in closure) -> use
'f' in source._fields. Base generic probes -> 'f' in source._fields too.
MED/LOW
- Dedup _has_executable_code/_has_real_code into viin_ai_approval/hooks.py.
- _stub_provider patches the registry class (was import-path) like _stub_llm.
- account: date.today()->fields.Date.today(); fix invalid payment_state key in
topic prompt. ops_brain: goal fallback model + inheriting view id/name.
Tests
- Added stored-XSS regression tests, real PII-absent assertions (account+sale),
ai_review_status-follows-vault-policy test.
- Replaced duck-typed-fake sale signal tests with real-record ORM tests + two
helpers (FIRST-deterministic skipTest when sale_margin/account absent).
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[ADD] viin_ai_approval(+account,+sale,+ops_brain): P-OPS-2 approval governance + AI advisory on human approvals
|
[ADD] viin_ai_approval,viin_ai_ops_brain,test_viin_ai: P-OPS-2 approval governance + no-key AI test framework
Phase 3.8 P-OPS-2 on top of viin_ai_ops, wired to the existing OmniApproval engine
(viin_approval, tvtmaaddons17), workflow-independent (behaviour + tests identical with
or without viin_workflow_automation), plus a cross-cutting no-key AI/LLM test framework.
Status docs (roadmap + AGENTS) reconciled. SSOT: docs/ai/operating-model.md, ADR-015/016.
== P-OPS-2 approval governance ==
Concern A - "Approval OF AI" (viin_ai_approval + viin_ai_ops_brain): a side-effecting
viin.ai.action.proposal (medium/high risk, or a runs_as_sudo tool) is routed to a human
viin.approval.request; on the human decision the proposal state syncs (approved/rejected/
draft) and the approved action is handed to execution. State sync is DATA-DRIVEN via the
approval type's code_* hooks (code_validate_post / code_refuse_post / code_cancel_post)
calling the proposal's public callbacks - NOT action_* method overrides (the idiomatic
OmniApproval way, which is why post-approval execution is environment-independent). Captures
trace_id / prompt_snapshot / iteration_index into approval evidence. viin_ai_ops_brain writes
an attributed "Approval Rationale" Brain page (T5: is_ai_authored + agent + trace_id +
confidence; html_escape on untrusted text).
Concern B - "AI IN approval" (viin_ai_approval infra + per-app pilots): posts an AI risk-
advisory card (risk score + summary + recommendation + signals) to a human approval request's
chatter, for the approver - ADVISORY-ONLY (no auto-approve). Data-driven trigger:
code_confirm_post enqueues (none -> pending) and wakes a cron; the cron runs the LLM
out-of-band, using cr.savepoint() per record + the ir.cron row-lock for non-concurrency
(no manual cr.commit). Graceful 'skipped' when no BYOK key is configured; silent 'error' on
failure. Per-app pilots (ADR-015 convention: auto_install bridge, data-XML agent/topic seed,
non-clobbering post_init_hook, PII-minimised signal builder): viin_ai_approval_account
(CUSTOMER_INVOICE / VENDOR_PAYMENTS / CUSTOMER_REFUNDS) + viin_ai_approval_sale (QUOTE).
== No-key AI/LLM test framework ==
L1 - shared unit-test helpers in viin_ai_base/tests/ (not on the runtime path): canonical
FakeResponse, result builders (make_llm_call_result / make_completion_result /
make_stream_chunks / make_fake_embed_fn), and AIStubMixin (_stub_llm / _stub_stream /
_stub_provider patching the registry-class agent seam) + seed_pgvector_rows. 10 legacy test
files converge onto these; 5 disconnected stub patterns (3 _FakeResponse copies, 2 identical
fake_embed, 3 raw-SQL pgvector blocks) are deduplicated. The provider-inheritance test is
hardened to assert the resolved MRO instead of the _inherit attribute.
L2 - test_viin_ai: a separate TEST-ONLY module (Odoo test_* convention) the customer NEVER
installs (installable, no auto_install, in no product dependency closure; dev/demo/CI only ->
customer prod carries zero fabrication code). Two roles: a gated mock LLM provider (overrides
one seam, make_request, for api_protocol='mock'; every protocol-keyed helper falls through to
the openai_compat shape so cost-cap / PII redaction / normalize / usage.log / trace all run
real; a fail-closed gate in models/gate.py - @api.constrains on the vendor + a refusal in
make_request - so no fabrication is reachable when the gate is closed, preserving the BYOK
invariant) AND the home for test cases that cannot live in a production module (cross-module /
full-stack / mock-dependent).
== Docs ==
ADR-015 (Concern B per-app advisory pattern), ADR-016 (shared AI test framework + gated
test_viin_ai placement + L3 rejection), docs/ai/testing.md (no-key testing SSOT: decision
tree, helper API, coverage fence, @tagged rule, Runbot lint/test commands). Pointers from
AGENTS.md §6, docs/ai/README.md, docs/decisions/README.md. roadmap.md + AGENTS.md §3
reconciled: P-OPS-2 shipped (flagging that the original spec assumed action_* overrides while
the shipped path is data-driven code_* hooks); gate M3 still needs P-OPS-3 (viin_ai_workflow)
+ P-OPS-4 (ops cockpit) + heartbeat routines.
== Verification ==
No-key: Community 376/376 + EE 304/304 (with viin_workflow_automation present), 0 failed /
0 error. Code-quality (test_lint + test_pylint, exact Runbot config): flake8 + pylint_odoo
clean, no lint suppressions; manifest/RST clean. Live demo: a real sale-quotation approval
confirm -> cron -> agent.run -> mock provider yields a genuine advisory card (no network,
no key, no canned message_post).
|
Failed
|
|
|
|
|
|
|
|
merged
[ADD] viin_ai_approval(+account,+sale,+ops_brain): P-OPS-2 approval governance + AI advisory on human approvals
|
[ADD] viin_ai_*: no-key AI/LLM test framework (helpers + test_viin_ai module + docs)
Give every viin_ai_* module a way to test LLM-dependent logic deterministically
without an API key and without network, and unlock a live no-key demo - without
weakening the BYOK invariant (no fabrication code reachable in a customer prod DB).
A code-grounded audit found 5 disconnected stub patterns across 15 test files
(3 _FakeResponse copies, 2 byte-identical fake_embed, 3 raw-SQL pgvector blocks,
no canonical response shape). See docs/ai/testing.md + ADR-016 for the rationale.
L1 - shared unit-test helpers in viin_ai_base/tests/ (not on the runtime path):
- fake_response.py: one canonical FakeResponse.
- builders.py: make_completion_result / make_llm_call_result / make_stream_chunks /
make_fake_embed_fn - single-sourced result shapes (Appendix A).
- common.py: AIStubMixin with _stub_llm / _stub_stream / _stub_provider context
managers patching the registry-class agent seam (_do_llm_call / _do_llm_stream),
plus seed_pgvector_rows. 10 legacy test files converge onto these; the duplicated
_FakeResponse / fake_embed / inline result dicts and raw-SQL blocks are deleted.
L2 - test_viin_ai: the cluster's TEST-ONLY module (Odoo test_* convention, cf.
test_pylint) the customer NEVER installs (installable but no auto_install, in no
product dependency closure, nothing depends on it; dev/demo/CI only -> customer prod
carries zero fabrication code). Two roles: (1) a gated mock LLM provider; (2) the home
for test cases that cannot live in a production module (cross-module / full-stack /
mock-dependent). It overrides one seam (viin.ai.provider.make_request) for
api_protocol='mock'; every protocol-keyed helper already falls through to the
openai_compat shape, so cost-cap, PII redaction, normalize, usage.log and trace all run
real - only the network call is faked (a local _MockResponse; models/ never imports the
tests-only FakeResponse). Fail-closed gate (SSOT in models/gate.py): an @api.constrains
on the vendor (a 'mock' vendor cannot persist outside test/demo) AND a refusal in
make_request (never fabricates when the gate is closed). Gate open iff --test-enable OR
registry test-mode OR ir.config_parameter test_viin_ai.enabled='1' (demo data sets it).
11 tests: 6 fail-closed gate proofs + 5 no-key full-stack proofs (real agent.run /
call_embedding -> mock -> usage.log written).
Also harden test_provider_inherits_mixin_and_thread: it asserted on
viin.ai.provider._inherit (which reports only the last-loaded module's own
declaration, so test_viin_ai legitimately flipped it) - rewritten to assert the
resolved MRO + the capabilities the bases contribute; still fails if a base is
genuinely removed.
Docs: docs/ai/testing.md (SSOT decision tree + helper API + coverage fence + @tagged
rule + Runbot lint/test commands with the W503/W504 trap called out), ADR-016
(decision + gated-placement rationale + L3 rejection), pointers from AGENTS.md §6,
docs/ai/README.md, docs/decisions/README.md.
Verified no-key: Community 376/376 + EE 304/304 (with viin_workflow_automation present),
0 failed 0 error; flake8 + pylint_odoo (exact Runbot config) clean, 10.00/10, no
lint suppressions. Live demo: a real sale-quotation approval confirm -> cron ->
agent.run -> mock provider yields a genuine advisory card (no network, no key, no
canned message_post).
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[ADD] viin_ai_approval(+account,+sale,+ops_brain): P-OPS-2 approval governance + AI advisory on human approvals
|
[ADD] viin_ai_*: no-key AI/LLM test framework (helpers + test_viin_ai module + docs)
Give every viin_ai_* module a way to test LLM-dependent logic deterministically
without an API key and without network, and unlock a live no-key demo - without
weakening the BYOK invariant (no fabrication code reachable in a customer prod DB).
A code-grounded audit found 5 disconnected stub patterns across 15 test files
(3 _FakeResponse copies, 2 byte-identical fake_embed, 3 raw-SQL pgvector blocks,
no canonical response shape). See docs/ai/testing.md + ADR-016 for the rationale.
L1 - shared unit-test helpers in viin_ai_base/tests/ (not on the runtime path):
- fake_response.py: one canonical FakeResponse.
- builders.py: make_completion_result / make_llm_call_result / make_stream_chunks /
make_fake_embed_fn - single-sourced result shapes (Appendix A).
- common.py: AIStubMixin with _stub_llm / _stub_stream / _stub_provider context
managers patching the registry-class agent seam (_do_llm_call / _do_llm_stream),
plus seed_pgvector_rows. 10 legacy test files converge onto these; the duplicated
_FakeResponse / fake_embed / inline result dicts and raw-SQL blocks are deleted.
L2 - test_viin_ai: the cluster's TEST-ONLY module (Odoo test_* convention, cf.
test_pylint) the customer NEVER installs (installable but no auto_install, in no
product dependency closure, nothing depends on it; dev/demo/CI only -> customer prod
carries zero fabrication code). Two roles: (1) a gated mock LLM provider; (2) the home
for test cases that cannot live in a production module (cross-module / full-stack /
mock-dependent). It overrides one seam (viin.ai.provider.make_request) for
api_protocol='mock'; every protocol-keyed helper already falls through to the
openai_compat shape, so cost-cap, PII redaction, normalize, usage.log and trace all run
real - only the network call is faked (a local _MockResponse; models/ never imports the
tests-only FakeResponse). Fail-closed gate (SSOT in models/gate.py): an @api.constrains
on the vendor (a 'mock' vendor cannot persist outside test/demo) AND a refusal in
make_request (never fabricates when the gate is closed). Gate open iff --test-enable OR
registry test-mode OR ir.config_parameter test_viin_ai.enabled='1' (demo data sets it).
11 tests: 6 fail-closed gate proofs + 5 no-key full-stack proofs (real agent.run /
call_embedding -> mock -> usage.log written).
Also harden test_provider_inherits_mixin_and_thread: it asserted on
viin.ai.provider._inherit (which reports only the last-loaded module's own
declaration, so test_viin_ai legitimately flipped it) - rewritten to assert the
resolved MRO + the capabilities the bases contribute; still fails if a base is
genuinely removed.
Docs: docs/ai/testing.md (SSOT decision tree + helper API + coverage fence + @tagged
rule + Runbot lint/test commands with the W503/W504 trap called out), ADR-016
(decision + gated-placement rationale + L3 rejection), pointers from AGENTS.md §6,
docs/ai/README.md, docs/decisions/README.md.
Verified no-key: Community 376/376 + EE 304/304 (with viin_workflow_automation present),
0 failed 0 error; flake8 + pylint_odoo (exact Runbot config) clean, 10.00/10, no
lint suppressions. Live demo: a real sale-quotation approval confirm -> cron ->
agent.run -> mock provider yields a genuine advisory card (no network, no key, no
canned message_post).
|
Failed
|
|
|
|
|
|
|
|
merged
[ADD] viin_ai_approval(+account,+sale,+ops_brain): P-OPS-2 approval governance + AI advisory on human approvals
|
[ADD] viin_ai_*: no-key AI/LLM test framework (helpers + gated viin_ai_mock + docs)
Give every viin_ai_* module a way to test LLM-dependent logic deterministically
without an API key and without network, and unlock a live no-key demo - without
weakening the BYOK invariant (no fabrication code reachable in a customer prod DB).
A code-grounded audit found 5 disconnected stub patterns across 15 test files
(3 _FakeResponse copies, 2 byte-identical fake_embed, 3 raw-SQL pgvector blocks,
no canonical response shape). See docs/ai/testing.md + ADR-016 for the rationale.
L1 - shared unit-test helpers in viin_ai_base/tests/ (not on the runtime path):
- fake_response.py: one canonical FakeResponse.
- builders.py: make_completion_result / make_llm_call_result / make_stream_chunks /
make_fake_embed_fn - single-sourced result shapes (Appendix A).
- common.py: AIStubMixin with _stub_llm / _stub_stream / _stub_provider context
managers patching the registry-class agent seam (_do_llm_call / _do_llm_stream),
plus seed_pgvector_rows. 10 legacy test files converge onto these; the duplicated
_FakeResponse / fake_embed / inline result dicts and raw-SQL blocks are deleted.
L2 - viin_ai_mock: a SEPARATE gated module the customer NEVER installs (auto_install
False, in no product dependency closure; installed only on dev/demo/CI). Overrides one
seam (viin.ai.provider.make_request) for api_protocol='mock'; every protocol-keyed
helper already falls through to the openai_compat shape, so cost-cap, PII redaction,
normalize, usage.log and trace all run real - only the network call is faked (a local
_MockResponse; models/ never imports the tests-only FakeResponse). Fail-closed gate
(SSOT in models/gate.py): an @api.constrains on the vendor (a 'mock' vendor cannot
persist outside test/demo) AND a refusal in make_request (never fabricates when the
gate is closed). Gate open iff --test-enable OR registry test-mode OR
ir.config_parameter viin_ai_mock.enabled='1' (demo data sets it). 11 tests: 6
fail-closed gate proofs + 5 no-key full-stack proofs (real agent.run / call_embedding
-> mock -> usage.log written).
Also harden test_provider_inherits_mixin_and_thread: it asserted on
viin.ai.provider._inherit (which reports only the last-loaded module's own
declaration, so viin_ai_mock legitimately flipped it) - rewritten to assert the
resolved MRO + the capabilities the bases contribute; still fails if a base is
genuinely removed.
Docs: docs/ai/testing.md (SSOT decision tree + helper API + coverage fence + @tagged
rule + Runbot lint/test commands with the W503/W504 trap called out), ADR-016
(decision + gated-placement rationale + L3 rejection), pointers from AGENTS.md §6,
docs/ai/README.md, docs/decisions/README.md.
Verified no-key: Community 376/376 + EE 304/304 (with viin_workflow_automation present),
0 failed 0 error; flake8 + pylint_odoo (exact Runbot config) clean, 10.00/10, no
lint suppressions. Live demo: a real sale-quotation approval confirm -> cron ->
agent.run -> mock provider yields a genuine advisory card (no network, no key, no
canned message_post).
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[ADD] viin_ai_approval(+account,+sale,+ops_brain): P-OPS-2 approval governance + AI advisory on human approvals
|
[ADD] viin_ai_approval,viin_ai_approval_account,viin_ai_approval_sale,viin_ai_ops_brain: P-OPS-2 approval governance + AI advisory on human approvals
Phase 3.8 P-OPS-2. Two cleanly separated concerns on top of viin_ai_ops, wired to
the existing OmniApproval (viin_approval) engine. Behaviour + tests do NOT depend
on viin_workflow_automation (identical with or without it installed).
Concern A - "Approval OF AI" (viin_ai_approval, viin_ai_ops_brain):
- Routes a side-effecting viin.ai.action.proposal (medium/high risk, or a
runs_as_sudo tool) to a human viin.approval.request; on the human decision the
proposal state syncs (approved/rejected/draft) and the approved action is handed
to execution. State sync is data-driven via the approval type's code_* hooks
(code_validate_post / code_refuse_post / code_cancel_post) calling the proposal's
public callbacks - NOT via action_* overrides (the viin_approval idiom).
- Post-approval execution is environment-independent (no workflow-engine gate;
P-OPS-3 viin_ai_workflow overrides execution later).
- viin_ai_ops_brain writes an attributed "Approval Rationale" Brain page (T5:
is_ai_authored + agent + trace + confidence; html_escape on untrusted text).
Concern B - "AI IN approval" (viin_ai_approval infra + per-app pilots):
- Posts an AI risk-advisory card (risk score + summary + recommendation + signals)
to the chatter of a human approval request, for the approver. Advisory only (no
auto-approve). Data-driven trigger: code_confirm_post enqueues (none->pending)
and wakes a cron; the cron runs the LLM out-of-band and posts the card.
- The cron uses cr.savepoint() per record for failure isolation and relies on the
ir.cron row-lock for non-concurrency - NO manual cr.commit(). Graceful 'skipped'
when the BYOK key is not configured; silent 'error' on failure (no chatter noise).
- Per-app pilots (ADR-015 convention; auto_install; data-XML agent/topic seed + a
non-clobbering post_init_hook wiring the approval type by stable code;
PII-minimised signal builder): viin_ai_approval_account (CUSTOMER_INVOICE /
VENDOR_PAYMENTS / CUSTOMER_REFUNDS) and viin_ai_approval_sale (QUOTE).
- UX: band-coloured chatter card (AA-contrast, "AI advisory - not a decision"
disclaimer), a form status badge, and a list AI-risk column + "High risk" filter.
Cluster decoupling kept: only viin_ai_approval depends viin_approval; the per-app
modules reach it through the bridge. ADR-015 records the per-app advisory
convention (decisions/README updated).
Tests: Odoo native TransactionCase/@tagged, behaviour-protecting (no live LLM - the
provider boundary is stubbed at agent.run / _do_llm_call). 36 tests, 0 failed and
0 error in BOTH a Community addons set AND a full Viindoo-EE set (with
viin_workflow_automation). flake8 + the full test_pylint enable-list both pass with
NO lint suppressions.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[ADD] viin_ai_approval(+account,+sale,+ops_brain): P-OPS-2 approval governance + AI advisory on human approvals
|
[REF] viin_ai_approval: advisory cron uses savepoint isolation, not manual commit
Replaces the dangerous manual self.env.cr.commit() in the advisory cron (and the
pylint disable that hid it) with the idiomatic Odoo pattern:
- The ir.cron row lock (FOR NO KEY UPDATE SKIP LOCKED, ir_cron.py) already
prevents the cron from running concurrently with itself, so the manual 'claim'
commit was never needed.
- Per-record isolation now uses 'with self.env.cr.savepoint()'; the ir.cron
wrapper commits once on clean return (the pattern viin_account_auto_transfer /
viin_zalo use). At-least-once retry on the next tick; no transaction breakage.
- Tests no longer patch cr.commit (that patch was a symptom of the bad design).
Also resolves R8180 without a disable: Concern A + Concern B are now one
viin.approval.request model class (one class per model per module), the two
concerns kept in clearly-marked sections; advisory_runner.py merged in + removed.
Fixed the SALE-6 fixture to map partner/currency/user (the OmniApproval origin
sync would otherwise null the sale.order partner_id). 36/36 tests pass; flake8 +
full pylint_odoo enable-list both exit 0 with NO disables.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[ADD] viin_ai_approval(+account,+sale,+ops_brain): P-OPS-2 approval governance + AI advisory on human approvals
|
[FIX] viin_ai_approval,viin_ai_approval_account,viin_ai_approval_sale: pass Runbot test_pylint lint
Runbot Code-Quality stage (test_pylint: pylint_odoo + flake8) was red while
local --test-enable passed (the lint suite is not run by --test-enable).
Resolved every reported violation:
- E8102 invalid-commit (advisory_runner cron commits) -> documented + pylint disable
- W8150 odoo-addons-relative-import (tests importing own module) -> relative imports
- R8180 consider-merging-classes-inherited (Concern A/B separate _inherit) -> documented disable
- E741 ambiguous 'l', F401 unused imports, E303, F841 unused var
- W503 line break before binary operator -> restructured so no operator sits at a line boundary
Verified: exact test_flake8 + full test_pylint enable-list both exit 0; 36/36 tests still pass.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[DOC] docs,decisions: ADR-013 voice modality + ADR-014 dual deployment BYOK/managed
|
[DOC] docs,decisions: ADR-013 voice modality + ADR-014 dual deployment BYOK/managed
Tài liệu hoá voice agent modality cho cụm AI (doc-only, chưa code).
- ADR-013 (Proposed): voice loop audio in -> STT -> agentic loop CŨ -> TTS
-> audio out, BYOK-first. Stack tham chiếu license sạch thương mại +
tiếng Việt (PhoWhisper/faster-whisper, Chatterbox, Pipecat/LiveKit),
framing reference-default + swappable. Loại VibeVoice (vendor khuyến cáo
không dùng thương mại + watermark + supply risk + voice cloning vướng
Luật 91/2025). Ranh giới: user-initiated voice = execute theo ACL user
(không proposal); AI-proactive = proposal-approve-workflow. Anti-misfire
cho tool requires_confirmation (TTS readback + STT ack + nonce gate).
- ADR-014 (Proposed, CẦN CEO ratify): phân định #11 BYOK - cấm proxy LLM
vendor cloud giữ nguyên; managed compute cho model open-source self-host
là phạm trù riêng (bán compute Viindoo). BYOK trước; managed Phase 9
(cuối roadmap, gap Phase 8). Invariant chặn managed proxy LLM cloud.
- Cập nhật architecture (voice_interactive mode), data-models (call_tts,
model_type=tts, usage_log audio billing, deployment_model), operating-model,
security (§5.5 voice/biometric, PII forced-on external STT), roadmap
(Phase 7.1 voice + Phase 9 managed), conventions (audio codec), integration
(voice evidence -> Brain), AGENTS pointer #11 (CHƯA HIỆU LỰC tới khi ratify).
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[ADD] viin_ai_approval(+account,+sale,+ops_brain): P-OPS-2 approval governance + AI advisory on human approvals
|
[DOC] docs,decisions: ADR-013 Concern-B per-app advisory pattern
Records the reusable viin_ai_approval_<app> convention (auto_install bridge,
data-XML agent/topic seed, non-clobbering post_init_hook by stable code,
PII-minimised signal builder, 5-test checklist) extracted from the two
shipped pilots (account + sale). Status: proposed, pending owner sign-off.
|
Failed
|
|
|
|
|
|
|
|
merged
[ADD] viin_ai_approval(+account,+sale,+ops_brain): P-OPS-2 approval governance + AI advisory on human approvals
|
[REF] viin_ai_approval: data-driven state sync via approval-type code_* hooks
Follow the viin_approval idiom: do NOT override action_validate/refuse/cancel in
Python to trigger side-effects. Instead the seeded approval.request.type carries
code_validate_post / code_refuse_post / code_cancel_post inline hooks (safe_eval)
that call the proposal's public callbacks _on_approval_validated/refused/cancelled.
- viin_approval_request.py: remove the action_* overrides + _propagate_to_work_item;
keep only the evidence/link fields (ai_proposal_id, ai_trace_id, ai_prompt_snapshot,
ai_iteration_index).
- action_proposal.py: add the public callbacks (approved + work-item start + honest
single-step execute; rejected; back-to-draft).
- data/approval_request_type.xml: the three code_* hooks dispatch to the callbacks
via ai_proposal_id.
Verified identical green (0 failed of 10) in BOTH Community and full-EE addons sets;
grep confirms no action_* override remains. Tests assert business outcomes (proposal
approved/rejected/draft after the decision), mechanism-agnostic.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[ADD] viin_ai_approval(+account,+sale,+ops_brain): P-OPS-2 approval governance + AI advisory on human approvals
|
[ADD] viin_ai_approval,viin_ai_ops_brain: P-OPS-2 approval adapter + Brain evidence
Wire the propose -> DECIDE -> remember steps of the AI operating model (Phase 3.8,
opens gate M3). Two new Community-compatible modules + small reconciles:
* viin_ai_approval (depends viin_ai_ops, viin_approval 0.2.2): maps
viin.ai.action.proposal <-> viin.approval.request. action_route submits a
pending proposal to an approval request; action_validate/refuse/cancel
overrides sync state back to the proposal (+ work item). Sudo/high-risk
proposals are forced through approval (never auto-execute). Community fallback
executes an approved proposal single-step via its mapped ir.actions.server
(state='ai_prompt') under sudo, and only marks 'executed' when a side effect
actually ran - otherwise it stays 'approved' (execution deferred to P-OPS-3 /
the Enterprise viin_workflow_automation when present). Evidence (trace, prompt
snapshot, iteration index) captured on the request.
* viin_ai_ops_brain (depends viin_ai_ops, viin_brain): generates a Brain
"Approval Rationale" evidence page per proposal with mandatory T5 attribution
(is_ai_authored + agent + trace + confidence), untrusted text html-escaped,
linked back to proposal/work item/goal. Idempotent.
* viin_ai_ops: base proposal form now provides one shared oe_button_box (first
child of sheet) so adapter stat buttons hoist to a single control-panel box.
* viin_ai_agent: deprecated stub anchors (viin.ai.schedule,
viin.ai.approval.chain) docstrings point to the shipped viin_ai_approval
adapter; field anchors retained for v17 schema compatibility (removal deferred).
Tests: 54 Odoo-native tests pass (ACL, 2-way sync, sudo/high-risk enforce,
Community fallback honesty + Enterprise-defer, T5 attribution, cross-module
integration). The workflow-dependent fallback tests force _workflow_automation_installed
deterministically (FIRST-compliant) so they pass whether or not the Enterprise
module is installed. Visual UI/UX review PASS (single button box, AA contrast,
theme-faithful, console clean).
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[ADD] viin_ai_approval(+account,+sale,+ops_brain): P-OPS-2 approval governance + AI advisory on human approvals
|
[ADD] viin_ai_approval,viin_ai_ops_brain: P-OPS-2 approval adapter + Brain evidence
Wire the propose -> DECIDE -> remember steps of the AI operating model (Phase 3.8,
opens gate M3). Two new Community-compatible modules + small reconciles:
* viin_ai_approval (depends viin_ai_ops, viin_approval 0.2.2): maps
viin.ai.action.proposal <-> viin.approval.request. action_route submits a
pending proposal to an approval request; action_validate/refuse/cancel
overrides sync state back to the proposal (+ work item). Sudo/high-risk
proposals are forced through approval (never auto-execute). Community fallback
executes an approved proposal single-step via its mapped ir.actions.server
(state='ai_prompt') under sudo, and only marks 'executed' when a side effect
actually ran - otherwise it stays 'approved' (execution deferred to P-OPS-3).
Evidence (trace, prompt snapshot, iteration index) captured on the request.
* viin_ai_ops_brain (depends viin_ai_ops, viin_brain): generates a Brain
"Approval Rationale" evidence page per proposal with mandatory T5 attribution
(is_ai_authored + agent + trace + confidence), untrusted text html-escaped,
linked back to proposal/work item/goal. Idempotent.
* viin_ai_ops: base proposal form now provides one shared oe_button_box (first
child of sheet) so adapter stat buttons hoist to a single control-panel box.
* viin_ai_agent: deprecated stub anchors (viin.ai.schedule,
viin.ai.approval.chain) docstrings point to the shipped viin_ai_approval
adapter; field anchors retained for v17 schema compatibility (removal deferred).
Tests: 53 Odoo-native tests pass (ACL, 2-way sync, sudo/high-risk enforce,
Community fallback honesty, T5 attribution, cross-module integration). Visual
UI/UX review: PASS (single button box, AA contrast, theme-faithful, console clean).
|
Failed
|
|
|
|
|
|
|
|
merged
[DOC] Vision/mission first-read + ADR-012 bridge re-purpose + reconcile drift
|
[DOC] docs,AGENTS: vision/mission first-read + ADR-012 bridge re-purpose + reconcile drift
Anti-drift: add a single SSOT "Vision & Mission" block at the top of AGENTS.md
(auto-loaded; CLAUDE.md/GEMINI.md @import it) and make it the first mandatory read
in section 0, plus a vision-first pointer in docs/README.md. When code/roadmap drift,
vision is the anchor.
ADR-012 (new): seed master data per application. Brain viin_brain_<app> modules are
re-purposed as seed-master-data carriers per app (vault/page/template), and each
viin_ai_<app> connector seeds AI master data (agent/topic/tool/SQL template) per app.
This overrides ONLY the "bridges redundant -> removal at v1.2" clause of ADR-001; the
mail.thread universal-sidebar decision stands. ADR-001 gets a partially-superseded-by
pointer; decisions index updated.
Reconcile drift with shipped state:
- roadmap §2: add viin_ai_ops (P-OPS-1) + viin_ai_editor (ADR-010) + 5 connector stubs;
fix viin_ai_agent version 0.2.1; ADR count 8->10.
- roadmap §7: add viin_ai_editor row; bridges re-scoped to seed carriers; total 31->38.
- roadmap §8: add ADR-010/012 rows; WI count 4->8; clarify ADR-009 (no file yet).
- roadmap §10/§11 + Track B timeline + M2 gate: drop stale "remove v1.2" wording.
- AGENTS.md §3 status: Track A stopped at P-OPS-1, Track B (P16/ADR-010/WI-007) DONE.
Doc-only; no code changes. Reviving bridges + writing seed data is a later code phase.
|
Killed
|
Not started
|
Not finished
|
|
|
|
|
|
merged
[IMP] viin_brain: Brain editor UI/UX polish (#44)
|
[IMP] viin_brain: Brain editor UI/UX polish - white canvas, AA contrast, inline icon, symmetric padding, mobile collapse, declutter chrome, a11y labels (#44)
Addresses issue #44 (P16 follow-up): white-paper canvas, WCAG-AA breadcrumb +
property labels, inline page icon, symmetric editor padding, mobile single-column
collapse, decluttered context-pane chrome (overflow menu), deduped vault selector,
inline record-mention chips, property input label/id association. Includes the
code-review cleanup and tour updates for the new overflow navigation.
|
Killed
|
|
Not finished
|
|
|
|
|
|
merged
[IMP] viin_brain: Brain editor UI/UX polish (#44)
|
[IMP] viin_brain: Brain editor UI/UX polish - white canvas, AA contrast, inline icon, symmetric padding, mobile collapse, declutter chrome, a11y labels (#44)
Addresses issue #44 (P16 follow-up). Includes code-review cleanup (shadow token,
deduped chip styling, t-foreach overflow menu, centralised property a11y attrs,
structural header layout, label-for only on input-bearing types), eslint/prettier
formatting, and tour updates for the overflow tab navigation.
|
Failed
|
|
|
|
|