When policy requires a human decision, Metnos does not execute the step. It records a pending proposal, presents it on the user's channel, and resumes only after a valid decision. Text, labels, and buttons follow the signed instance language.
The vaglio and policy
evaluate the step prepared by the planner. The outcome may allow execution, deny it,
or produce an approval_required proposal. Only the third case opens a
consent dialog.
Proposal shape depends on the capability and channel. There is no public contract that always requires three lines or always requires two buttons. The stable contract is semantic: before deciding, the person must be able to identify the action, its target, and its relevant consequences.
cap_pending, bound to the sender and turn_id.For flows that use the atomic approval_registry, the token is also
single-use, expires after ten minutes by default, and is bound to channel and sender.
The registry applies its transition inside a SQLite transaction.
| Channel | Presentation | Decision |
|---|---|---|
| Web chat | The proposal appears in the conversation; structured dialogs may open as an embedded form. | The answer or form is resolved in the same conversation and user scope. |
| Telegram | When the proposal type supports them, the message includes localized Approve and Reject buttons. Dialogs that cannot be represented as buttons remain textual. | The
cap:<turn_id>:yes|no callback must match the proposal that is still
pending. A textual yes/no answer uses the same state. |
Transferring a conversation between devices does not transfer identity to another user: conversation, active session, and pending state remain in their owner's scope.
User-facing labels are i18n catalog keys, not prose owned by the renderer. The language is resolved once from the signed instance request, and the same value is propagated to the renderer, planner, and Tutor. A channel-to-user binding cannot replace it.
All users of one instance therefore receive the same language. Two simultaneously active languages require two separate instances. A key that has not yet been translated follows the instance language → English → Italian fallback chain.
Ask Metnos with a request like this example: “Send the report to [email protected] and show me what will be sent before proceeding.”
Metnos prepares the step and, when policy requires confirmation, displays a localized proposal. One possible presentation is:
Send the monthly report to [email protected]
The operation cannot be undone after delivery
[Approve] [Reject]
The example illustrates the required information, not a fixed layout. Telegram may use buttons; web chat may use a conversational reply or an embedded form.
| Invariant | Effect |
|---|---|
| Decider = requester | Forwarding a message or knowing a token does not transfer authority to another person. |
| Current identifier | A button belonging to an earlier turn is rejected. |
| Consume before execution | Double clicks and retries do not duplicate the action. |
| Expiry | An old proposal must be generated again. |
| Per-user isolation | State, conversation, and decision are not shared between users; language belongs to the instance. |
| No implicit delegation | Approving one step does not automatically create a general or permanent grant. |
A persistent grant must be represented explicitly by policy, have a recognizable scope, and provide a revocation path. Metnos does not infer one from approval frequency, wording, or user silence.
runtime/channels/inline_ui.py: Telegram buttons and callbacks.runtime/channels/daemon.py: pending state, consumption, and dispatch.runtime/http_routes_agent.py: equivalent web-chat path.runtime/approval_registry.py: atomic tokens with TTL and decider verification.runtime/i18n.py: catalog and propagation of the instance language.tests/runtime/channels/test_telegram_authorization_buttons.py and
tests/runtime/safety/test_approval_registry_atomic.py: primary regression tests.For the user-facing route through pages and channels, see the interface guide.