A SEND refusal does not say the rule is target-dependent, so it reads as "you may not send at all" #769

Open
opened 2026-10-05 11:26:50 +02:00 by ltms · 0 comments
Owner

From the same trinotes report as #768.

What the observer saw

fleet_send -> anki (another unconfigured pane)   : accepted
fleet_send -> "lead: opus" (term_65d106559b02e1) : forbidden: observer:term_65d106559d7e43 may not SEND

Its own conclusion was: "either the observer can send or it cannot. Today the answer depends on the target, and the error does not say so."

The behaviour is right; the message is wrong

The first half of that is a misreading, and it is the message's fault. SEND for an observer is target-dependent by design — auth/Authz.java:143:

|| (caller.isObserver() && knownObserverTarget.test(targetSession));

An observer may send to another observer pane and to nothing else. Refusing a send to a lead is correct and deliberate (#705).

But may not SEND states a fact about the caller for a decision that was made about the target. A reader who has just had an identical call accepted can only conclude the gate is flaky, or that its role changed between the two calls. Both are wrong, and the second is a plausible enough story that an agent will act on it — for example by retrying, or by reporting its own role as broken.

Asked for

Name the target in the refusal, and name the rule. Something like:

forbidden: an observer may send only to another observer pane;
term_65d106559b02e1 resolves as lead

The resolution is already in hand at the point of refusal, so this is a message change, not a new lookup. Two things it must not do: name a role for a target the caller is not allowed to enumerate, and leak a pane label or cwd that the filtered panes array deliberately withholds from an observer (#758). Naming the caller's own rule and the fact that this target is not in its permitted set may be as far as it can safely go — decide that explicitly rather than by accident.

Why it is worth fixing

An observer cannot read CLAUDE.md's observer paragraph unless it happens to be in a repo that carries the block, and the canonical block says plainly: "Don't infer what you can ask." A refusal that misdescribes the rule is the one place the daemon actively teaches the wrong thing. The same applies to the collaborator rule, which is also target-dependent.

From the same `trinotes` report as #768. ## What the observer saw ``` fleet_send -> anki (another unconfigured pane) : accepted fleet_send -> "lead: opus" (term_65d106559b02e1) : forbidden: observer:term_65d106559d7e43 may not SEND ``` Its own conclusion was: *"either the observer can send or it cannot. Today the answer depends on the target, and the error does not say so."* ## The behaviour is right; the message is wrong The first half of that is a misreading, and it is the message's fault. `SEND` for an observer **is** target-dependent by design — `auth/Authz.java:143`: ```java || (caller.isObserver() && knownObserverTarget.test(targetSession)); ``` An observer may send to another observer pane and to nothing else. Refusing a send to a lead is correct and deliberate (#705). But `may not SEND` states a fact about the **caller** for a decision that was made about the **target**. A reader who has just had an identical call accepted can only conclude the gate is flaky, or that its role changed between the two calls. Both are wrong, and the second is a plausible enough story that an agent will act on it — for example by retrying, or by reporting its own role as broken. ## Asked for Name the target in the refusal, and name the rule. Something like: ``` forbidden: an observer may send only to another observer pane; term_65d106559b02e1 resolves as lead ``` The resolution is already in hand at the point of refusal, so this is a message change, not a new lookup. Two things it must not do: name a role for a target the caller is not allowed to enumerate, and leak a pane label or `cwd` that the filtered `panes` array deliberately withholds from an observer (#758). Naming the caller's own rule and the fact that this target is not in its permitted set may be as far as it can safely go — decide that explicitly rather than by accident. ## Why it is worth fixing An observer cannot read `CLAUDE.md`'s observer paragraph unless it happens to be in a repo that carries the block, and the canonical block says plainly: *"Don't infer what you can ask."* A refusal that misdescribes the rule is the one place the daemon actively teaches the wrong thing. The same applies to the collaborator rule, which is also target-dependent.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: fleet/fleetd#769