[P1] Add four-tier host-controlled authority policy and approval/diff API #53

Closed
opened 2026-08-16 04:07:11 +00:00 by lost-rob0t · 1 comment
lost-rob0t commented 2026-08-16 04:07:11 +00:00 (Migrated from github.com)

Goal

Add a reusable four-tier authority policy to prolog-rlm core so downstream builders can limit model autonomy consistently.

The authority modes belong in the RLM codebase. They are runtime policy primitives for hosts/builders. They are not controlled by the model/agent itself.

Interactive rendering remains outside core: prolog-rlm owns authority state, approval state, pending diffs/operations, fingerprints, and approve/deny/edit predicates; clients such as agentProlog/ decide how to display them.

Required four-tier authority modes

  1. approve_diff — applicable side effects become pending reviewable operations/diffs and require explicit approval.
  2. allow_once — authorize exactly one matching pending operation, then consume that authorization atomically.
  3. allow_session — authorize matching side effects for the current runtime/session only.
  4. dangerous — explicit dangerous/unattended mode; otherwise-valid side effects execute without interactive approval.

There is no yolo public mode name. Tier 4 is dangerous in APIs, status, traces, and downstream UI.

Ownership and control

Trusted host/application code may set policy:

rlmSetAuthorityIfUnset(approve_diff).
rlmSetAuthorityIfUnset(allow_once).
rlmSetAuthorityIfUnset(allow_session).
rlmSetAuthorityIfUnset(dangerous).

rlmAuthority(Mode).
rlmSetAuthority(Mode).

Exact naming may follow project conventions.

The model/agent must not receive an unrestricted tool/predicate that can call rlmSetAuthority/1 or otherwise raise its own authority. Authority changes originate from trusted host code or an explicitly mediated user action.

A child agent may inherit an equal or stricter authority policy, but must never widen beyond the authority ceiling established by its parent/host.

rlmSetAuthorityIfUnset/1 is idempotent and never overwrites an existing host-selected mode.

Unset authority defaults to approve_diff; it must never silently become dangerous.

Core approval/diff API

The library exposes structured pending operations that any UI can render. At minimum provide equivalents of:

rlmPendingApproval(ApprovalId, Approval).
rlmApprove(ApprovalId, Outcome).
rlmDeny(ApprovalId, Reason, Outcome).
rlmEdit(ApprovalId, EditedOperation, Outcome).

Approve, deny, and edit are required first-class operations.

A pending approval contains enough structured data for downstream clients to render:

  • operation/tool name;
  • side-effect class;
  • target path/command/server/request;
  • proposed diff/patch or normalized operation description;
  • cwd and bounded environment delta where relevant;
  • exact executable arguments;
  • stable fingerprint/hash;
  • creation/session metadata;
  • capability required;
  • current host authority tier.

Approval

Approval authorizes the exact fingerprinted proposal. A changed payload requires fresh approval.

Denial

Denial records a structured reason/event and performs no target mutation.

Edit

Editing creates a new proposal/fingerprint. Approval attached to the old fingerprint cannot authorize the edited operation.

The edit predicate is a library primitive, not an editor UI.

Authority ordering

tool installed/loaded
        -> capability granted
        -> host authority ceiling evaluated
        -> pending diff/operation if required
        -> approve / deny / edit when applicable
        -> execute

Capabilities remain the hard allow-list. Authority controls how much human mediation an otherwise-capable model receives.

Tier semantics

approve_diff

Every applicable mutation enters the pending-approval API. Core returns structured approval_required(...); it renders nothing.

allow_once

  • bound to one exact operation/fingerprint;
  • consumed atomically when execution starts;
  • cannot leak across retries or parallel branches;
  • does not authorize a modified payload.

allow_session

  • scoped to a concrete runtime/session identifier;
  • non-persistent by default;
  • expires on runtime teardown/restart;
  • status API reports that session-level authority is active.

dangerous

  • explicit host/user opt-in only;
  • never selectable merely by model output;
  • prominently represented as dangerous in status and trace data;
  • skips interactive approval for side effects that already pass capability and hard runtime validation;
  • does not bypass schemas, budgets, path confinement, network policy, process policy, or capability denial;
  • does not imply arbitrary call/1 or arbitrary shell execution.

Child-agent rule

Authority is narrowing-only across delegation, analogous to capability narrowing.

Examples:

host dangerous      -> child dangerous / allow_session / allow_once / approve_diff
host allow_session  -> child allow_session / allow_once / approve_diff
host allow_once     -> child allow_once / approve_diff
host approve_diff   -> child approve_diff only

No child/model may widen upward.

Side-effect metadata

Tool metadata should declare classes such as:

read
write
process
network_write
install
service_start
service_stop
repository_mutation

Pure reads should not require diff approval unless host policy explicitly requests it.

UI boundary

prolog-rlm MUST NOT own:

  • terminal widgets;
  • approval dialogs;
  • diff rendering UI;
  • keybindings;
  • questionnaire screens;
  • plan/spec/build/verify screens.

It only exposes structured APIs/events/state. agentProlog/ and other downstream clients render them.

Acceptance criteria

  • Implement approve_diff, allow_once, allow_session, and dangerous.
  • Authority policy is set by trusted host/application code, not autonomously by the model.
  • Remove/avoid yolo as a public authority-mode name.
  • Add set-if-unset, inspect, and trusted-host set predicates.
  • Default unset authority to approve_diff.
  • Enforce narrowing-only authority across child agents/delegation.
  • Add pending-approval query API.
  • Add explicit approve predicate.
  • Add explicit deny predicate.
  • Add explicit edit predicate.
  • Bind approvals to stable exact fingerprints.
  • Editing invalidates old approval fingerprints.
  • allow_once is atomically single-use under retries/parallel execution.
  • allow_session is runtime/session scoped and non-persistent by default.
  • dangerous remains subject to capability/schema/budget/confinement checks.
  • Read-only operations can execute without pointless diff approval.
  • No TUI/interactive UI code lands in core.
  • Deterministic tests cover all tiers, approve/deny/edit, stale fingerprints, retries, concurrency, session teardown, child narrowing, and attempted agent self-escalation.

Integration

Compose with:

  • #48 external tool-library loading boundary;
  • #50 external standard tool pack;
  • #52 declarative MCP server install/run predicates;
  • downstream agentProlog/ editor/TUI.

MCP install/start and other side-effecting operations must use this same host-controlled authority policy rather than inventing separate confirmation systems.

Non-goals

  • No interactive UI in core.
  • No model-controlled authority escalation.
  • No automatic escalation from rlmLoadTools/1 or rlmLoadAllTools/0.
  • No conflation of loaded tools, capabilities, and authority.
  • dangerous does not disable hard runtime boundaries.
## Goal Add a reusable four-tier authority policy to `prolog-rlm` core so downstream builders can limit model autonomy consistently. The authority modes **belong in the RLM codebase**. They are runtime policy primitives for hosts/builders. They are **not controlled by the model/agent itself**. Interactive rendering remains outside core: `prolog-rlm` owns authority state, approval state, pending diffs/operations, fingerprints, and approve/deny/edit predicates; clients such as `agentProlog/` decide how to display them. ## Required four-tier authority modes 1. `approve_diff` — applicable side effects become pending reviewable operations/diffs and require explicit approval. 2. `allow_once` — authorize exactly one matching pending operation, then consume that authorization atomically. 3. `allow_session` — authorize matching side effects for the current runtime/session only. 4. `dangerous` — explicit dangerous/unattended mode; otherwise-valid side effects execute without interactive approval. There is **no `yolo` public mode name**. Tier 4 is `dangerous` in APIs, status, traces, and downstream UI. ## Ownership and control Trusted host/application code may set policy: ```prolog rlmSetAuthorityIfUnset(approve_diff). rlmSetAuthorityIfUnset(allow_once). rlmSetAuthorityIfUnset(allow_session). rlmSetAuthorityIfUnset(dangerous). rlmAuthority(Mode). rlmSetAuthority(Mode). ``` Exact naming may follow project conventions. The model/agent must **not** receive an unrestricted tool/predicate that can call `rlmSetAuthority/1` or otherwise raise its own authority. Authority changes originate from trusted host code or an explicitly mediated user action. A child agent may inherit an equal or stricter authority policy, but must never widen beyond the authority ceiling established by its parent/host. `rlmSetAuthorityIfUnset/1` is idempotent and never overwrites an existing host-selected mode. Unset authority defaults to `approve_diff`; it must never silently become `dangerous`. ## Core approval/diff API The library exposes structured pending operations that any UI can render. At minimum provide equivalents of: ```prolog rlmPendingApproval(ApprovalId, Approval). rlmApprove(ApprovalId, Outcome). rlmDeny(ApprovalId, Reason, Outcome). rlmEdit(ApprovalId, EditedOperation, Outcome). ``` Approve, deny, and edit are required first-class operations. A pending approval contains enough structured data for downstream clients to render: - operation/tool name; - side-effect class; - target path/command/server/request; - proposed diff/patch or normalized operation description; - cwd and bounded environment delta where relevant; - exact executable arguments; - stable fingerprint/hash; - creation/session metadata; - capability required; - current host authority tier. ### Approval Approval authorizes the exact fingerprinted proposal. A changed payload requires fresh approval. ### Denial Denial records a structured reason/event and performs no target mutation. ### Edit Editing creates a new proposal/fingerprint. Approval attached to the old fingerprint cannot authorize the edited operation. The edit predicate is a library primitive, not an editor UI. ## Authority ordering ```text tool installed/loaded -> capability granted -> host authority ceiling evaluated -> pending diff/operation if required -> approve / deny / edit when applicable -> execute ``` Capabilities remain the hard allow-list. Authority controls how much human mediation an otherwise-capable model receives. ## Tier semantics ### `approve_diff` Every applicable mutation enters the pending-approval API. Core returns structured `approval_required(...)`; it renders nothing. ### `allow_once` - bound to one exact operation/fingerprint; - consumed atomically when execution starts; - cannot leak across retries or parallel branches; - does not authorize a modified payload. ### `allow_session` - scoped to a concrete runtime/session identifier; - non-persistent by default; - expires on runtime teardown/restart; - status API reports that session-level authority is active. ### `dangerous` - explicit host/user opt-in only; - never selectable merely by model output; - prominently represented as `dangerous` in status and trace data; - skips interactive approval for side effects that already pass capability and hard runtime validation; - does not bypass schemas, budgets, path confinement, network policy, process policy, or capability denial; - does not imply arbitrary `call/1` or arbitrary shell execution. ## Child-agent rule Authority is narrowing-only across delegation, analogous to capability narrowing. Examples: ```text host dangerous -> child dangerous / allow_session / allow_once / approve_diff host allow_session -> child allow_session / allow_once / approve_diff host allow_once -> child allow_once / approve_diff host approve_diff -> child approve_diff only ``` No child/model may widen upward. ## Side-effect metadata Tool metadata should declare classes such as: ```text read write process network_write install service_start service_stop repository_mutation ``` Pure reads should not require diff approval unless host policy explicitly requests it. ## UI boundary `prolog-rlm` MUST NOT own: - terminal widgets; - approval dialogs; - diff rendering UI; - keybindings; - questionnaire screens; - plan/spec/build/verify screens. It only exposes structured APIs/events/state. `agentProlog/` and other downstream clients render them. ## Acceptance criteria - [ ] Implement `approve_diff`, `allow_once`, `allow_session`, and `dangerous`. - [ ] Authority policy is set by trusted host/application code, not autonomously by the model. - [ ] Remove/avoid `yolo` as a public authority-mode name. - [ ] Add set-if-unset, inspect, and trusted-host set predicates. - [ ] Default unset authority to `approve_diff`. - [ ] Enforce narrowing-only authority across child agents/delegation. - [ ] Add pending-approval query API. - [ ] Add explicit approve predicate. - [ ] Add explicit deny predicate. - [ ] Add explicit edit predicate. - [ ] Bind approvals to stable exact fingerprints. - [ ] Editing invalidates old approval fingerprints. - [ ] `allow_once` is atomically single-use under retries/parallel execution. - [ ] `allow_session` is runtime/session scoped and non-persistent by default. - [ ] `dangerous` remains subject to capability/schema/budget/confinement checks. - [ ] Read-only operations can execute without pointless diff approval. - [ ] No TUI/interactive UI code lands in core. - [ ] Deterministic tests cover all tiers, approve/deny/edit, stale fingerprints, retries, concurrency, session teardown, child narrowing, and attempted agent self-escalation. ## Integration Compose with: - #48 external tool-library loading boundary; - #50 external standard tool pack; - #52 declarative MCP server install/run predicates; - downstream `agentProlog/` editor/TUI. MCP install/start and other side-effecting operations must use this same host-controlled authority policy rather than inventing separate confirmation systems. ## Non-goals - No interactive UI in core. - No model-controlled authority escalation. - No automatic escalation from `rlmLoadTools/1` or `rlmLoadAllTools/0`. - No conflation of loaded tools, capabilities, and authority. - `dangerous` does not disable hard runtime boundaries.
lost-rob0t commented 2026-08-16 23:53:16 +00:00 (Migrated from github.com)

Core-hardening update from PR #62:

Implemented and deterministically green on the current PR head:

  • canonical host authority tiers and safe default
  • set-if-unset + child narrowing
  • exact executable fingerprints (correlation metadata excluded)
  • atomic allow_once consumption/replay at tool + MCP lifecycle boundaries
  • non-blocking deferred pending-resolution Futures
  • approve/deny/edit
  • tool/MCP/agent/graph integration coverage
  • graph approval pause/resume without scheduler-worker hostage
  • canonical async-first MCP lifecycle facades restored
  • Future pre-bound outcome mismatch hang fixed
  • authority docs + canonical test-runner inclusion

#53 is not ready to close yet. The hardening audit found two P0 invariants that must be resolved first:

  • #63 approval -> execution handoff is not yet cancellation-linearizable; owner cancellation can hit a narrow state where the record is approved but the execution Future has not been attached.
  • #64 terminal pending/control/Future retention is not yet bounded for long-lived contexts.

The configured REAL OpenRouter gate is also externally blocked by HTTP 429 free-models-per-day-high-balance; the gate has not been weakened or moved to a paid model.

Keep #53 open until #63 and #64 are satisfied and the exact final PR head clears required deterministic + REAL gates.

Core-hardening update from PR #62: Implemented and deterministically green on the current PR head: - canonical host authority tiers and safe default - set-if-unset + child narrowing - exact executable fingerprints (correlation metadata excluded) - atomic `allow_once` consumption/replay at tool + MCP lifecycle boundaries - non-blocking deferred pending-resolution Futures - approve/deny/edit - tool/MCP/agent/graph integration coverage - graph approval pause/resume without scheduler-worker hostage - canonical async-first MCP lifecycle facades restored - Future pre-bound outcome mismatch hang fixed - authority docs + canonical test-runner inclusion #53 is **not ready to close** yet. The hardening audit found two P0 invariants that must be resolved first: - #63 approval -> execution handoff is not yet cancellation-linearizable; owner cancellation can hit a narrow state where the record is approved but the execution Future has not been attached. - #64 terminal pending/control/Future retention is not yet bounded for long-lived contexts. The configured REAL OpenRouter gate is also externally blocked by HTTP 429 `free-models-per-day-high-balance`; the gate has not been weakened or moved to a paid model. Keep #53 open until #63 and #64 are satisfied and the exact final PR head clears required deterministic + REAL gates.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nsaspy/prolog-rlm#53
No description provided.