Name: [ADD] viin_ai_ops: Operating Layer foundation (P-OPS-1)

State: Killed

PR State: merged

PR Author: David Tran

PR Author Email:

PR: #36

Committer: David Tran

Committer Email: davidtran.hp@gmail.com

Commit: c93bc741d18db308e9ce9c5b49aa0e6a8eccb82c

Description:

                                            [FIX] viin_ai_chat: absent pending tool call → NO_PENDING, not a server warning

Root cause (not the symptom): /viin_ai/chat/tool_confirm raised
UserError('No pending tool call: %s') whenever _load_pending_tool
returned None. But an absent pending state is a NORMAL, designed-in
client condition — the proposal was already handled, the single-use
nonce was burned, the session-stored state expired, or the user
double-submitted the dialog. Raising UserError from a JSON route makes
Odoo log it at WARNING on the odoo.http logger, so this fired for
ordinary user behaviour in production (and surfaced on runbot as
'Subbuild #...: No pending tool call: wi15-nonce-burn-001').

The sibling NONCE_MISMATCH condition already returns a structured,
FE-renderable error instead of raising. Make the no-pending branch do
the same: return {status:'error', error_code:'NO_PENDING'}. The agent
loop is never entered, nothing is logged, the FE renders a friendly
recovery message via its existing status==='error' branch.

This supersedes the earlier change that merely @mute_logger'd the
AC10 test — that hid the test warning while production logs kept
filling. Tests now assert the corrected contract (intent, not output):
- AC06: unknown/expired id → NO_PENDING + tool never executed
- AC10: post-burn retry → NO_PENDING (was: tolerate UserError)
docs/ai/security.md documents the NO_PENDING contract beside NONCE_MISMATCH.
                                            

Branch: 17.0

Instance ID: 0

Age:

Up-time: Not finished