diff --git a/docs/render-user-guide.py b/docs/render-user-guide.py
new file mode 100644
index 0000000..0d2d908
--- /dev/null
+++ b/docs/render-user-guide.py
@@ -0,0 +1,191 @@
+#!/usr/bin/env python3
+"""Render the user guide to a print-ready HTML page.
+
+From web/, to regenerate docs/user-guide.pdf after editing docs/user-guide.md:
+
+ python3 docs/render-user-guide.py docs/user-guide.md /tmp/ug.html \
+ "HumAIn Flow" "User Guide" "Version of 16 September 2026"
+ npx playwright pdf file:///tmp/ug.html docs/user-guide.pdf
+
+Deliberately not a general Markdown implementation: it handles the constructs the
+guide actually uses, so the output is predictable and the print styling can be
+tuned for it (page breaks per chapter, a real table of contents, no orphan
+headings). Pandoc is not installed here and pulling one in for one document costs
+more than it saves.
+"""
+import html
+import re
+import sys
+
+INLINE = [
+ (re.compile(r"`([^`]+)`"), r"\1"),
+ (re.compile(r"\*\*([^*]+)\*\*"), r"\1"),
+ (re.compile(r"(?\1"),
+ (re.compile(r"\[([^\]]+)\]\(([^)]+)\)"), r'\1'),
+]
+
+
+def inline(text):
+ out = html.escape(text)
+ for pattern, replacement in INLINE:
+ out = pattern.sub(replacement, out)
+ return out
+
+
+def convert(markdown):
+ lines = markdown.split("\n")
+ out, toc = [], []
+ i = 0
+ list_stack = []
+
+ def close_lists(to_depth=0):
+ while len(list_stack) > to_depth:
+ out.append(f"{list_stack.pop()}>")
+
+ while i < len(lines):
+ line = lines[i]
+ stripped = line.strip()
+
+ if not stripped:
+ close_lists()
+ i += 1
+ continue
+
+ if stripped.startswith("```"):
+ close_lists()
+ i += 1
+ block = []
+ while i < len(lines) and not lines[i].strip().startswith("```"):
+ block.append(html.escape(lines[i]))
+ i += 1
+ i += 1
+ out.append("
" + "\n".join(block) + "
")
+ continue
+
+ heading = re.match(r"^(#{1,4})\s+(.*)$", stripped)
+ if heading:
+ close_lists()
+ level = len(heading.group(1))
+ text = heading.group(2)
+ anchor = re.sub(r"[^a-z0-9]+", "-", text.lower()).strip("-")
+ # Chapters are h2 and sections h3, so the contents list is those two levels.
+ if 2 <= level <= 3:
+ toc.append((level - 1, text, anchor))
+ out.append(f'{inline(text)}')
+ i += 1
+ continue
+
+ if stripped.startswith("|") and i + 1 < len(lines) and re.match(r"^\|[\s:|-]+\|$", lines[i + 1].strip()):
+ close_lists()
+ header = [c.strip() for c in stripped.strip("|").split("|")]
+ i += 2
+ rows = []
+ while i < len(lines) and lines[i].strip().startswith("|"):
+ rows.append([c.strip() for c in lines[i].strip().strip("|").split("|")])
+ i += 1
+ head = "".join(f"{inline(c)} | " for c in header)
+ body = "".join("" + "".join(f"| {inline(c)} | " for c in r) + "
" for r in rows)
+ out.append(f"")
+ continue
+
+ if stripped in ("---", "***"):
+ close_lists()
+ out.append('
')
+ i += 1
+ continue
+
+ bullet = re.match(r"^(\s*)[-*]\s+(.*)$", line)
+ number = re.match(r"^(\s*)\d+\.\s+(.*)$", line)
+ if bullet or number:
+ match = bullet or number
+ tag = "ul" if bullet else "ol"
+ depth = len(match.group(1)) // 2 + 1
+ while len(list_stack) > depth:
+ out.append(f"{list_stack.pop()}>")
+ while len(list_stack) < depth:
+ out.append(f"<{tag}>")
+ list_stack.append(tag)
+ out.append(f"{inline(match.group(2))}")
+ i += 1
+ continue
+
+ if stripped.startswith("> "):
+ close_lists()
+ out.append(f'{inline(stripped[2:])}
')
+ i += 1
+ continue
+
+ close_lists()
+ paragraph = [stripped]
+ i += 1
+ while i < len(lines) and lines[i].strip() and not re.match(r"^\s*([-*]|\d+\.|#|\||>|```)", lines[i]):
+ paragraph.append(lines[i].strip())
+ i += 1
+ out.append(f"{inline(' '.join(paragraph))}
")
+
+ close_lists()
+ return "\n".join(out), toc
+
+
+CSS = """
+@page { size: A4; margin: 18mm 16mm 20mm; }
+* { box-sizing: border-box; }
+body { font: 10.5pt/1.55 -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
+ color: #1e293b; margin: 0; }
+h1 { font-size: 21pt; color: #0f172a; margin: 0 0 6pt; line-height: 1.25; }
+h2 { font-size: 19pt; color: #0f172a; page-break-before: always; page-break-after: avoid;
+ border-top: 2px solid #2563eb; padding-top: 6pt; margin: 0 0 10pt; line-height: 1.25; }
+h3 { font-size: 12.5pt; color: #1d4ed8; margin: 16pt 0 5pt; page-break-after: avoid; }
+h4 { font-size: 10.8pt; color: #0f172a; margin: 12pt 0 3pt; page-break-after: avoid; }
+p { margin: 0 0 7pt; orphans: 2; widows: 2; }
+ul, ol { margin: 0 0 8pt; padding-left: 18pt; }
+li { margin-bottom: 2.5pt; }
+code { font-family: "SF Mono", Menlo, Consolas, monospace; font-size: 9pt;
+ background: #f1f5f9; border: 1px solid #e2e8f0; border-radius: 3px; padding: 0 3px; }
+pre { background: #f8fafc; border: 1px solid #e2e8f0; border-left: 3px solid #94a3b8;
+ border-radius: 4px; padding: 8pt 10pt; overflow-x: auto; page-break-inside: avoid; }
+pre code { background: none; border: none; padding: 0; font-size: 8.5pt; line-height: 1.45; }
+table { width: 100%; border-collapse: collapse; margin: 0 0 10pt; font-size: 9.5pt;
+ page-break-inside: avoid; }
+th { background: #eff6ff; color: #1e3a8a; text-align: left; font-weight: 600; }
+th, td { border: 1px solid #cbd5e1; padding: 4pt 6pt; vertical-align: top; }
+.note { border-left: 3px solid #f59e0b; background: #fffbeb; padding: 6pt 9pt;
+ margin: 0 0 8pt; page-break-inside: avoid; }
+hr.page-break { border: 0; page-break-after: always; }
+.cover { height: 247mm; display: flex; flex-direction: column; justify-content: center; }
+.cover-title { font-size: 34pt; border: 0; padding: 0; }
+.cover-sub { font-size: 13pt; color: #475569; margin-top: 2pt; }
+.cover-meta { margin-top: 26pt; font-size: 10pt; color: #64748b; }
+.toc { page-break-after: always; }
+.toc-title { font-size: 19pt; color: #0f172a; border-top: 2px solid #2563eb;
+ padding-top: 6pt; margin: 0 0 10pt; }
+.toc ol { list-style: none; padding-left: 0; counter-reset: chapter; }
+.toc li.l1 { counter-increment: chapter; font-weight: 600; margin-top: 6pt; }
+.toc li.l1::before { content: counter(chapter) ". "; color: #2563eb; }
+.toc li.l2 { padding-left: 16pt; font-weight: 400; font-size: 9.5pt; color: #475569; }
+.toc a { color: inherit; text-decoration: none; }
+a { color: #1d4ed8; }
+"""
+
+
+def main(md_path, html_path, title, subtitle, meta):
+ # The cover carries the document title, so the file's own leading H1 would only repeat it.
+ source = re.sub(r"\A\s*#\s+.*\n", "", open(md_path).read())
+ body, toc = convert(source)
+ items = "".join(
+ f'{html.escape(text)}'
+ for level, text, anchor in toc
+ )
+ page = f"""{html.escape(title)}
+
+{html.escape(title)}
+
{html.escape(subtitle)}
+
{html.escape(meta)}
+
+{body}"""
+ open(html_path, "w").write(page)
+ print(f"html written: {html_path} ({len(page)} bytes, {len(toc)} toc entries)")
+
+
+if __name__ == "__main__":
+ main(*sys.argv[1:])
diff --git a/docs/user-guide.md b/docs/user-guide.md
index de7f6fe..87beaf0 100644
--- a/docs/user-guide.md
+++ b/docs/user-guide.md
@@ -1,205 +1,279 @@
-# Guida Utente HumAIn Flow
+# HumAIn Flow — User Guide
-## Introduzione
+## Introduction
-HumAIn Flow e un'applicazione web per progettare, validare ed eseguire workflow composti da blocchi logici, connessioni dati e dipendenze di esecuzione.
+HumAIn Flow is a web application for designing, validating and running workflows made of logical blocks, data connections and execution dependencies.
-L'app supporta due modi principali di lavoro:
+The application supports two main ways of working:
-- costruzione manuale del flow nell'editor visuale
-- generazione o modifica del flow tramite assistente
+- building the flow by hand in the visual editor
+- generating or reshaping the flow through the assistant
-Una volta creato il flow, puoi salvarlo, controllarne gli errori di validazione ed eseguirlo nel tab `Tasks`.
+Once a flow exists you can save it, review its validation errors, run it from the `Tasks` tab, and — for flows that involve language models — study how a deliberately introduced bias, or a mitigation meant to counter one, changes what the flow produces.
-## Accesso all'applicazione
+This guide covers the standard use of the editor, of executions, and of the bias tooling. It describes what you see on screen and what each action does, not how the platform is implemented internally.
-Dopo il login entri nella schermata principale. La barra superiore contiene:
+## Signing in and finding your way around
-- `Editor`: area in cui si progettano i flow
-- `Tasks`: area in cui si consultano ed eseguono le istanze dei flow
-- menu utente: operazioni personali come cambio password
+After signing in you land on the main screen. The top bar contains:
-Se il tuo account ha accesso ad aree aggiuntive, queste compaiono nel menu utente. Questa guida si concentra sull'uso standard dell'editor e delle esecuzioni.
+- `Editor`: where flows are designed
+- `Tasks`: where flow instances are inspected and run
+- the user menu, under your username: `Change password`, and `Admin users` for accounts that have it
-## Struttura generale dell'interfaccia
+Areas you are not entitled to use are not shown at all.
-### Editor
+Notifications — confirmations, warnings and errors — appear briefly at the bottom of the screen, colour-coded, and can be dismissed by clicking them.
-Nel tab `Editor` trovi tre aree principali:
+### The Editor layout
-- pannello sinistro con elenco di `Blocks` e `Containers`
-- canvas centrale del workflow
-- pannello destro con `Assistant` ed eventuali `Errors`
+The `Editor` tab has three main areas:
-### Tasks
+- a left panel listing the available `Blocks` and `Containers`
+- the central workflow canvas
+- a right panel holding the `Assistant` and, when relevant, the `Errors` panel
-Nel tab `Tasks` trovi:
+### The Tasks layout
-- lista delle esecuzioni nella colonna sinistra
-- dettaglio della singola esecuzione nella parte centrale
+The `Tasks` tab has:
-## Concetti base
+- the list of executions in the left column
+- the detail of the selected execution in the central area
+
+## Core concepts
### Flow
-Un flow e la definizione del workflow. Contiene:
+A flow is the definition of a workflow. It contains:
-- blocchi
-- container
-- connessioni dati
-- dipendenze di esecuzione
+- blocks
+- containers
+- data connections
+- execution dependencies
+
+A flow also carries a status. The one that matters day to day is whether it is `EXECUTABLE` — a flow that still has validation errors stays a `DRAFT` and cannot be run.
### Block
-Un block rappresenta un singolo step del workflow. Alcuni esempi tipici:
+A block represents a single step of the workflow. Typical examples:
-- blocchi LLM
-- blocchi di input o output
-- blocchi di interazione umana
-- blocchi condizionali
+- LLM blocks, which send a prompt to a language model
+- input and output blocks
+- human interaction blocks
+- conditional blocks, which route the flow down one branch or another
+- agent blocks, which let a model call external tools
### Container
-Un container contiene un `subFlow`, cioe un flow annidato. Serve per raggruppare una parte del workflow in un'unita riutilizzabile o piu leggibile.
+A container holds a `subFlow` — a nested flow. Use one to group part of the workflow into a reusable, more readable unit, or to repeat a part of the workflow over a list of items.
-Un container senza `subFlow` e considerato incompleto.
+A container without a `subFlow` is considered incomplete.
-### Connessioni dati
+### Data connections
-Le connessioni standard collegano:
+Standard connections link:
-- un output sorgente
-- a un input destinazione
+- a source output
+- to a destination input
-Servono per trasferire valori tra nodi.
+They exist to carry values between nodes.
-### Dipendenze di esecuzione
+### Execution dependencies
-Le dipendenze non trasferiscono dati. Impongono solo un ordine di esecuzione.
+Dependencies carry no data. They only impose an order of execution.
-Le label visibili sui nodi sono:
+The labels visible on the nodes are:
-- `Depends on`: questo nodo deve aspettare un altro nodo
-- `Prerequisite of`: questo nodo sblocca l'esecuzione di un altro
+- `Depends on`: this node must wait for another node
+- `Prerequisite of`: this node unblocks the execution of another
-Usa una dependency quando vuoi garantire l'ordine corretto tra due step, ma senza passare un valore in input.
+Use a dependency when you want to guarantee the right order between two steps without passing a value as input.
-## Creare un flow manualmente
+### Global inputs
-### 1. Aprire l'editor
+Some values are not produced by a node but supplied when the execution starts — a document to analyse, a topic, a threshold. These are the flow's global inputs. They are declared once on the flow and can then be referenced by any block that needs them, instead of being wired node by node.
-Vai nel tab `Editor`. Se non hai un flow aperto puoi:
+A block that references a global input which the flow does not declare is a validation error (`GLOBAL_INPUT_NOT_DECLARED`). Global inputs belong to the top-level flow: a subflow inside a container cannot declare its own.
-- crearne uno nuovo
-- usare l'assistente per generarlo
+## Projects
-### 2. Aggiungere blocchi o container
+A project groups related flows under one name. It gives you three things: a folder in the flows list, a set of shared values every flow in it can read, and an order in which those flows run when you run the project as a whole.
-Dal pannello sinistro:
+Projects are private to whoever created them. A flow can be published and shared; the project structure around it cannot.
-- cerca il tipo di blocco o container
-- trascinalo nel canvas
+### Creating a project
-Se il catalogo non e ancora pronto, vedrai un loader al posto del messaggio vuoto.
+In the editor sidebar, the `New` button above the flows list offers `New project` alongside `Empty flow`, `Create with AI` and `From JSON`. A project needs a name; the description is optional. Both can be changed later from the same dialog.
-### 3. Spostare e organizzare i nodi
+### Organising flows into projects
-Puoi:
+Once projects exist, the flows list is grouped by project, with a `No project` group for everything else. Each group header shows the name, the description and how many flows it holds, and `Filters & sorting` gains a project filter.
-- trascinare i nodi nel canvas
-- selezionare e spostare nodi
-- clonare un nodo con l'icona di clone
-- eliminare un nodo con l'icona di delete
+There are two ways to put a flow in a project:
-Quando elimini un nodo, vengono rimosse anche:
+- from the project group's `⋮` menu, `New flow in this project`, which creates an empty flow already assigned;
+- from a flow's own `⋮` menu, `Move to project`, which also offers `Remove from project`.
-- le connessioni dati collegate
-- le dependency edges collegate
+When a project group holds more than one flow, arrows appear beside each entry: `Run earlier in this project` and `Run later in this project`. That order is what a project run follows.
-### 4. Collegare i nodi
+### Shared context
-Per passare dati:
+A project's `⋮` menu offers `Shared context…` — values that every flow in the project can read at run time. Each entry has a name, a type (`TEXT`, `BOOLEAN`, `JSON` or `CSV`) and a value, and the dialog shows the placeholder to use it with, which you can copy:
-- collega un output a un input
+```
+${{project.myVariable}}
+```
-Per imporre solo ordine di esecuzione:
+They work exactly like the flow's own global inputs, one level up: define a value once for the project, reference it from any flow inside it.
-- collega `Prerequisite of` del nodo sorgente
-- a `Depends on` del nodo destinazione
+Names must be unique, must be usable in a placeholder (start with a letter, then letters, digits, `_`, `.` or `-`), and must not start with `project.`, `global.`, `context.` or `vars.`. Saving replaces the whole context, so an entry you remove from the dialog is removed for good.
-Le connessioni di dependency sono visualizzate in modo diverso dalle connessioni dati, con linea piu fine e tratteggiata.
+### Running a project
-### 5. Modificare il nome dei nodi
+The play button on the project group header runs every executable flow in the project. It is available only when at least one of them is executable; the ones that are not are skipped, and the notification tells you how many.
-Per i blocchi e i container:
+The flows run **one at a time, in the project's order**, each starting only after the previous one has succeeded. The application takes you to the `Tasks` tab with the first execution selected, and executions started this way carry a chip with the project name.
-- clicca l'icona matita accanto al nome
-- modifica il nome
-- conferma con `Save`
+Creating the run does not fill anything in for you. Each execution still needs its own inputs and credentials, exactly as it would on its own. A flow that fails stops the run, and a flow waiting for inputs or a credential blocks it — supply what is missing and run the project again to carry on from where it stopped.
-### 6. Configurare i parametri
+### Deleting a project
-Cliccando su un nodo puoi vedere i suoi parametri.
+`Delete project` deletes the project **and every flow in it**, finalized flows included — the ordinary protection on a finalized flow does not apply here. The confirmation dialog lists the flows that will go, marks the finalized ones, and asks you to type the project name before the button becomes available.
-Per i campi editabili:
+Past executions are kept: each one holds its own snapshot of the flow it ran, so deleting a project does not erase its history.
-- usa il pulsante matita sui parametri
-- modifica il valore nel dialog
-- salva
+## Building a flow by hand
-Per i testi lunghi:
+### 1. Open the editor
-- se il campo e lungo viene troncato
-- puoi aprirlo per intero con l'icona occhio
+Go to the `Editor` tab. The sidebar lists the flows you can open, with a search box and a `Filters & sorting` disclosure offering visibility toggles, an ordering control and, once you have projects, a project filter. Each row shows whether the flow is public or private, who wrote it and when it was created.
-Nel caso dei subflow in sola lettura, i campi lunghi mantengono comunque l'icona occhio per una lettura completa in readonly.
+The `New` button offers four ways to start:
-## Lavorare con i container
+- `Empty flow`
+- `Create with AI`, which hands the job to the assistant
+- `From JSON`, which imports a flow file
+- `New project`
-### Inserire un subflow
+Each flow's `⋮` menu offers `Clone flow`, `Export flow` — which downloads it as JSON — `Move to project` and `Delete flow`.
-Un container puo ricevere un subflow in piu modi:
+### 2. Add blocks or containers
-- importando un flow
-- trascinando dentro una selezione di nodi
+From the left panel:
-Quando un subflow viene sostituito:
+- look for the block or container type you need
+- drag it onto the canvas
-- la configurazione strutturale del container viene ricostruita
-- i vecchi parametri del subflow precedente non vengono mantenuti
+If the catalogue is not ready yet, a loader is shown in place of the empty-list message.
-### Importare un flow nel container
+### 3. Move and organise the nodes
-Se il tipo di container lo supporta:
+You can:
-- clicca `Import flow`
-- scegli il flow disponibile
-- conferma
+- drag nodes around the canvas
+- select and move nodes
+- clone a node with the clone icon
+- delete a node with the delete icon
-### Visualizzare il subflow
+Deleting a node also removes:
-Se il container contiene un subflow:
+- the data connections attached to it
+- the dependency edges attached to it
-- compare il pulsante `View Flow`
-- si apre una finestra di anteprima in sola lettura
+### 4. Connect the nodes
-Da questa vista puoi:
+To pass data:
-- esplorare il subflow
-- aprire i parametri lunghi in readonly con l'icona occhio
+- connect an output to an input
-## Parametri obbligatori e validazione locale
+To impose order only:
-Se un nodo ha parametri mancanti, compare un indicatore di warning.
+- connect `Prerequisite of` on the source node
+- to `Depends on` on the destination node
-Per i blocchi il warning mostra i campi obbligatori mancanti.
+Dependency connections are drawn differently from data connections: a thinner, dashed line.
-Per i container:
+### 5. Rename nodes
-- se manca il `subFlow`, viene mostrato solo `Subflow`
-- non vengono mostrati come mancanti i campi interni del `FlowData` come `Blocks`, `Connections` o `Dependencies`
+For both blocks and containers:
-Inoltre i campi tecnici di tipo non vengono mostrati come parametri utente. Questo include campi come:
+- click the pencil icon next to the name
+- edit the name
+- confirm with `Save`
+
+### 6. Configure the parameters
+
+Clicking a node shows its parameters.
+
+For editable fields:
+
+- use the pencil button on the parameter
+- edit the value in the dialog
+- save
+
+For long texts:
+
+- a long field is truncated on the node
+- open it in full with the eye icon
+
+In read-only subflow views the long fields keep the eye icon, so you can still read them completely.
+
+### Placeholders in prompts and parameters
+
+A parameter can reference a value produced elsewhere — a connected input or a global input — through a placeholder. On the node, placeholders are rendered as distinct segments rather than plain text, so you can tell at a glance which part of a prompt is literal and which part will be substituted at run time.
+
+During an execution, the same preview shows the resolved value once it is available; until then the original placeholder stays visible.
+
+## Working with containers
+
+### Putting a subflow inside a container
+
+A container can receive a subflow in more than one way:
+
+- by importing an existing flow
+- by dragging a selection of nodes into it
+
+When a subflow is replaced:
+
+- the structural configuration of the container is rebuilt
+- parameters belonging to the previous subflow are not preserved
+
+### Importing a flow into the container
+
+If the container type supports it:
+
+- click `Import flow`
+- choose one of the available flows
+- confirm
+
+### Viewing the subflow
+
+If the container holds a subflow:
+
+- a `View Flow` button appears
+- it opens a read-only preview window
+
+From that view you can:
+
+- explore the subflow
+- open long parameters in read-only mode with the eye icon
+
+### Validation errors inside a subflow
+
+A subflow is validated together with the flow that contains it. An error produced inside a subflow is reported on the container that holds it — that is the node you can actually see and act on — and the inner node responsible for it is highlighted when you open the subflow view.
+
+## Required parameters and local validation
+
+If a node has missing parameters, a warning indicator appears on it.
+
+For blocks, the warning lists the missing required fields.
+
+For containers:
+
+- if the `subFlow` is missing, only `Subflow` is listed
+- the inner fields of the `FlowData`, such as `Blocks`, `Connections` or `Dependencies`, are not reported as missing
+
+Technical type fields are never shown as user parameters. These include:
- `type`
- `typeName`
@@ -207,325 +281,752 @@ Inoltre i campi tecnici di tipo non vengono mostrati come parametri utente. Ques
- `configurationType`
- `configurationClass`
-## Uso dell'assistente
+Fields that belong to a branch of the configuration you have not selected are hidden as well: a parameter that only applies when another field has a particular value appears when that value is chosen, and does not count as missing before then.
-L'assistente si trova nel pannello destro dell'editor.
+## Using the assistant
-### Modalita create e refine
+The assistant lives in the right panel of the editor.
-L'assistente cambia comportamento in base allo stato del flow aperto:
+### Create and refine modes
-- se non c'e nessun flow aperto, oppure il flow aperto e vuoto, l'assistente lavora in modalita `Create`
-- se il flow aperto contiene gia nodi o connessioni, l'assistente lavora in modalita `Refine`
+The assistant changes behaviour depending on the state of the open flow:
-Questo significa che un flow vuoto non blocca la creazione assistita.
+- with no flow open, or with an open flow that is still empty, it works in `Create` mode
+- with a flow that already contains nodes or connections, it works in `Refine` mode
-### Cosa puoi chiedere all'assistente
+An empty flow therefore does not block assisted creation.
-Esempi tipici:
+### What you can ask
-- creare un flow da zero
-- modificare un flow esistente
-- spiegare un flow
-- aiutare a correggere problemi di validazione
+Typical requests:
-### Risultato dell'assistente
+- create a flow from scratch
+- modify an existing flow
+- explain what a flow does
+- help fix validation problems
-Quando l'assistente restituisce un draft:
+### What you get back
-- il flow viene caricato nell'editor
-- puoi continuare a modificarlo manualmente
-- puoi salvarlo come un flow normale
+When the assistant returns a draft:
-## Salvataggio del flow
+- the flow is loaded into the editor
+- you can keep editing it by hand
+- you can save it like any other flow
-Quando lavori nell'editor, il flow puo essere modificato ma non ancora salvato.
+The assistant proposes; nothing is persisted until you save.
-### Salvataggio
+## Saving a flow
-Usa il pulsante `Save` nella toolbar del flow.
+While you work in the editor the flow can be modified but not yet saved.
-Il save:
+### Saving
-- aggiorna il flow sul backend
-- ricalcola la validazione quando necessario
+Use the `Save` button in the flow toolbar.
-### Rinominare il flow
+Saving:
-Il titolo del flow usa azioni esplicite:
+- updates the flow on the backend
+- recomputes validation when needed
+
+### Renaming the flow
+
+The flow title uses explicit actions:
- `Save`
- `Cancel`
-Non viene piu salvato automaticamente al blur del campo.
+It is not saved automatically when the field loses focus.
-## Pannello errori di validazione
+## The validation errors panel
-Nel pannello destro c'e un'icona dedicata agli errori del flow.
+The right rail has a dedicated icon for flow errors.
-### Quando compare
+### When it appears
-L'icona:
+The icon:
-- e sempre visibile nella rail destra
-- e disabilitata se non ci sono errori
-- si attiva quando il flow ha errori di validazione
+- is always visible in the right rail
+- is disabled when there are no errors
+- becomes active when the flow has validation errors
-### Come vengono caricati gli errori
+### How errors are loaded
-Gli errori vengono richiesti dal backend:
+Errors are requested from the backend:
-- dopo il save, se il flow non e `EXECUTABLE`
-- anche all'apertura di un flow gia `DRAFT`
+- after saving, when the flow is not `EXECUTABLE`
+- when opening a flow that is already a `DRAFT`
-### Cosa mostra il pannello errori
+### What the panel shows
-Per ogni errore vengono mostrati:
+For each error you see:
-- codice
-- messaggio leggibile
+- a code
+- a readable message
-I metadati troppo rumorosi come `entity`, `field` e `id` non vengono mostrati nella card.
+Noisy metadata such as `entity`, `field` and `id` is not shown on the card.
-### Evidenziazione dei nodi
+### Node highlighting
-Se l'errore include nodi correlati:
+When an error names related nodes, those nodes are highlighted on the canvas. If the error comes from inside a container's subflow, the container is highlighted while you are looking at the main flow, and the inner node is highlighted once you open the subflow.
-- questi nodi vengono evidenziati nel canvas
+### Stale validation
-### Validazione stale
+If you make a structural change without saving, the errors panel shows a notice:
-Se fai una modifica strutturale senza salvare ancora, il pannello errori mostra un avviso:
+`Validation will be recomputed after save.`
-- `Validation will be recomputed after save.`
+Purely graphical moves of the nodes do not count as structural changes.
-Gli spostamenti puramente grafici dei nodi non contano come modifica strutturale.
+## Published and Finalized
-## Published e Finalized
-
-Se sei il proprietario del flow puoi vedere due controlli nella toolbar:
+If you own the flow, two controls appear in the toolbar:
- `Published`
- `Finalized`
### Published
-Controlla la visibilita del flow.
-
-Puoi:
-
-- pubblicare
-- depubblicare
+Controls the visibility of the flow. You can publish and unpublish freely.
### Finalized
-Segna il flow come definitivo e non piu modificabile.
+Marks the flow as definitive and no longer modifiable.
-Una volta finalizzato:
+Once finalized:
-- il contenuto del flow diventa read-only
-- il flow non puo essere un-finalized
-- il flow non puo essere cancellato
-- il publish/depublish resta comunque disponibile
+- the content of the flow becomes read-only
+- the flow cannot be un-finalized
+- the flow cannot be deleted
+- publishing and unpublishing remain available
-## Eseguire un flow
+Finalize a flow when you intend to use it as a stable baseline — for instance as the reference version for a bias experiment.
-Quando un flow e valido ed eseguibile, puoi usare `Execute`.
+## Running a flow
-### Cosa succede al click su Execute
+When a flow is valid and executable, `Execute` becomes available.
-L'app:
+### What happens when you click Execute
-- apre subito il tab `Tasks`
-- crea l'esecuzione in background
-- mostra un loader finche la nuova execution non e pronta
+The application:
-Questo evita il ritardo percepito prima del cambio tab.
+- switches to the `Tasks` tab immediately
+- creates the execution in the background
+- shows a loader until the new execution is ready
-## Lavorare nel tab Tasks
+This avoids the perceived delay before the tab changes.
-Nel tab `Tasks` hai una lista di esecuzioni sulla sinistra e il dettaglio a destra.
+## Working in the Tasks tab
-### Lista delle esecuzioni
+The `Tasks` tab shows a list of executions on the left and the detail on the right.
-Ogni elemento mostra:
+### The execution list
-- nome
-- stato
-- data/ora
-- eventuale badge `Simulated`
+Each entry shows:
-### Dettaglio di una esecuzione
+- the name
+- the status
+- the date and time
+- a `Simulated` badge when the run was simulated
+- the run number, when the execution is part of a group of repeated runs
-Nel dettaglio puoi:
+### The detail of an execution
-- vedere il grafo in sola lettura
-- controllare input richiesti
-- leggere output e log
-- eseguire azioni come start, simulate, cancel o resume se disponibili
+In the detail you can:
-## Input delle esecuzioni
+- see the graph in read-only mode
+- fill in the required inputs
+- read outputs and logs
+- take the actions that are available for the current state: start, simulate, cancel, resume, re-run
-Se un execution step richiede input manuali, li trovi nel pannello dedicato.
+## Execution inputs
-### Modalita di salvataggio degli input
+When an execution step needs manual input you provide it in the dedicated panel.
-Gli input non vengono piu inviati automaticamente on blur.
+### How inputs are saved
-Ora il comportamento e:
+Inputs are not sent automatically when a field loses focus. The behaviour is:
-- modifichi il valore
-- il draft resta locale
-- premi `Save` per inviarlo
+- you edit the value
+- the draft stays local
+- you press `Save` to send it
-Questo vale anche per campi multipli.
+This applies to multi-value fields as well.
-## Blocchi Human Interaction
+### Text and list inputs
-I blocchi di interazione umana possono richiedere conferma o inserimento manuale.
+A text input is edited in place; long values can be opened in a larger editor. A list input lets you add and remove entries, each one edited on its own row.
-### Dialog di interazione
+### File inputs
-Quando il nodo lo richiede, si apre un dialog dedicato.
+An input that expects a file is filled through a drop zone: drag a file onto it, or click it to pick one.
-Nel caso non-chat puoi:
+Once a file is chosen it is uploaded straight away and the zone reports its state:
-- confermare l'input corrente
-- modificare la risposta
-- inviare con `Send Output`
+- while the transfer is running, a progress indicator and the file name
+- when it is finished, the file name and a `Replace` action
+- if the transfer fails, the error and the possibility to try again
-Nel caso chat puoi:
+A note under the zone states the behaviour explicitly: the file is stored as soon as it is chosen, not when you press `Save`. To change your mind, use `Replace` and pick another file.
-- continuare la conversazione
-- inviare una risposta finale
+Prompts and parameters that reference an uploaded file show its file name, not the internal storage path.
-### Invio reale al backend
+### Files on global inputs
-I pulsanti di invio effettuano una chiamata reale al backend. In particolare:
+A file can also be supplied as a global input of the flow, so that several blocks use the same document without uploading it more than once. A block input that accepts a file can either take a file of its own or point at a global input; when it points at a global input, the file is chosen once with the rest of the execution inputs.
+
+### Finding your way around the panel
+
+The panel keeps a running count of how many inputs are provided out of how many are required, and separates `Global inputs` from `Node inputs`, each group badged with how many of its values are still missing, or ticked when none are.
+
+At the bottom, a bar states whether you have unsaved changes and how many. It is the only thing that sends them.
+
+A few conveniences are worth knowing:
+
+- a list input can be filled in one go with `Import from JSON array`;
+- a long value can be opened in a larger editing box;
+- `Copy from another run` fills the panel from a previous execution. The values arrive as unsaved changes, so you can review them before saving. Files and credentials are not copied — an uploaded file belongs to the run it was uploaded to, and credentials never leave the server.
+
+## Credentials and authorizations
+
+Some blocks need a secret before they can run: an API key for a language model provider, an authorization header for an HTTP call. The application treats these two cases differently on purpose.
+
+| | Provider credential | Runtime authorization |
+|---|---|---|
+| Typical case | an LLM provider's API key | a header value for an HTTP call |
+| What you supply | a **saved credential**, chosen from a list | the value itself, typed into a masked field |
+| Where it is kept | encrypted in the vault, on the server | with the execution |
+
+A provider key is never pasted as a plain value: it always goes through the vault.
+
+### When an execution asks for a credential
+
+An execution that needs one says so before it will start. A banner above the graph reads:
+
+> This execution needs a Vault credential for `` before it can start.
+
+and the start button is disabled, with a tooltip naming the missing provider. `Choose credential` takes you to the requirement in the `Inputs` tab.
+
+Each outstanding requirement appears as its own card, headed `Vault credential for `, listing the steps that need it, with a dropdown of the credentials you already have for that provider and an `Add credential` button for when you have none or want a new one.
+
+### Adding a credential
+
+`Add credential` asks for:
+
+- the **provider**, which is fixed to the one being asked for;
+- a **label**, so you can recognise it later;
+- an optional **description**;
+- the **API key** itself.
+
+The value is encrypted and stored, and is never shown again — not in this dialog, not anywhere else. Only your own active credentials for that provider are offered.
+
+Once a requirement is satisfied, the amber card becomes a green row naming the credential in force, with `Change` to pick a different one. A credential that cannot be used — unknown, disabled, or belonging to a different provider — is refused at the moment you choose it rather than in the middle of the run, and the reason is shown under the picker.
+
+### Runtime authorizations
+
+Requirements that are not provider credentials appear in the `Provider Authorizations` section of the inputs panel. Each card names the provider and the field, describes what is wanted, lists the steps that need it, and offers a masked input with `Show` / `Hide` and its own `Save`.
+
+### Credentials and reruns
+
+A rerun carries over the authorizations that were already provided, so it usually starts ready to go. Copying inputs from another run does **not** carry them over.
+
+## Human interaction blocks
+
+Human interaction blocks can ask for a confirmation or for a manual answer.
+
+### The interaction dialog
+
+When a node requires it, a dedicated dialog opens.
+
+In the non-chat case you can:
+
+- confirm the current input
+- edit the answer
+- send it with `Send Output`
+
+In the chat case you can:
+
+- carry on the conversation
+- send a final answer
+
+### These buttons really do send
+
+The sending buttons perform a real call to the backend. Specifically:
- `Send Output`
- `Confirm Input`
-- invio messaggi chat
-- invio risposta finale
+- sending a chat message
+- sending the final answer
-passano attraverso l'endpoint di interaction dell'esecuzione.
+all go through the interaction endpoint of the execution.
-## Visualizzazione dei task node
+## Reading an execution
-Nel viewer di esecuzione:
+The detail of an execution has a header, the graph, and a side panel with five tabs: `Inputs`, `Intermediate`, `Logs`, `Output` and `Bias impact`.
-- i container mostrano il pulsante `View Subflow`
-- le dependency ports vengono mostrate solo se effettivamente connesse
-- i testi lunghi possono essere aperti in readonly
+### The header
-Se un prompt o un parametro contiene placeholder:
+The header carries the run's name, a fullscreen toggle, and any badges that apply — `Simulated`, `Subflow` for one iteration of a container's subflow, and the bias variant badges described later. A `Details` disclosure shows the execution identifier and, for a subflow run, its parent, container, iteration and role. Below it, five tiles: status, start, end, duration and steps.
-- quando il valore runtime e disponibile, il preview puo mostrarlo risolto
-- se il valore non e ancora pronto, il placeholder originale resta visibile
+Two banners appear when they apply:
-## Output delle esecuzioni
+- *Execution cancelled. This execution is closed. Start a new execution to continue.*
+- *Execution suspended after service restart. Resume the execution to continue.* — paired with a `Resume execution` button.
-Gli output sono raggruppati per nodo.
+### Outcomes
-### Struttura della vista output
+Above the graph, a collapsible `Outcome(s)` section lists what each End node concluded: the outcome code, its label, and the step it came through, with its payload rendered as text or as a JSON tree. An End node that received no value shows only its label, and says so.
-Per ogni nodo trovi:
+### Task nodes
-- titolo del nodo
-- elenco dei singoli output
+In the execution viewer:
-Se un output supera la lunghezza prevista:
+- containers show a `View Subflow` button
+- dependency ports are shown only when they are actually connected
+- long texts can be opened in read-only mode
-- viene troncato
-- puoi aprirlo interamente con l'icona occhio
+### Outputs
-### Output array
+Outputs are grouped by node. For each node you see the node title and the list of its individual outputs.
-Se la response e un array:
+If an output exceeds the expected length it is truncated, and the eye icon opens it in full.
-- non viene mostrata come blob unico
-- viene espansa in entry separate, come `Item 1`, `Item 2`, eccetera
+When a response is an array it is not shown as a single blob: it is expanded into separate entries, `Item 1`, `Item 2`, and so on.
-## Logs delle esecuzioni
+### Logs
-Nel tab dei log:
+In the logs tab:
-- il contenuto scorre automaticamente in basso durante il refresh
-- viene mostrato il testo leggibile
-- il blocco JSON raw dei dettagli non viene piu visualizzato
+- the content scrolls automatically to the bottom as it refreshes
+- the readable text is shown
+- the raw JSON detail block is not displayed
-## Caricamento di blocchi, container e schemi
+### Intermediate inputs
-### Catalogo blocchi e container
+The `Intermediate` tab shows the inputs each step actually received, grouped by node, with the eye icon for long values. For agent and LLM steps it is what was really sent to the model — the first place to look when a step produces something unexpected, because it distinguishes a bad prompt from a bad answer.
-Nel pannello sinistro:
+### The execution tree
-- se il catalogo e in caricamento, compare un loader
-- non viene mostrato `No blocks found` o `No containers found` durante il fetch iniziale
+When a run has container iterations, an `Execution tree` panel appears under the executions rail. It shows the run, its container steps — badged `waiting` when a container is waiting on its subflow — and one child entry per iteration. Clicking any of them opens that execution.
-### Caricamento schema del nodo
+This is the only place to reach *past* iterations of a container; the canvas shows the current one.
-Se clicchi un nodo e lo schema non e ancora disponibile:
+### Containers waiting for a person
-- il nodo mostra un loader (`Loading block...` o `Loading container...`)
-- il click puo forzare un retry del caricamento
+When more than one container is waiting for a human answer, a bar above the viewer lists them, one button per container and iteration, so you can go straight to the one you mean to answer.
-Questo e utile quando i type descriptor arrivano in cache solo dopo il mount del nodo.
+## Simulated runs
-## Connessioni e selezione
+A simulated run is one where the flow's **human interaction steps are answered by a language model instead of by a person**, so the flow can go from end to end without anyone sitting at the dialog.
-Le connessioni, sia dati sia dependency, possono essere selezionate.
+### Starting one
-Quando una connessione e selezionata:
+Next to the ordinary start button in the execution toolbar there is a second play button, marked with a robot icon rather than a person. The two tooltips say which is which:
-- viene evidenziata
-- compare l'icona `x` per eliminarla
-- puoi anche usare `Delete` o `Backspace`
+- `Run - you answer the human steps`
+- `Run simulated - an LLM answers the human steps`
-Cliccando nel vuoto del canvas:
+The simulated button only appears when the flow actually has interactive steps and all of them can be simulated. It is otherwise subject to the same conditions as a normal start: the inputs filled in, the credentials provided, the execution not yet started, and not a subflow run.
-- la connessione viene deselezionata
+Clicking it opens `Simulation Settings`, which asks for the provider and the model, and — under `Model parameters` — optionally for temperature, top P, top K, maximum tokens and a seed. Each is left at the server's default when empty.
-## Suggerimenti pratici
+The seed deserves attention. It fixes the randomness, so the same inputs produce the same answer. Without it, two runs of the same flow differ for reasons that have nothing to do with anything you changed — which is precisely what you are trying to rule out when you compare runs.
-- salva spesso dopo modifiche strutturali
-- controlla il pannello errori prima di eseguire
-- usa `Connections` per passare dati e `Dependencies` solo per imporre ordine
-- usa i container per isolare parti riutilizzabili del flow
-- se un nodo sembra incompleto, controlla i parametri mancanti prima di eseguire
-- se un testo e troncato, usa l'icona occhio invece di allargare il nodo
+### What is different about a simulated run
-## Problemi comuni
+- The interactive nodes cannot be opened during the run; the interaction dialog will not appear.
+- The execution header carries a `Simulated` badge and, under `Details`, the simulator that answered: `provider / model`.
+- The execution list marks the run with the same badge.
+- Containers pass the simulation down to their subflow runs automatically.
-### Vedo un loader invece dei blocchi nella sidebar
+Only the *answering of interactive steps* is simulated. LLM blocks, HTTP calls and agent steps run exactly as they normally would.
-Il catalogo blocchi o container e ancora in caricamento. Attendi che il backend restituisca i type descriptor.
+### Repeating a simulated run
-### Un nodo sembra vuoto quando lo apro
+A rerun inherits the simulator of the run it repeats, but not the decision to simulate — that stays an explicit act. When you rerun a simulated execution, a notice says so:
-Lo schema del tipo potrebbe non essere ancora disponibile. Cliccando il nodo l'app puo ritentare automaticamente il caricamento.
+> The run this one repeats was simulated with ``. Start it simulated, with the same model, or the comparison will also be comparing two simulators.
-### Un container risulta incompleto
+The settings dialog preselects that model for you.
-Verifica che abbia un `Subflow`. Un container senza subflow viene considerato mancante.
+## Re-running and comparing executions
-### Il flow resta DRAFT dopo il save
+### Re-running
-Apri il pannello errori di validazione. Il flow puo essere stato salvato ma non essere ancora eseguibile.
+Each run in the executions list has a replay button, `Rerun execution`, available once the run has reached a final state. Subflow runs cannot be rerun on their own.
-### Non vedo una risposta intera nei task
+A rerun carries over the workflow inputs, the global input descriptors, the credentials that were provided, and the simulator descriptor. It is **created, not started**: you still press play, or the simulate button.
-Se il valore e lungo o e stato troncato, usa l'icona occhio per aprire il contenuto completo in readonly.
+Re-running is also how you find out how much a flow varies on its own, which is worth knowing before you read any comparison — a difference no larger than the flow's ordinary run-to-run variation is not evidence of anything.
-## Conclusione
+### Run history
-HumAIn Flow permette di lavorare sia in modo visuale sia assistito. Il percorso tipico consigliato e:
+Runs of the same flow are grouped into one collapsible card in the executions rail, showing the flow name, the project chip when there is one, the number of runs, the latest status and when it last ran. Collapsed, it offers `Open latest execution`. Expanded, it lists each run by number, with a badge saying whether it is an original `Run` or a `Rerun`, and — for a rerun — which run it repeats.
-1. creare o aprire un flow
-2. costruirlo manualmente o con l'assistente
-3. salvare
-4. correggere eventuali errori di validazione
-5. eseguire il flow dal tab `Tasks`
-6. monitorare input, output e log fino al completamento
+Above the list there is a search box and a `Filters & sorting` disclosure with status toggles (`All`, `Init`, `Running`, `Paused`, `Final`) and an ordering control.
-Se vuoi distribuire questa guida agli utenti finali, puoi condividerla direttamente come documento markdown oppure convertirla in PDF o pagina documentazione interna.
+> These run groups are the rerun history of a single flow. They are a different thing from a project run, which is one execution of each of several flows.
+
+### Comparing two runs
+
+On a group with more than one run, the `Compare` button turns on a picking mode: tick exactly two runs — the bar keeps count — and confirm. The comparison opens in place of the execution viewer.
+
+It shows:
+
+- a header naming the two runs, a toggle between `Only differences` and `All values`, and a `Close` button;
+- a summary line — *X of Y nodes differ* — followed by the caveat that model output varies between runs on its own, so a difference is not by itself evidence of a change in behaviour;
+- one section per outcome and per node, each marked `equal`, `changed`, `only-left` or `only-right`, with the status each side reached and the differing values side by side, highlighted inline.
+
+Two runs that share no node at all are almost certainly runs of different versions of the flow, and the view says so rather than lining up unrelated steps. If the two runs were answered differently — one simulated and one not, or simulated with different models — a warning says that part of what follows comes from that, not from the runs themselves.
+
+This is the general-purpose comparison. A bias report does the same work, but knows which intervention was applied and can therefore speak about its effect.
+
+## Bias and mitigation: the idea
+
+A flow that runs successfully is not necessarily a flow you should trust. A prompt can lean on a stereotype, a decision step can favour one kind of answer, an automated recommendation can be taken at face value by the next step. The bias tooling exists to turn that worry into a measurement: you state the risk on the node where you suspect it, you inject the suspect behaviour on purpose, and you look at what actually changed downstream.
+
+The work has three stages:
+
+1. **Annotate** — record the risk on the node, in the editor.
+2. **Probe** — optionally give the annotation an executable behaviour, so an experiment can switch it on.
+3. **Experiment and read the report** — run the flow with the probe active, compare it against a baseline run, and see whether the outputs, the branches or the final outcome moved.
+
+The same machinery works in both directions. A **bias probe** injects the problematic behaviour to see what it does. A **mitigation probe** injects a corrective instruction to see whether it helps. You can also activate both at once.
+
+> Nothing here scores a flow as fair or unfair. The platform shows you what changed; the judgement remains yours.
+
+## Annotating a node
+
+### Where annotations live
+
+Bias annotations are attached to a node in the **editor**, on the node card itself. Not every block type accepts them — where the feature is not offered, the trigger does not appear.
+
+A node that carries annotations shows a small badge with the count. Hovering it tells you how many there are and the highest severity among them, and states when one of them carries an executable probe.
+
+Clicking the `Bias annotations` trigger opens the list of annotations for that node, with `Add bias annotation` in its header and `Edit` / `Remove` on each card. A node can hold up to 50.
+
+During an execution the same list is reachable from the node in the execution canvas, in read-only mode.
+
+### What an annotation records
+
+The form is driven by the server, so the exact set of fields follows the platform version. Currently it asks for:
+
+| Field | Required | What it is for |
+|---|---|---|
+| `Category` | yes | The kind of risk: selection, automation, historical, accessibility, measurement, confirmation or exclusion bias, or a transparency risk. The form shows a description of the option you pick. |
+| `Severity` | yes | Low, Medium or High. |
+| `Issue` | yes | The risk itself, in prose. Up to 2000 characters. |
+| `Rationale` | no | Why it matters. Up to 4000 characters. |
+| `Mitigation` | no | A possible corrective action, in prose. Up to 4000 characters. This field is documentation — it is not executed. |
+| `Status` | no | Where the review stands: Proposed, Confirmed, Mitigated or Dismissed. New annotations start as Proposed. |
+| `Source` | no | Manual or Automated. Annotations you write by hand are Manual. |
+| `Analysis identifier` | no | For annotations produced by an external analysis. |
+| `Bias probe` | no | The executable behaviour to measure. See below. |
+| `Mitigation probe` | no | The executable correction to apply. See below. |
+
+Length-limited fields show a live character counter.
+
+An annotation with no probe is perfectly useful: it documents a risk for whoever reviews the flow. It just cannot be run.
+
+### Behavioral probes
+
+A probe is what makes an annotation executable. It is described as *an optional controlled behaviour used only by bias experiments* — it never affects a normal run of the flow, only a run started from an experiment.
+
+A probe has an **activation mode**, which says how the behaviour is injected. Which modes are on offer depends on the node type, so the editor loads them per node:
+
+| Mode | What it does | Where it applies |
+|---|---|---|
+| `PROMPT_DIRECTIVE` | Adds an experimental behavioural directive to the node's prompt. | LLM blocks, MCP agent and agent-chat blocks, human interaction and decision blocks, and conditional or switch blocks that are configured to use an LLM. |
+| `INPUT_TRANSFORMATION` | Rewrites selected textual inputs before the node runs. | Any block. |
+| `OUTPUT_TRANSFORMATION` | Rewrites textual outputs after the node runs. | Any block. |
+| `ROUTING_OVERRIDE` | Forces a conditional, switch or human decision block down a named branch. | Conditional, switch and human decision blocks. |
+| `MOCK_RESPONSE` | Skips an external call and returns a configured answer instead. | HTTP server-call blocks. |
+
+The fields you fill in follow from the mode:
+
+- **Prompt directive** asks for an `Experimental instruction` — the text added to the prompt.
+- **Input transformation** and **output transformation** ask for a `Transformation template`. `${original}` is the only placeholder, and stands for the value being transformed; if you leave it out, your instruction is simply prepended to the original value. Input transformation additionally offers a `Target inputs` checklist of the node's real input ports — leave them all unchecked to target every textual input, and note that multiple inputs are transformed item by item.
+- **Routing override** asks for the `Forced output branch`, chosen from the node's actual output ports.
+- **Mock response** asks for one typed value per output port rather than an instruction — a checkbox for boolean outputs, a JSON value for structured ones, plain text otherwise.
+
+Every probe also has an `Expected impact` field. It records what you expect to observe; it does not change the execution.
+
+> A prompt directive on a human interaction node biases the *simulator's* prompt. If a real person answers that step, the probe does nothing.
+
+A probe counts as **executable**, and is therefore selectable in an experiment, once it has an activation mode and the content that mode needs — a non-empty instruction, or at least one mock output for a mock response. The annotation list marks such annotations with an `Executable probe` badge.
+
+### Bias probe or mitigation probe
+
+The two probe editors are identical. What differs is intent, and which side of the annotation an experiment switches on:
+
+- the **bias probe** is *the behaviour to measure* — inject the suspect behaviour and see what moves;
+- the **mitigation probe** is *the correction to apply* — inject the corrective instruction and see whether the answer improves.
+
+Every experiment dialog has an `Intervention direction` selector that decides which of the two fires:
+
+- `BIAS` — only annotations that carry a bias probe are offered, and only bias probes run.
+- `MITIGATION` — only annotations that carry a mitigation probe are offered.
+- `BOTH` — only annotations that carry *both* are offered, and both fire in the same run.
+
+Take care with the word *mitigation*: the `Mitigation` text field and the `Mitigated` status are documentation, while the mitigation **probe** is the executable one.
+
+## Running an experiment
+
+### The two shapes of experiment
+
+There are two, and you choose between them by choosing which control you click — there is no single dialog with a toggle.
+
+| | Isolated step experiment | Full-flow biased rerun |
+|---|---|---|
+| Started from | the chart icon on a **node**, in the execution canvas | the `Create biased rerun` button on the execution **toolbar** |
+| What runs | that one block, repeated with the probe on | the whole flow, as a new execution |
+| Baseline | the output already recorded in the completed run | the run you started from |
+| Report | produced immediately, in the same dialog | produced later, when you click `Compare with baseline` |
+
+Both need a **finished** baseline execution: an experiment compares a run against a run, so the baseline has to be complete.
+
+### Measuring one step in isolation
+
+The chart icon appears on nodes that carry an executable probe. It is shown disabled, rather than hidden, when the experiment is not currently possible, and the tooltip says why — the execution has not finished yet, or the node cannot be replayed on its own.
+
+Nodes that cannot be replayed on their own are the user-interactive ones (chat interaction, human decision, human interactive) and containers. For those, the tooltip points you at `Create biased rerun` instead.
+
+The `Measure bias impact` dialog asks for:
+
+- the `Intervention direction`;
+- which `Executable annotations` to activate — all of them are pre-selected, and changing the direction drops the ones that no longer qualify;
+- `Repetitions`, between 1 and 10, 3 by default — how many times to run the biased variant;
+- whether to `Include raw outputs in the report`, on by default;
+- the external side effect policy, described below.
+
+The experiment is queued on the server and the dialog reports its progress. When it finishes, the report replaces the form in the same window.
+
+Two things are worth knowing about how an isolated experiment works. The baseline is **not** re-run: it is the output already recorded in the completed execution, and only the variants are executed. And because only one block runs, an isolated report has **no downstream section** — nothing downstream was executed to observe.
+
+Closing the dialog does not cancel the job. It keeps running, and its report appears in the `Bias impact` tab when it is done.
+
+### Creating a biased rerun
+
+`Create biased rerun` is on the toolbar above the execution graph. It needs a top-level run — a subflow run cannot be a baseline — that has reached a final state and contains at least one node with an activatable probe. When one of those conditions is missing, the `Bias impact` tab states which one.
+
+The dialog lists one section per candidate node:
+
+- for ordinary nodes, a checkbox per eligible annotation. Unlike the isolated dialog, **nothing is pre-selected** here;
+- for containers, a single `Activate biases in the subflow` checkbox, which turns on every executable probe on the nodes inside it.
+
+Confirming creates a **new execution** of the flow, with the same inputs and the probes active, and takes you to it. No report is produced yet — the variant simply runs.
+
+A variant run is clearly labelled. Its header carries a `Bias variant`, `Mitigation variant` or `Bias + mitigation` badge, with the reminder that probes were active and the result is therefore not a baseline, and an expandable block showing the experiment, baseline and intervention identifiers. In the run picker, such runs are suffixed `· bias variant`.
+
+### Comparing with the baseline
+
+Once the variant has finished, `Compare with baseline` — the compare icon, shown only on a variant run — produces the report. The comparison walks both runs and records what differs.
+
+Comparisons are cached: asking again for the same pair returns the report that already exists, and a report built by an older version of the comparison is recomputed in place, keeping its identity and its assessment history.
+
+## External side effects
+
+Some blocks call the outside world — HTTP server calls, MCP agents, MCP agent chats. Repeating them in an experiment would repeat their effects, so every experiment dialog has an `External side effects` section. When the nodes you selected can make such calls, a warning appears above it.
+
+| Policy | What happens |
+|---|---|
+| `Block external calls` | **The default.** The experiment refuses to run anything that would reach an external service. |
+| `Mock external calls` | The call is not made. The block is bypassed and its outputs are filled with placeholders. |
+| `Allow after confirmation` | Real calls are made. You are asked to confirm before the experiment starts. |
+
+Choosing `Allow after confirmation` and pressing the run button opens a confirmation dialog first; your answer is recorded on the resulting execution, so the choice remains auditable afterwards.
+
+Three practical consequences:
+
+- With the default policy, an experiment on a flow containing an HTTP or MCP node will **fail** rather than run — the message says the side effects were blocked. This is the most common reason for an experiment to error.
+- The isolated experiment checks this up front, so it fails immediately. A full-flow rerun only discovers it when the offending step is reached, in the middle of the run.
+- With `Mock external calls`, everything downstream sees a placeholder instead of a real answer. Differences downstream of a mocked node therefore partly reflect the mock, not the intervention. The report lists every mocked call for exactly this reason.
+
+## Reading a bias impact report
+
+The same report viewer is used everywhere: inline at the end of an isolated experiment, in the compare dialog, and when you open a stored report from the `Bias impact` tab.
+
+### The headline
+
+The top of the report tells you, before any number, whether the thing that matters changed:
+
+- `Final decision changed` or `Final decision unchanged` — set when the flow's final outcome differed, or when it took a different branch somewhere.
+- `X of Y subjects changed`, when the compared output is a list and each element is a subject.
+- The strongest numeric delta, written in the flow's own units, for example `Score 5 → 8 (+3)`.
+- A chip about simulation, when either run was simulated.
+- A chip summarising the model assessment, when one has been requested.
+
+Below them, a one-sentence summary generated by the server, which leads with the decision, then the subjects, then the averages, then the counts of changed downstream nodes and routing changes.
+
+A collapsed section carries the identifiers — baseline execution, biased execution, experiment, node, annotations, repetitions — for when you need to trace a report back to its runs.
+
+### Immediate impact
+
+What the activated node itself produced, baseline against variant.
+
+Three figures are given: whether the output changed at all, the change rate, and the maximum **text difference**, which runs from 0 for identical to 1 for maximally different.
+
+For list-shaped outputs there is a `Per subject` breakdown, showing only the changed subjects by default. Each subject expands into a two-column diff with word-level highlighting. For container nodes, the breakdown is per iteration, and states the status each side ended on.
+
+The numbers come in three flavours, in increasing order of usefulness:
+
+- a normalised **text difference**, which tells you *that* something moved;
+- the **share of list elements** that changed, which tells you *how widely*;
+- **labelled numeric deltas**, which tell you *how much*, in the flow's own units. These are extracted from plainly labelled numbers such as `Score: 7` or `confidence = 0.8`, which is why they are sometimes absent.
+
+List elements are paired by position: element *i* is treated as the same subject on both sides.
+
+### Downstream impact
+
+Every node downstream of the activated one, with whether the change propagated, the status each side reached, and the same per-field and per-subject drill-down. A checkbox narrows the list to the changed nodes.
+
+An isolated experiment always shows this section empty, and says so.
+
+### Routing and outcome changes
+
+The two largest things an intervention can do:
+
+- an **outcome** change — the flow ended on a different outcome;
+- a **routing** change — the flow took a different branch at a named node.
+
+A routing change is reported only when both runs produced exactly one branch at that node and the two differ.
+
+### Mocked side effects
+
+Present only when something was mocked. Each entry names the node and the kind of call that was skipped. Read it as a caveat on everything downstream of those nodes.
+
+### Warnings
+
+Always shown, even when empty. The ones you will actually meet:
+
+- on an isolated report, that the baseline was captured from the completed run and only the variants were repeated;
+- on a full-flow report, that observed differences may also include ordinary non-determinism from external models;
+- that only the first 200 elements of a long list, or the first 20 000 characters of a long text, were compared;
+- that the two runs did not perform the same number of container iterations;
+- that the interactive steps were simulated on one side and answered directly on the other, or simulated with different models — in which case part of the difference does not come from the intervention at all.
+
+Warnings are not failures. They tell you how far to trust the numbers above them.
+
+### Highlighting the report on the canvas
+
+`Highlight on canvas` closes the report and paints it onto the execution graph. A legend appears above the canvas, nodes carrying an active annotation and nodes whose output changed get their own badges, and connections on a branch that changed are marked. `Back to normal view` restores the ordinary canvas.
+
+## Asking a model to assess a report
+
+A report measures differences; it does not say whether a difference matters. `Evaluate impact with LLM` asks a model to read the compared pairs and give an opinion. You choose the provider and model in the usual picker.
+
+Each assessment yields:
+
+- an **impact level** — `NONE`, `COSMETIC`, `SUBSTANTIVE` or `DECISIVE` — how far the meaning of the output moved;
+- an **attribution** — `INJECTION`, `NON_DETERMINISM` or `UNCLEAR` — whether the change carries the intervention's fingerprint or looks like ordinary model variation;
+- a narrative, plus a badge on each individual subject and field.
+
+Assessments accumulate: the report keeps a history, newest first, so a second opinion does not erase the first. The per-subject badges follow the most recent one. Pairs that are identical are never sent to a model.
+
+Two limits are worth stating plainly, and the viewer states them itself. This is an assessment by a model, not a measurement. And with a single run per side, a difference cannot be separated from ordinary model variation with certainty — which is the argument for raising `Repetitions`, and for setting a seed on the judge model when you want a repeatable opinion.
+
+> A report saved with `Include raw outputs` switched off cannot be assessed later: the outputs it would need were not kept. If you may want a model's opinion, leave that box ticked.
+
+## The Bias impact tab
+
+Every report produced for an execution is listed in the `Bias impact` tab of the execution panel, alongside `Inputs`, `Intermediate`, `Logs` and `Output`.
+
+Each row shows the kind of report, whether anything changed, the date, the summary sentence, how many annotations were activated, and whether a model has assessed it. Clicking a row opens the full report.
+
+When the tab is empty it explains what would fill it: on a variant run, that comparing it with the run it repeats is what produces the report; on an ordinary run, what a report is and how many nodes carry a probe that could be activated.
+
+## Administration
+
+Accounts with the administrator role reach a separate workspace through `Admin users` in the user menu. It has a `Back to editor` link and a sidebar with two groups: `Auth`, holding `Users` and `Create user`, and `Stats`, holding `Operations`.
+
+### Users
+
+The list shows each account with its username and email. For each one you can change the role between `USER` and `ADMIN` — the `Save role` button stays disabled until you actually change it — reset the password, or delete the account.
+
+### Create user
+
+Provisions a new account: username, email, role and password. A live checklist below the password field shows which policy rules the password currently satisfies.
+
+### Operations
+
+Aggregated usage figures, for everyone or for one account at a time. The cards cover flows (with how many are published and finalized), executions (running, succeeded, failed), simulations, and — once a user is selected — authentication figures such as login count and average session length, alongside the last flow update and last execution.
+
+## Practical tips
+
+- save often after structural changes
+- check the errors panel before running
+- use `Connections` to pass data and `Dependencies` only to impose order
+- use containers to isolate reusable parts of the flow
+- if a node looks incomplete, check its missing parameters before running
+- if a text is truncated, use the eye icon instead of widening the node
+- finalize the flow you intend to use as a baseline, so later runs remain comparable
+- before trusting a difference between two runs, re-run the baseline once and see how much the flow moves on its own
+- when a comparison has to mean something, simulate both runs with the same model and a fixed seed
+- leave `Include raw outputs` ticked on an experiment you may want a model to assess later
+- annotate risks as you build, even without a probe: an annotation with no probe is still the record of a concern
+
+## Common problems
+
+### I see a loader instead of the blocks in the sidebar
+
+The block or container catalogue is still loading. Wait for the backend to return the type descriptors.
+
+### A node looks empty when I open it
+
+The type schema may not be available yet. Clicking the node makes the application retry the load.
+
+### A container is reported as incomplete
+
+Check that it has a `Subflow`. A container without one is considered missing.
+
+### The flow stays DRAFT after saving
+
+Open the validation errors panel. The flow may have been saved and still not be executable.
+
+### An error points at a container, not at a block
+
+The error comes from inside the container's subflow. Open the subflow with `View Flow`: the node responsible is highlighted there.
+
+### I cannot see a whole answer in the tasks view
+
+If the value is long or truncated, use the eye icon to open the full content in read-only mode.
+
+### The start button is disabled and I cannot see why
+
+Check the banner above the graph. An execution that still needs a provider credential cannot start, and the tooltip on the start button names the provider it is waiting for.
+
+### The experiment button on a node is greyed out
+
+Its tooltip says why. Either the execution has not finished yet — an experiment needs a completed baseline — or the node cannot be replayed on its own, which is the case for interactive steps and containers. For those, use `Create biased rerun` on the toolbar instead.
+
+### My experiment failed with a side-effect error
+
+The flow contains an HTTP or MCP node and the policy was left at `Block external calls`. Choose `Mock external calls`, or `Allow after confirmation` if the real calls are safe to repeat.
+
+### I cannot ask a model to assess my report
+
+The report was saved without its raw outputs, so there is nothing left to assess. Run the experiment again with `Include raw outputs in the report` ticked.
+
+### A comparison shows differences I did not cause
+
+Read the warnings. Model output varies between runs on its own; if the two runs were simulated differently, or not simulated at all, part of the difference comes from that. Fix a seed and simulate both sides the same way, then compare again.
+
+### My uploaded file does not appear in the prompt
+
+Check that the input is the one the block actually reads. A block input can either hold its own file or point at a global input; if it points at a global input, the file must be provided with the execution inputs.
+
+## Conclusion
+
+HumAIn Flow lets you work visually and with assistance. The recommended path is:
+
+1. create or open a flow
+2. build it by hand or with the assistant
+3. save it
+4. fix any validation errors
+5. run it from the `Tasks` tab
+6. follow inputs, outputs and logs to completion
+
+And, when the question is not only *does the flow work* but *what is it sensitive to*:
+
+7. finalize the flow and record a baseline run
+8. annotate the steps you want to probe
+9. run the experiment and read the report
diff --git a/docs/user-guide.pdf b/docs/user-guide.pdf
new file mode 100644
index 0000000..2ba0ca7
Binary files /dev/null and b/docs/user-guide.pdf differ