← Documentation index Architecture guide › approval

Metnos

Approval flow
How a proposal is shown, decided, and executed without losing identity, language, or state.

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 that individual user's language.

Contents

  1. When a decision is requested
  2. The current flow
  3. Web chat and Telegram
  4. Language and per-user isolation
  5. Natural-language example
  6. Security invariants
  7. Current boundaries and references

1. When a decision is requested

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.

2. The current flow

  1. The runtime prepares the executor and arguments without running the action.
  2. Policy produces a typed proposal and the channel stores it in cap_pending, bound to the sender and turn_id.
  3. The UI shows the proposal text and available choices.
  4. A textual answer or valid callback consumes that same proposal.
  5. State is removed before execution, so a double click or repeated answer cannot execute the step twice.
  6. Rejection, expiry, or a stale identifier executes nothing.

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.

3. Web chat and Telegram

ChannelPresentationDecision
Web chatThe 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.
TelegramWhen 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.

4. Language and per-user isolation

User-facing labels are i18n catalog keys, not prose owned by the renderer. The lang preference is resolved from the channel-to-user binding and applied only to that turn through a local context. The same context is propagated to workers that run the planner and Tutor.

Two users may therefore use different languages in the same process without changing METNOS_LANG. When a language is added, the preferences page derives its list from the i18n catalog; a key not yet translated follows the normal user language → English → Italian fallback chain.

5. Natural-language example

Ask Metnos with a request like this example: “Send the report to management@example.org 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 management@example.org
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.

6. Security invariants

InvariantEffect
Decider = requesterForwarding a message or knowing a token does not transfer authority to another person.
Current identifierA button belonging to an earlier turn is rejected.
Consume before executionDouble clicks and retries do not duplicate the action.
ExpiryAn old proposal must be generated again.
Per-user isolationState, language, conversation, and decision are not shared between users.
No implicit delegationApproving one step does not automatically create a general or permanent grant.

7. Current boundaries and references

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.

For the user-facing route through pages and channels, see the interface guide.