The copilot: refusals explained, procedures guided

What the copilot does with a structured refusal and with the real state of the installation: explain it in plain language, guide step by step, act only after confirmation — and what it never does.

The principle

A rail present everywhere, a provided context

The copilot rail sits on the right edge of every screen (visible on every capture on this page and on the pilotage page). It has three distinct modes — a free-form chat, an explain mode triggered by the last refusal, and a guide mode for the three end-to-end procedures below — but the rule that holds across all three is the same: the prompt is restricted to only the context provided. The copilot explains and guides from structured data the portal already computed; it never invents a figure of its own.

Explaining a refusal

“Why the last refusal?”

Any business-rule refusal a screen receives — a structured { code, params, message }, the same contract every business rule on this site produces — is published on a small event bus (lib/refusal-bus.ts) the moment backendFetch sees it, regardless of which screen triggered it. The copilot, mounted once globally, subscribes to that bus: the “Why the last refusal?” button appears the instant a refusal lands, on any screen, not only on document forms — creating a business rule with an invalid parameter counts too, as the capture below shows.

Creating an over-receipt tolerance rule with an invalid -5 parameter, then asking the copilot why the last refusal happened

Regles metier > New rule, an invalid tolerance triggers regle_parametre_invalide; the copilot reads the same code and params to explain it.

The context sent to GET /api/v1/ai/explain-refusal is built, never guessed: when the code maps to a configurable rule type (codeRefusVersTypeRegle, e.g. limite_credit_depasseelimite_credit_client), the active rule (type, parameters, condition, mode) is read from the same catalog Settings › Business Rules uses — never a second definition that could drift — plus a bounded read of the document when the refusal came from a write. A code with no configurable rule behind it (document_fige, referentiel_bloque…) still explains, just without the “active rule” section.

Copilot context (excerpt)
GET /api/v1/ai/explain-refusal
{
  "code": "regle_parametre_invalide",
  "params": { "parametre": "pct_max", "type": "sur_reception", "attendu": "minimum 0" },
  "message": "Parameter 'pct_max' is invalid for rule 'sur_reception' (expected: minimum 0)."
}

When the code does map to a rule type, the response carries a rules_settings_route — the portal turns it into a direct “Fix this rule” link next to the explanation, so the fix is one click away instead of a re-read of the message.

Guiding a procedure

“Guide me”

“Guide me” lists three end-to-end procedures, declared as plain Go data (internal/ai/procedures.go) the same way the rule catalog is declared — code stays compiled and tested, only labels are translated (copilot.procedures.<code>.*, mirroring rules.types.<code>.*). Opening one shows a stepper with the real state of the tenant: each step's “done” flag comes from a bounded COUNT(*) against the tenant's schema, not a guess.

ProcedureStepsPath
creer_fournisseur_et_commander6Supplier record → evaluation → purchase request → purchase order → reception → invoice.
lancer_production5Finished item → bill of materials → MO → release → stock closing (component consumption + finished-goods receipt).
cloturer_periode3Validate draft invoices → record payments → close the period.
Guide me open on the Launch a manufacturing order procedure, stepper with steps 2 to 5 already done and step 1's Do it for me form open

The stepper reads the tenant's real state (steps 2–5 already done here); “Do it for me” opens a mini-form for the remaining step.

Acting, only when confirmed

“Do it for me”

A step only offers “Do it for me” when two conditions hold: the step declares an action_entity (a supplier, an article, a purchase request…) and that entity has a mini-form registered (COPILOT_ACTION_FIELDS). Most steps — a purchase order, a reception — carry too much pricing and line logic for a voice-sized form, so they stay a plain link to the screen (“Do”), exactly as the plan intends: an action, or else a link, never an invented shortcut. When a mini-form exists, nothing writes until the user clicks Confirm and execute inside it — the same explicit confirmation gate the voice agent uses for a typed command.

After a successful action, the procedure's state is recalculated from the same COUNT(*) the stepper reads — never a locally patched flag — and the created document is added to a recap list linking straight to its record, so a full run through “Create a supplier and order” ends with six clickable proofs, not a blind “done”.

What it never does

Explains, does not decide

The copilot never writes without an explicit confirmation click, never invents a rule type, a threshold or a figure the portal has not already computed, and never skips a mini-form for an entity that carries pricing or costing logic — those stay a link to the full screen. On the flow-designer and on an affair page, it reads the exact same risk-signal JSON the panel already shows; nothing is recalculated on the AI side. It explains and guides — the write path stays the one the rest of this site describes: typed rules, status profiles, and the generic CRUD handler.

Back to Business Rules and Control
See also : Deployment · Pilotage and risk signals · Status profiles