MdgSuite — User Manual
This manual covers the day-to-day workflows for WMS operators and tenant ADMINs. If you are looking for the architecture, data model, or SECS/GEM heritage, read The Agentic Org instead.
Audience: tenant ADMIN (full access to setup pages) and warehouse OPERATOR (day-to-day app usage). Where a section is ADMIN-only we mark it explicitly.
🔑 Sign in & 2FA
MdgSuite uses a two-factor authentication flow: email + password, then a 6-digit TOTP code from an authenticator app (Google Authenticator, Authy, 1Password, Bitwarden, any RFC-6238 compliant app).
Enter email and password
On the login screen, type your full email and password. If
your company uses a bare username (e.g. marco), the
system appends the tenant's default domain for you.
First-ever login — enrol TOTP
A QR code appears. Scan it with your authenticator app. The app now shows a rotating 6-digit code. Type the current code on screen to confirm enrolment. From now on, this is your second factor.
Subsequent logins — type the code
After email + password, the app asks for the current 6-digit code. Type it. You are in.
🔒 Passwords & PINs
Password policy
The default policy requires at least 8 characters, including:
- one uppercase letter
- one lowercase letter
- one digit
- one special character (e.g.
!,?,#,$)
Your tenant ADMIN can tighten this further in Security Setup but never loosen it.
Badge PIN vs password
WMS operators sign in at the warehouse terminal with a badge code + PIN — typically 4 or 6 digits. This is not the password. Passwords are for the web app; PINs are for the terminal. You can change either one from the user profile menu.
🏡 First-tenant bootstrap (ADMIN)
A fresh WMS instance lands without any tenant provisioned. The first person to log in creates the tenant by supplying a company name on the login form alongside their email and password.
The system:
- creates a dedicated tenant database
mdgwms_<id> - seeds default settings (warehouses, CCNL, governance defaults)
- promotes you to ADMIN for that tenant
🧠 Agent chats — how they work
MdgSuite ships with a virtual C-suite. Each persona owns a different lens on the business; you interact through a chat window pinned to the relevant context (plan, job, or the current company as a whole).
| Persona | Owns | Typical questions |
|---|---|---|
| COO | Plan quality, bottlenecks, operational rebalancing | «Where's the backlog?», «Who's overloaded this week?», «Why did score regress vs last week?» |
| CFO | OT budget, SLA tardiness cost, rebalance churn | «How far are we from the weekly OT budget?», «If I raise MaxOT to 120 minutes, what's the euro delta?» |
| CHRO | CCNL caps, consent renewals, rest windows | «Anyone at risk of breaching weekly overtime?», «Which operators need a consent refresh this month?» |
| Planner | The active plan on a specific job/run | «Rebalance Sara from picking to packing», «Why is mission X unscheduled?», «What if I raise send-ahead to 40%?» |
| CMO | Customer SLA risk, ship-late patterns, post-sale complaint trends | «Which customers are at risk of missing their SLA this week?», «Who is accumulating non-conformities?», «Any recurring ship-late to a single customer?» |
| QA | Shelf-life exposure, storage-class compliance, QC pass-rate trend | «Which lots are inside the block-ship window?», «Any incompatible items in the same bin?», «Is the QC pass rate slipping this week?» |
| CEO
read-only |
Cross-domain synthesis. Reads CFO / COO / CHRO / QA / CMO memories and writes an executive briefing. Never applies actions — names the persona who should. | Send an empty message for the daily briefing. «Give me a one-bullet summary per domain for today.», «What are the two decisions I need to take this week?» |
🍭 Action pills
When the agent proposes to do something (update a setting, run a plan, rebalance), the proposal lands as a coloured pill at the end of the chat bubble. The colour tells you the outcome:
| Pill | Meaning | Action |
|---|---|---|
| ✓ applied | Action ran automatically (within the Governance threshold). The system state has already changed. | Nothing to do — verify the change if you want. |
| ⏸ pending | Action is above the autonomy threshold (e.g. HIGH impact). Waiting for a human decision. | Click Approve on the pill OR approve from Telegram if configured. See Approve & reject. |
| · skipped | The agent linked you to the right admin page instead of acting. Common when the proposed change is out of the agent's scope. | Follow the link and edit manually. |
| ⚠ failed | Action was attempted but the backend rejected it (validation, conflicting state, network). | Hover the pill for the reason. Fix the precondition or ask differently. |
Contextual buttons
Some pills carry extra buttons:
- Approve — applies a pending action.
- ▶ Run now — triggers a recompute of
the linked job. Rarely needed for
rerunany more: as of 2026-04-19 the Planner chat runs the scheduler inline when it emitsrerun, and the reply already carries the new score / overtime / unscheduled numbers. The button is still there for the other clone actions (apply_rebalance,apply_level,apply_absence) which create a clone inREADY. - ↗ Open new job — navigates to the newly-created job so you can inspect the output. The clone carries an auto-generated Label like «Rebalance (2 moves) · MaxOT 60 · OTw×2» so you can tell it apart from the original at a glance.
🧭 What-if simulations
The Planner can recompute the current plan with temporary setting overrides — overtime cap raised, spillover target lowered, MaxOT per day bumped, an operator marked absent, and so on. The tenant's real defaults are never touched until you explicitly promote the override. This is the day-to-day what-if tool.
Trigger a what-if
Open the 💬 Chat with the Planner section on the UFCP page (WMS) or on a specific Job (UFCP SaaS) and phrase the question as a hypothetical. Examples in plain English or Italian:
- «What if I raise MaxOT to 120 minutes per day?»
- «Rerun with spillover target at 85%.»
- «Simula assenza di Sara martedì e ricalcola.»
- «Prova send-ahead al 40% su tutte le operazioni.»
The Planner invokes one of the tools scoped to simulation
(update_setting with scope=Job, then
rerun, or one of apply_rebalance /
apply_level / apply_absence) and
recomputes. The tenant's default settings are not changed
— only this Job's SettingsOverrideJson is.
The SIMULATION banner
Above the plan summary a yellow banner appears with the label SIMULATION, a one-line summary of what was changed (e.g. «MaxOtMinutesPerDay: 90 → 120»), and two buttons on the right. Expanding the banner lists every override field-by-field.
As long as the banner is visible, every number on the page — Gantt, daily capacity, violations, plan score — reflects the simulated plan, not the tenant default.
Discard vs Promote
| Button | What it does | When to use |
|---|---|---|
| Discard override | Clears this Job's SettingsOverrideJson
and recomputes with the tenant defaults. The banner
disappears. |
The simulated result is worse, or you were just exploring. Safe, reversible, LOW impact — no approval needed. |
| Promote to tenant default | Copies the override values into the tenant-wide UFCP / CFO / CHRO setting rows. From now on every new plan for this tenant uses the new values. | The simulation is the direction you want the whole business to go. HIGH-impact action: lands as ⏸ pending unless the tenant's autonomy threshold is HIGH. Approve from the chat pill or Telegram. |
Named simulations — the SIM badge
When the Planner runs apply_rebalance, apply_level,
or apply_absence, the clone is saved with an
auto-generated Label (max 80 chars) summarising what
changed — for example:
- Rebalance (2 moves) · MaxOT 60 · OTw×2
- Level (Tuesday → Wednesday)
- Absence Mario · 2026-04-22
You see this label everywhere the Job shows up: the
Simulations page, the UFCP page header, the chat pill that
offers to open the clone. Any Job that carries a
SettingsOverrideJson override also carries a small
SIM badge next to its status — it's the visual
cue that you're looking at a what-if, not at a canonical plan.
The UFCP page job-header bar
After you press Calculate Plan or open a Job from
the Simulations page, a thin header bar appears above the plan view:
a short Job id + an editable Label input + a
Save button. Use it to rename a simulation in place
before you share the link with a colleague. An empty label clears the
name (back to «no label»); the endpoint behind the button
is PATCH /api/jobs/{id}/label (WMS) or
PATCH /v1/jobs/{id}/label (UFCP).
The Simulations page
Open 🧪 Simulations from the WMS sidebar (under the Agents collapsible section, next to the chats) to get a list of your recent Jobs. One row per Job, each with:
- Label — inline-editable. Click, type, press Enter (or click away) to save. Empty clears the name.
- Status pill —
READY/RUNNING/COMPLETED/FAILED, same colour scheme as the action pills. - SIM badge — present only when the Job has a settings override. Canonical plans show a plain status pill and nothing more.
- Open — reloads the Job in the UFCP Planner page, so you can walk through the Gantt and the simulation banner again.
- Delete — drops the Job entirely. The tenant defaults are unaffected; the deletion is local to this Job.
Comparing scenarios
Today there is no automated side-by-side diff — each what-if
replaces the previous one on the same Job, and clone-based actions
(apply_*) produce distinct Jobs. To compare two scenarios
end-to-end:
- Run scenario A via the chat. The clone gets an auto-label; rename it from the Simulations page if you want something shorter.
- Go back to the original Job (same page, the parent Job id is in the header), run scenario B.
- Open Simulations and compare metrics between the two clones — OT minutes, unscheduled count, plan score. Click each Open to inspect the Gantt.
A dedicated side-by-side diff (metric table rendered for two Jobs at once) is on the roadmap.
✅ Approve & reject
Every action has an impact level: LOW, MEDIUM, HIGH. Each tenant sets an autonomy threshold in Agent Governance. Actions at or below the threshold are auto-applied; higher-impact actions land as ⏸ pending and wait.
Approve from the chat
- Scroll to the pending pill in the chat.
- Click Approve.
- The pill turns ✓ applied or ⚠ failed depending on the backend's validation.
Reject from the chat
Click Reject on the pending pill. The action is
archived as rejected_action with your user id — the
agent will not propose the exact same thing again within 24 hours.
ApprovalTimeoutMinutes
window (default 60), the system auto-rejects it with reason
via=timeout. Never assume a forgotten pending will
eventually apply itself.
📱 Telegram approval
If the tenant has Telegram configured, high-impact pending actions also land as a message in a chosen chat with inline Approve and Reject buttons. Tapping one of them closes the loop the same way the in-chat pill does.
Setup (ADMIN)
- Create a Telegram bot via @BotFather and keep the bot token.
- Add the bot to a private chat with yourself (or a team channel);
send
/startso Telegram records your chat id. - In MdgSuite open Agent Governance, fill Telegram Bot Token and Telegram Chat Id, save.
- Go to Telegram Setup and click Install webhook. The bot now forwards button presses back to MdgSuite.
⚖ Agent Governance (ADMIN)
One-page control of how autonomous the agents can be. Tenant-wide settings, applied to every persona.
| Field | What it does |
|---|---|
AutoApproveEnabled |
Master switch. When off, every action the agent proposes lands as ⏸ pending regardless of level — classic human-in-the-loop. |
AutoApproveMaxLevel |
Highest impact level the system applies without asking.
READ_ONLY = read-only actions only (nothing is
auto-applied in practice today). LOW = auto-apply
LOW only. MEDIUM (default) = LOW + MEDIUM.
HIGH = everything, including HIGH. Use
HIGH only in non-production tenants. |
ApprovalTimeoutMinutes |
After this many minutes a pending action is auto-rejected
(fail-closed). 0 means «never expire»
— only set this if you have operational discipline to
clear pending rows manually. |
AutonomousEnabled |
Master switch for the autonomous decision runtime
(observe → decide → apply). Off by
default — tenants opt in explicitly once they
trust the ecosystem enough to let agents act without a human
triggering the chat. When on, background agents may propose
actions on every tick; the usual AutoApproveMaxLevel
gate still applies to each proposal. |
DailyActionBudget |
Hard cap on auto-applied background actions per tenant per UTC day. Once the count is reached, the autonomous runtime skips further dispatches until the next midnight rollover. Default 5. This is the safety net against a runaway agent spamming the system. |
AutonomousShadowMode |
Trust-builder switch. Off by default. When on
the autonomous runtime keeps ticking on its normal cadence and
keeps running every agent's decide step, but every
proposal lands as ⏸ pending
on Agent Decisions regardless
of impact level — your AutoApproveMaxLevel
is ignored while shadow is on. You review each row by hand
and approve or reject with the normal pills. Use it for
weeks of real traffic before flipping it off
and letting auto-apply actually happen. |
TelegramBotToken & TelegramChatId |
Out-of-band approval channel. See Telegram approval. |
- Flip
AutonomousEnabled = trueandAutonomousShadowMode = trueat the same time. The background runtime starts ticking; every proposal lands ⏸ pending on Agent Decisions so you can review what the agents would have done without them actually doing it. - Let it run for at least a couple of weeks of real traffic. Walk the Agent Decisions feed, read the rationales (Input JSON), approve the good ones by hand, reject the bad ones, and let the 30-day Outcome tile build up a verdict history.
- Once you trust the proposal quality, flip
AutonomousShadowModeoff. The same agents now auto-apply up toAutoApproveMaxLevel. KeepDailyActionBudgetsmall (3–5) at first and raise only after a week of healthy auto-applies.
📡 Observer Setup (ADMIN)
Each C-level persona has a background observer that ticks on its own schedule. Use this page to turn them on/off and set the interval per persona.
Enabled: the persona is allowed to wake. Interval: minutes between scheduled ticks (5–1440). Last / Next run: for observability — lets you confirm ticks are actually happening.
AUTONOMOUS
that receives every HIGH-severity event alongside the real C-levels.
End-to-end path: observer writes a HIGH concern → bus notifies
→ autonomous runtime immediately runs its three agents for the
tenant → the dispatched proposal lands in
Agent Decisions and the summary lands
in the Chamber as an
autonomous_action. The 5-minute timer stays as the fallback
for missed NOTIFY deliveries, so you still see activity even if the
bus connection is reconnecting. In practice: if you flip a
production agent that raises a HIGH concern, the autonomous proposal
now appears in the audit log within a second or two — no
waiting for the next poll.
Typical values:
- CFO 60 min — weekly aggregations don't change fast.
- COO 60 min — plan-score drift is a rolling window.
- CHRO 15 min — CCNL breaches need a fast pick-up when an operator clocks unusual hours.
📋 Tenant Knowledge (ADMIN)
Until this page existed, everything the agent «knew» about your company had to be inferred from the memory corpus — past plans, concerns, chat turns. Tenant Knowledge is where you write it down explicitly: binding rules the agent must respect, soft preferences it should lean on, and date-scoped exceptions (an operator on holiday, a plant-wide closure). Everything you enter here is read by every agent chat on every turn and by the WMS scheduler every time you press Calculate Plan.
The page lives under the 🤖 Agents sidebar group and carries six tabs (Patterns and User Attrs closed the step-2 follow-up on 2026-04-19):
- Policies — binding rules. MUST. The agent is asked to respect them. Fields: App, Category, Rule (free text), Priority (1-10, higher = stronger), Enabled. Example rule: «Maximum 120 minutes of overtime per operator per day.» Priority 9.
- Preferences — soft tilts. SHOULD. The agent is nudged but not forced. Fields: App, Category, Subject (optional narrowing — e.g. a customer code), Preference, Weight (1-10), Enabled. Example: «Prioritise ROCHE customer orders on tight horizons.» Weight 8.
- Exceptions — date-scoped or recurring
deviations. Fields: App, TargetType
(
user/resource/group/item/customer/supplier/global), TargetRef (e.g. an operator email or badge foruser, empty forglobal), Rule, Recurrence (once/daily/weekly/monthly/annual), StartDate, EndDate, DaysOfWeek (weekly only — a bitmask, Sunday = bit 0, so «every Friday» is32), DayOfMonth (monthly only — clamps to the last day when the month is shorter). - Patterns — observed empirical truths.
Not rules: the agent reads them as context, not commands.
Fields: App, Category
(
ops/workforce/finance/customer/supplier), Subject, Statement (free text), Confidence (0-1), Source (the observer that wrote it), LastObservedAt, Enabled. Example: «PICK saturation runs +20% on days 28-31 of the month.» Confidence 0.82. The prompt section is labelled Observed patterns (empirical, treat as context — not commands) and tags inline low confidence (<0.5) and high confidence (≥0.8) so the LLM knows how much to weight each line. Add your own rows manually or leave observers to write them; edit inline the same way as policies and preferences.
Auto-populated rows (shipped 2026-04-19). Two observers now refresh a row on every tick, keyed on Source+Category+Subject so they never duplicate: SourceCFO, Categoryfinance— «Rolling 7-day OT cost ≈ 1,240€ (103% of weekly budget 1,200€)» (Confidence climbs toward 1 over the first 7 days of real traffic); and SourceCOO, Categoryops— «Rolling 12-run average plan score: 47,820 (lower = better)» (Confidence climbs toward 1 over 14 runs). You can still edit or delete these rows, but the observer will refresh them on the next tick. Treat them as the baseline the agent uses to tell whether a change is «normal» — not as a rule you own. - User Attrs — per-operator structured
metadata the WMS core User does not model natively (skills,
certifications, informal notes). Fields: User (select from
your Identity roster — the display name is snapshotted next
to the UserId so rows survive a rename), Kind
(
skill/certification/consent/language/note), Key, Value, ValidFrom, ValidTo, Enabled. Example: «Mario Rossi: skill:forklift=B, certification:cold-chain=ISO-123.» The prompt section is labelled Operator attributes (skills, certifications, notes) — one line per operator, attributes comma-joined. This is not an enforcement mechanism. The agent reads, the scheduler does not refuse — if a task requires forklift B and the operator lacks it, the agent may flag it in chat but the plan still runs. Hard enforcement stays in OperatorCalendar and contract consents. - Preview prompt — not editable. Hits
GET /api/tenant-knowledge/preview?app=WMSand renders the literal text the LLM will receive appended to the system prompt, laid out as## Tenant rules→### Binding policies (MUST)→### Soft preferences (SHOULD)→### Active exceptions (CURRENT WINDOW)→### Observed patterns (empirical, treat as context — not commands)→### Operator attributes (skills, certifications, notes). Use this tab to double-check the agent is actually seeing what you think it's seeing.
First-time walkthrough
Open the page and click 📚 Load examples
The button seeds 12 editable templates (4 policies + 4 preferences + 4 exceptions). These are examples, not hidden defaults — you are expected to edit or delete them. Nothing in them is treated specially by the engine.
Adapt a policy or preference
Click into any cell and edit inline. The App, Category, Recurrence and TargetType cells are controlled dropdowns with short explanations; the rest is free text. Column headers carry tooltips — hover the ⓘ marker next to each header if you are unsure what a field means.
Add a concrete exception
Say Giuseppe Verdi is off on 21 April
2026. On the Exceptions tab: set
TargetType user, TargetRef
giuseppe.verdi@your-domain (the Identity email,
the display name if unambiguous, or the WMS BadgeCode
all work — case-insensitive),
Recurrence once,
Rule «Vacation». The
StartDate / EndDate fields do not yet have a
date picker in the UI — set them via
PATCH /api/tenant-knowledge/exceptions/{id} with
a JSON body {"startDate":"2026-04-21","endDate":"2026-04-21"}
until the picker ships.
Open the Preview prompt tab
Confirm that under ### Active exceptions (CURRENT WINDOW) you see a line like «user giuseppe.verdi@… — Vacation (2026-04-21)». If the line is missing, the recurrence or the date range does not cover today — re-check them. If the block is entirely empty, nothing gets injected and the agent behaves as before.
Run Calculate Plan in WMS
On 21 April 2026 Giuseppe must not appear
in the resulting plan — the scheduler now treats his
user-targeted exception identically to an
AbsenceCode on his operator calendar. The same holds
for global exceptions: they short-circuit
scheduling tenant-wide for their active dates. For v1, all
other TargetType values (resource,
group, item, customer,
supplier) are surfaced to the chat via the system
prompt but do not yet steer the scheduler;
the agent reads them and you approve manually.
How to think about it
- Keep policies few and high-priority. A policy list of 30 items waters itself down in the prompt; five crisp rules with priority 8-10 carry more weight.
- Use preferences for business flavour (customer priorities, seasonal tilts, internal conventions) and policies for hard constraints (caps, forbidden patterns).
- Exceptions are not a calendar. Single-day absences still fit on the operator calendar; use exceptions for recurring patterns («Mario every Friday») or for non-operator targets (a supplier closure, a plant-wide holiday, a customer freeze period).
- Everything the agent respects (or breaches) shows up in Agent Decisions. When a decision's outcome pill goes negative, checking Tenant Knowledge is the first place to look — the agent may be missing a rule you haven't written down yet.
mdgagent_<companyId> DB
— the same one that holds AgentMemoryItem and
AgentDecisionLog. A company that runs both WMS and
UFCP shares one rulebook; the same policy applies in both hosts.
💰 CFO Setup (ADMIN)
Thresholds the CFO uses to decide whether to raise a concern.
| Field | Meaning |
|---|---|
OvertimeRatePerMinute | Euro/minute used to convert OT minutes into euros. |
OvertimeWeeklyBudget | If the last 7 days of plans exceed this, a concern is raised. |
UnscheduledAccumulationAlertDays | How many consecutive days with unscheduled work triggers a capacity concern. |
MaxRebalancesPerWeek | A concern is raised when total rebalances across plans in the last 7 days exceed this — symptom of mis-sized departments. |
SlaTardinessThresholdDays | Cumulative tardiness across recent plans above this raises an SLA-risk concern. |
🧑⚖️ CHRO Setup (ADMIN)
Policy guardrails on top of the CCNL master data. The CCNL itself is the hard floor — these settings can only make the rule stricter, not looser.
| Field | Meaning |
|---|---|
MaxOvertimeCapOverrideMinutesPerDay |
Optional tenant-wide override of the CCNL daily OT cap. Must be ≤ the CCNL limit. |
MinRestHoursOverride | Optional minimum rest between shifts — again, must tighten the CCNL floor, not relax it. |
ConsentRenewalMonths | How often a night-shift or special-task consent must be renewed. The CHRO observer raises concerns when a consent is expiring or expired. |
EnforcementMode | On a CCNL breach:
WARN (concern only) or BLOCK (concern +
the plan is marked unshippable). |
📊 COO Setup (ADMIN)
Objective weights and strategy priority the Planner uses when generating a plan. The COO observer watches the drift of the resulting plan score over time.
StrategyPriority— ordered list of high-level goals (e.g. tardiness > cost > utilization).ObjectiveWeights— fine-grained numeric weights for each scoring dimension.SpilloverObjective— what to optimise when work exceeds capacity for the day (defer vs over-assign).
🛡 Security Setup (ADMIN)
Tenant-wide security policy + per-user reset actions.
Policy
- Lockout: failed-attempt threshold + lockout duration.
- Login window: optional hours-of-day the login is accepted.
- IP allow-list: restrict login to known subnets. Empty = no restriction.
- Password policy: min length, required character classes (always at least the platform default).
Per-user reset
- Reset password — forces the user to choose a new password on next login.
- Reset 2FA — wipes the TOTP enrolment; the user re-enrols on next login.
🤖 AI Setup (ADMIN)
Configure which AI provider powers the agents. MdgSuite supports Anthropic (Claude), OpenAI (GPT), Google (Gemini), and Ollama (local). All four can be enabled at once; the priority order decides cascade.
- Pick a provider, paste the API key. The key is encrypted at rest.
- Choose the default model. Some hints:
- Anthropic:
claude-sonnet-4-5(balanced) orclaude-opus-4-5(deepest reasoning). - OpenAI:
gpt-4o(balanced) orgpt-4.1. - Gemini:
gemini-2.5-flash. - Ollama:
qwen2.5:7bruns comfortably on a single GPU.
- Anthropic:
- Set Max tokens and Temperature. Defaults are 2048 / 0.3 — low temperature favours consistent, auditable replies.
- Set Priority. Lower = tried first. If the top provider times out, the cascade moves to the next.
🗣 Agent Chamber
A read-only timeline of what the agents have been doing. Use it to audit autonomous actions, read concerns, or simply follow along while work happens.
Entry kinds
- concern — an observer flagged something worth attention.
- action — an autonomous or approved change was applied. Includes the target endpoint and the response.
- pending_action — a change is waiting for approval.
- rejected_action — a change was rejected (by a human or by timeout).
- autonomous_action — the narrative companion of a row in Agent Decisions. Every time the autonomous runtime dispatches a decision (whether it auto-applied or just parked the proposal as pending), a one-line summary lands here in the form «CFO auto-applied update_setting: <rationale>» or «CHRO proposed (pending review) update_setting: …». Shipped 2026-04-19 so you can follow what happened overnight without having to scroll the audit table — concerns, autonomous actions, meetings and chat turns now sit in the same timeline.
- meeting — a round-by-round transcript of
a multi-agent meeting (convened via
convene_meeting). - interaction — a user-to-agent chat turn.
Filters & search
- Role filter: see only CFO, COO, CHRO, Planner, ...
- Kind filter: see only concerns, or only meetings, ...
- Free-text search: semantic retrieval over the full memory (pgvector + HNSW).
🔎 Agent Decisions (ADMIN)
The Chamber shows agent activity as a narrative timeline — useful to follow along. Agent Decisions, reached from the same menu group, is the structured audit trail: every action that reaches the dispatcher lands here as a row, regardless of whether it came from a chat click, a Telegram button, or the autonomous runtime. Use it to answer questions like «what did the agent do last week and why did it think it was the right call?».
What's in each row
- Persona and App — who proposed, from which host (WMS or UFCP of the same company).
- Action code (e.g.
update_setting,apply_rebalance,validate_plan) and Level (LOW / MEDIUM / HIGH) as classified by the dispatcher. - Trigger distinguishes the origin surface:
chat— the reply came from a human chat turn.approve— a human clicked Approve on a pending pill (web UI).telegram— a human tapped Approve on the Telegram card.background— an observer or the autonomous runtime produced it without a user turn.
- Status lifecycle:
- auto within-threshold action, auto-applied.
- manual approved by a human.
- pending waiting on approval.
- rejected dropped by a human.
- failed apply threw — check the Result block for the error.
- expired approval window elapsed with no decision.
- Esito (Outcome) — verdict the evaluator wrote after comparing the plan score before and after the decision. A colored pill: positive (plan score improved by more than 5%), neutral (within ±5%), negative (worsened by more than 5%), inconclusive (no comparable baseline or successor). A long dash means the sweep hasn't scored the row yet — it runs every 15 minutes and only considers rows older than 6 hours. Hover the pill for the baseline → successor score and Δ%.
- Duration — time from proposal to terminal state. Useful to spot stuck pendings or slow dispatchers.
The 30-day summary tile
The top of the page shows an Outcome — last 30 days rollup: five counters (positive / neutral / negative / inconclusive / not-yet-evaluated) plus a per-persona breakdown with an average Δ score column. A negative average means the agents of that persona, on balance, improved the plan over the window — that is the single number to trust when answering «is autonomy paying off?» for a tenant.
Filters
The filter bar accepts any combination of persona, action code, status, from, to. Results are newest-first and hard-capped at 200 rows per page; use the Prev / Next buttons to walk back in time.
The detail modal
Clicking Open on a row reveals four JSON blocks:
- Input — context snapshot at proposal time (rationale for background actions, pointer to the bound Job for apply_* actions).
- Output — the raw dispatcher payload (same shape the LLM emitted in the ACTION tag).
- Result — the apply outcome: applied details on success, error envelope on failure, rejection reason for rejected rows.
- Outcome KPIs — the evaluator's workings: baseline plan id + score, successor plan id + score, delta, and the neutral-band threshold used. For inconclusive verdicts it shows the reason (no baseline, no successor yet, decision not in terminal state).
AgentDecisionLog table in
mdgagent_<companyId> — the same DB that
holds AgentMemoryItem. When a company runs both WMS
and UFCP, both hosts write into the same log, so the Agent
Decisions page in either PWA shows a unified view.
What an autonomous proposal looks like
When the autonomous runtime (not a chat turn) produces a row, the
Trigger column reads background. As of
2026-04-19 three agents can populate those rows:
AutonomousRebalanceAgent (persona COO),
AutonomousCfoBudgetAgent (persona CFO),
AutonomousChroComplianceAgent (persona CHRO). Read them by
opening the detail modal and scanning the Input block —
the agent puts its rationale there in plain prose next to the raw
numbers. The two new personas read as follows:
- CFO budget proposal — Persona
CFO, Actionupdate_setting, Level LOW. Typical rationale: «Rolling 7-day OT spend is at €1,240 / €1,200 weekly budget (103%). CurrentMaxOverTimeMinutesPerDay = 60; tightening to 45 (−25%).» The Output block shows the concreteupdate_settingpayload that will bringMaxOverTimeMinutesPerDaydown. The agent never loosens the cap and never drops below a 30-minute floor, so a rejected row just leaves today's cap in place. - CHRO compliance proposal — Persona
CHRO, Actionupdate_setting, Level MEDIUM. Typical rationale: «Tenant cap is 120 min/day but the strictest in-use CCNL (CCNL_CHIM_FARM_IND) allows 90. Pinning tenant cap to 90 to stay compliant.» The Output block carries theupdate_settingpayload with the strict value. Safe by construction: the agent only proposes the cap when the tenant-wide setting is looser than the contract allows — never the other way round, never on zero / negative caps.
Both proposals respect AutonomousShadowMode:
while shadow is on they land pending regardless of level. Once
you flip shadow off, LOW (CFO budget) is inside the default
AutoApproveMaxLevel = MEDIUM and auto-applies; MEDIUM
(CHRO compliance) also auto-applies under the default, but we recommend
keeping the CHRO proposals in manual review for a bit longer than the
CFO ones — they move a global cap that affects every upcoming
plan.
Thumbs-up / thumbs-down on a decision
Clicking Open on a row now also surfaces two buttons next to the outcome pill: 👍 Useful and 👎 Bad call. Pressing one writes your vote against the baseline plan memory that bracketed the decision:
- Positive votes push the memory's feedback score toward +1; negative votes push it toward −1. The hybrid recall the agent uses on future chat turns then surfaces upvoted memories first and down-ranks the ones you flagged — your thumbs literally steer future retrieval.
- Re-clicking the same thumb does nothing (idempotent). Switching from 👍 to 👎 (or back) computes the net delta so your score never compounds. Clearing a vote reverts the shift.
- The outcome sweep also writes to this score automatically (±0.5 per positive / negative verdict), so even tenants that don't vote benefit from the signal.
🧩 Memory Browser & GDPR (ADMIN)
Before this page existed, the only way an admin could inspect what
the agents remembered about the company was to open a SQL client and
query mdgagent_<companyId> by hand. The
Memory Browser page (under
🤖 Agents, 🧩 icon, ADMIN-only) now
exposes that corpus as a plain table with filters, per-row delete,
and two GDPR controls at the top.
Browsing the corpus
- Open 🤖 Agents → Memory Browser.
- Set the filters you care about: Kind
(
plan,concern,action,interaction,meeting, ...), Role (the producing persona — COO, CFO, CHRO, ...), UserId (if you're hunting for everything tied to a specific user), Take (default 100, max 500). - Press Search. The raw rows come back ordered by most recent first, with columns for CreatedAt, Kind, Role, App, Content (truncated to 400 chars with a «...» expand), FeedbackScore (the number in [-1, +1] described above), and UserId.
- Click the inline 🗑 icon on any row to delete just that entry. Useful to scrub a single stale concern without touching the rest of the history.
GDPR Article 15 — export a user's data
Type the target user's UserId (a GUID; grab it from the
Security Setup user roster) into the top control bar and press
Export JSON. The browser downloads a
agent-memory-export-<userId>.json file containing:
- Every memory row where
UserIdequals the target — plans, interactions, concerns, actions, anything. - Every
UserAttributerow attached to the same user (skills, certifications, consents, notes).
Send this bundle to the data subject. It satisfies the right of access without you writing any SQL.
GDPR Article 17 — purge a user's data
- Fill the UserId field with the target GUID.
- Fill the Display name field with the exact name the agents have been using in free-prose memory (e.g. «Mario Rossi»). This is important: some memory rows reference the user by name inside their Content rather than by UserId — without the display name, those rows would survive.
- Press Purge all for user. You will be asked to confirm twice.
The backend then deletes: every memory whose UserId
matches OR whose Content contains the display-name
substring, plus every UserAttribute attached to the
user. You get a count of deleted rows back.
mdgagent_<companyId>, running a purge on
either host scrubs the entire corpus — you don't need to
do it twice.
How long memories live anyway
Even without manual purging, plan and interaction rows expire automatically via a background sweep:
- plan rows — 90 days.
- interaction rows — 60 days.
- Everything else (concern, action, note, pattern, pending_action, meeting) is kept forever so the company's long-term memory survives.
The sweep runs daily per company and is silent — you won't
see it in Agent Decisions. If you need to verify it ran, check the
server logs for MemoryRetentionService.
🔧 Troubleshooting
«I approved an action but nothing happened.»
Check the pill: if it turned ⚠ failed, hover for the validation error. If it stays ⏸ pending, the backend didn't receive your click — refresh the page and try again. If the pending window has expired, the action is gone (auto-rejected); re-ask the agent.
«The observer hasn't ticked since yesterday.»
Open Observer Setup and look at Last run / Next run. If Enabled is off, turn it on. If Last error is populated, read the error: it is usually a missing configuration (no CFO thresholds, no completed job in 48h) rather than a crash.
«Telegram approval doesn't work.»
Open the Telegram Setup page and re-run Install webhook. Then click Test connection — the bot should reply with a small JSON blob. If the test fails, the bot token is wrong or the chat id was never /start'd.
«My 2FA code is rejected.»
Codes are time-based with a 30-second rotation and a 1-step grace window. If your phone's clock has drifted more than ~30 seconds from real time, codes fail. Re-sync time on the device and retry. If you still can't log in, ask an ADMIN to reset your 2FA from Security Setup.
«I got locked out after failed attempts.»
The lockout window is shown on the login screen. Wait it out, or ask an ADMIN to clear the lockout from Security Setup.
«The AI replies look confused / empty.»
Open AI Setup and click Test on the current provider.
If the test succeeds but chats are weird, try a different model
(swap gemini-flash for claude-sonnet, or
vice versa). If the test fails, the API key has expired or the
provider is down — the cascade will automatically fall
through to the next enabled provider.
«I asked the Planner to recalculate and nothing happened.»
As of 2026-04-19 the rerun action runs the
scheduler inline — the reply should say something like
«Applico. Score 1432 → 1208, OT 40′,
unscheduled 0.» with a green
✓ applied pill. If
you still see an old-style «I set the job to READY, click
Run» reply, you are on a cached PWA shell: hard-reload
(Ctrl+Shift+R) or wait for the
service worker to pick up the new version. If the pill is
⚠ failed, hover it for
the scheduler error.
«My sidebar is missing pages I used to see.»
The sidebar now groups pages into collapsible sections and opens them all collapsed on first load. Click the section header (e.g. 🤖 Agents, Inventory) to expand it. See WMS menu layout for the full mapping.
«Simulations page is empty / SIM badge is missing.»
The Simulations page lists the last N jobs for your tenant. If
you see nothing, you simply haven't produced clones yet —
run an apply_rebalance, apply_level, or
apply_absence from a Planner chat, or just press
Calculate Plan from UFCP. The SIM badge
appears only on Jobs with an active settings override; canonical
plans don't carry it by design.
🔑 Terms & Privacy
The platform is operated under the EU GDPR framework. The full legal texts live at Privacy Policy and Terms of Use. This section is a plain-English summary for day-to-day users.
Consent modal at sign-in
When a new version of the Terms or Privacy Policy is published, at your next
desktop sign-in you see a small modal:
“We've updated our Terms & Privacy”. Review the two
linked documents (they open in a new tab) and click Continue.
Your acceptance is recorded with version, UTC timestamp, and IP address on
the Users row for audit. Badge-scanner logins skip the modal
— operators accept at their next email-based sign-in instead.
What we collect
- Your name, work email, tenant membership, role, and login events.
- Anything your users enter into the app: schedules, tickets, chats, agent decisions, voice transcriptions (when Voice is enabled).
- Security-relevant metadata: IP on login, token-usage counters, reasoning traces for agentic actions.
Where it lives
All operational data is stored in EU data centres (Hetzner, Germany/Finland). LLM API calls for agent reasoning may be routed to US regions when you select US-based providers in Governance settings — this is disclosed at selection time and can be disabled per-tenant.
Your rights
EU/EEA residents have full GDPR rights (access, rectification, erasure, portability, objection, complaint to the Garante). Users in other regions retain equivalent rights under their local regime (UK-GDPR, CCPA, LGPD, revFADP). Access requests are handled via privacy@mdgsuite.com. Tenant ADMINs can also trigger Memory Browser exports directly — see the Memory Browser & GDPR section.
When things change
We bump the version string of the Terms or Privacy Policy on material changes. The consent modal re-appears at the next sign-in so you're never silently bound by a new text.
📖 Glossary
- ADMIN
- A user with full access to the tenant's setup pages and per-user reset actions.
- Agent Chamber
- Read-only timeline of agent activity. See above.
- Agent Decisions
- Structured audit log of every dispatcher action, filterable by persona / action / status / time window. See above.
- Autonomous runtime
- The background loop that lets agents propose actions without a
human chat turn. Gated by
AutonomousEnabledandDailyActionBudgetin Agent Governance. - Autonomy threshold
- The highest impact level the agents may act on without asking. Set in Agent Governance.
- AutonomousShadowMode
- Governance switch (default off). When on, the autonomous runtime keeps proposing but every proposal lands pending regardless of level — admin reviews and approves by hand. Trust-builder before auto-apply is enabled. See Agent Governance.
- autonomous_action
- A memory Kind written every time the autonomous runtime dispatches a decision — one-line narrative companion to the structured row that lands in Agent Decisions. Surfaces in the Agent Chamber timeline so the operator gets a readable story of what the agent did while they were away. Shipped 2026-04-19.
- CCNL
- Italian collective labour contract. Hard-coded limits on OT, rest, and night-shift. The CHRO persona enforces them.
- Concern
- A finding an observer wrote to memory when a threshold was breached. Surfaces in the Chamber and influences the chat's answers.
- Event bus (A#2)
- Postgres
LISTEN/NOTIFYchannel that wakes peer observers sub-second on a high-severity concern. See The Agentic Org for the full protocol. - Impact level
- LOW / MEDIUM / HIGH — how much the action could disrupt operations. Decides whether the action is auto-applied or pending.
- Observer
- A background service that periodically reads memory, checks thresholds, and writes concerns. One per persona.
- OPERATOR
- A warehouse user with badge + PIN access, no setup rights.
- Outcome
- The verdict the OutcomeEvaluator writes on each dispatcher row after comparing the plan score before and after the decision. One of positive, neutral, negative, inconclusive. Shown as a coloured pill in Agent Decisions.
- Plan score
- A numeric summary of how well a computed plan hits the tenant's objectives. Lower = better. The COO observer watches drift over a 14-day window.
- SIM badge
- Small marker shown next to a Job's status when the Job
carries a
SettingsOverrideJson. It means the Job is a what-if simulation, not a canonical plan. Autonomy ignores these. - Simulations page
- WMS sidebar entry (under Agents) listing recent Jobs with inline rename, status, SIM badge, Open, Delete. See What-if simulations.
- Tenant Knowledge
- ADMIN page holding the five admin-curated tables that steer the agent: policies (binding MUST rules), preferences (soft SHOULD tilts), exceptions (date-scoped / recurring deviations), patterns (observed empirical truths read as context, not commands) and user attributes (per-operator skills / certifications / notes). Read on every chat turn; honoured by the WMS scheduler during Calculate Plan. See above.
- TenantException
- A row in Tenant Knowledge that narrows a TargetType
(
user/global/ …) to a recurrence and a date window.userandglobalexceptions steer the WMS scheduler; other target types are prompt-injection only for v1. - TenantPattern
- A row in Tenant Knowledge that captures an observed empirical truth about how the company actually operates — e.g. «PICK saturation runs +20% on days 28-31 of the month». Has a Confidence between 0 and 1; agents read it as context, not as a rule to enforce. The Source field records which observer wrote it. See above.
- UserAttribute
- A row in Tenant Knowledge attaching structured metadata to a
single operator — Kind in
(
skill/certification/consent/language/note), plus a Key and Value. Makes skills and certifications visible to the agent without extending the core User entity. Not enforced by the scheduler. See above. - Memory Browser
- ADMIN page under 🤖 Agents to inspect the per-company agent memory corpus with filters and per-row delete, and to run GDPR export / purge for a specific user. See above.
- FeedbackScore
- A number in [-1, +1] attached to every memory row. Nudged by admin thumbs-up / thumbs-down on Agent Decisions and by the outcome sweep when it writes a verdict. Used as a fourth term (weight 0.1) when the agent retrieves relevant context on future turns.
- TOTP
- Time-based one-time password, RFC-6238 — the 6-digit code from your authenticator app.