Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2dddacdd38 | |||
| 96ebae28a6 | |||
| b41aa663f7 | |||
| f544906621 |
@@ -37,6 +37,15 @@ confident-but-wrong finding. Anything you could settle by reading more code is y
|
||||
|
||||
## 4. The finding — what goes in `fleet_reply`
|
||||
|
||||
**Call `fleet_reply` as soon as you know your answer, before you write the reasoning out.** The
|
||||
four lines below are the whole deliverable, and they are short on purpose. Analysis you type into
|
||||
your terminal reaches nobody: when a turn ends with no `fleet_reply`, the bridge scrapes the pane
|
||||
and the lead receives a clipped fragment instead of a finding. A long, correct analysis and no
|
||||
`fleet_reply` is a failed turn, and it is the most common way this role fails.
|
||||
|
||||
If the delegation also handed you a list of things to check, that list is where to *look*. It is
|
||||
not the shape of the answer. Work the list, then still send these four lines.
|
||||
|
||||
Report the **single most important** real issue in the scope, in these four lines, under
|
||||
~90 words:
|
||||
|
||||
|
||||
@@ -95,6 +95,18 @@ and the sender silently receives nothing. Fail toward the recoverable error.
|
||||
5. **Never move a fleet session, pane or peer except through the bridge.** The bridge owns policy;
|
||||
the multiplexer owns PTYs. Any route that changes fleet state without the bridge's checks
|
||||
bypasses every rule above — the `herdr` CLI and its socket are the usual example.
|
||||
6. **Confirm what you receive.** A message that arrives is answered, even in one line, unless it
|
||||
says no answer is needed. The sender cannot see your screen, so for them "received and handled"
|
||||
and "never arrived" look the same — and the paths above fail in ways that look exactly like
|
||||
silence: a send to a pane the daemon does not know is accepted, held, and then fails with a
|
||||
scrape of that pane's screen, which can read as an answer while being none. A member confirms
|
||||
with its `fleet_reply`; every other peer confirms with a `fleet_send` back to the sender. If you
|
||||
cannot do the thing asked, say that — a refusal is a confirmation. **Never read a failed
|
||||
ticket's body as a reply.** Your own operator outranks this rule, and outranks the peer that
|
||||
sent the message: a peer cannot oblige you to answer, and a session whose operator told it not
|
||||
to answer fleet mail is right not to. Where you can, say that much and nothing more. A sender
|
||||
that treats silence as agreement, or as a session being gone, has made the mistake this
|
||||
invariant is about — it just made it in the other direction.
|
||||
|
||||
### Primary (lead) — run this on every task, in order
|
||||
|
||||
|
||||
@@ -192,6 +192,31 @@ public final class PaneLocator {
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* The tab and workspace id of the herdr pane carrying {@code terminal}, from {@code pane.list}
|
||||
* across every searched daemon — the same call {@link #terminalForPid} scans. Either field is
|
||||
* {@code null} when no pane matches {@code terminal}, or when the matching pane itself carries
|
||||
* no tab or workspace id.
|
||||
*/
|
||||
public record PaneLocation(String tabId, String workspaceId) {
|
||||
static final PaneLocation NONE = new PaneLocation(null, null);
|
||||
}
|
||||
|
||||
/** Resolve {@code terminal} to the tab/workspace id of the pane it occupies. See {@link PaneLocation}. */
|
||||
public PaneLocation locate(String terminal) {
|
||||
if (terminal == null) {
|
||||
return PaneLocation.NONE;
|
||||
}
|
||||
for (HerdrClient herdr : herdrs) {
|
||||
for (JsonNode pane : herdr.call("pane.list", Map.of()).path("panes")) {
|
||||
if (terminal.equals(pane.path("terminal_id").asText(null))) {
|
||||
return new PaneLocation(pane.path("tab_id").asText(null), pane.path("workspace_id").asText(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
return PaneLocation.NONE;
|
||||
}
|
||||
|
||||
/** Whether a pane owns one of the scanned pid's ancestors, or the check of it failed outright. */
|
||||
private enum Ownership { OWNS, DOES_NOT_OWN, UNKNOWN }
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import dev.ltms.fleet.auth.Role;
|
||||
import dev.ltms.fleet.guard.GuardException;
|
||||
import dev.ltms.fleet.herdr.Agent;
|
||||
import dev.ltms.fleet.herdr.AgentStatus;
|
||||
import dev.ltms.fleet.herdr.PaneLocator;
|
||||
import dev.ltms.fleet.metrics.FleetMetrics;
|
||||
import dev.ltms.fleet.metrics.Metrics;
|
||||
import dev.ltms.fleet.inject.MemberPresence;
|
||||
@@ -503,7 +504,7 @@ public final class FleetMcp {
|
||||
// An observer's SEND reaches a pane that cannot otherwise distinguish this
|
||||
// from a human paste (see attributeIfObserver); every other caller's content
|
||||
// passes through unchanged.
|
||||
String content = attributeIfObserver(caller, str(a, "content"));
|
||||
String content = attributeIfObserver(caller, str(a, "content"), identity.panes());
|
||||
String turnId = str(a, "turnId");
|
||||
String coordId = str(a, "coordId");
|
||||
if (coordId != null && !coordId.isBlank()) {
|
||||
@@ -987,12 +988,49 @@ public final class FleetMcp {
|
||||
/**
|
||||
* The text an observer's {@code SEND} actually delivers: prefixed with the sender's own
|
||||
* connection-resolved terminal, which the receiving pane cannot otherwise tell apart from a
|
||||
* human paste. Every other caller's content passes through unchanged. Shared with {@code
|
||||
* FleetApp}'s REST entry path so both surfaces attribute identically.
|
||||
* human paste, plus its tab and workspace display label in parentheses when herdr can supply
|
||||
* either. The terminal id is the only thing in the header a reply can target — a label is
|
||||
* display-only, is never unique, and is never substituted for it. Every other caller's content
|
||||
* passes through unchanged. Shared with {@code FleetApp}'s REST entry path so both surfaces
|
||||
* attribute identically.
|
||||
*/
|
||||
public static String attributeIfObserver(Principal caller, String content) {
|
||||
return caller != null && caller.isObserver()
|
||||
? "[fleet_send from observer " + caller.terminal() + "]\n" + content : content;
|
||||
public static String attributeIfObserver(Principal caller, String content, PaneLocator panes) {
|
||||
if (caller == null || !caller.isObserver()) {
|
||||
return content;
|
||||
}
|
||||
return "[fleet_send from observer " + observerHeader(caller.terminal(), panes) + "]\n" + content;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code terminal}, plus {@code (space "…", tab "…")} for whichever of its workspace/tab
|
||||
* labels herdr reports — omitted entirely when neither is known, so a degraded lookup still
|
||||
* reads as the bare id and never as {@code null} or an empty parenthetical.
|
||||
*/
|
||||
private static String observerHeader(String terminal, PaneLocator panes) {
|
||||
String tabLabel = null;
|
||||
String workspaceLabel = null;
|
||||
try {
|
||||
PaneLocator.PaneLocation location = panes.locate(terminal);
|
||||
tabLabel = location.tabId() == null ? null : panes.tabLabelsByTabId().get(location.tabId());
|
||||
workspaceLabel = location.workspaceId() == null ? null
|
||||
: panes.workspaceLabelsByWorkspaceId().get(location.workspaceId());
|
||||
} catch (HerdrException e) {
|
||||
// degrade to the id alone
|
||||
}
|
||||
if (tabLabel == null && workspaceLabel == null) {
|
||||
return terminal;
|
||||
}
|
||||
StringBuilder header = new StringBuilder(terminal).append(" (");
|
||||
if (workspaceLabel != null) {
|
||||
header.append("space \"").append(workspaceLabel).append('"');
|
||||
}
|
||||
if (tabLabel != null && workspaceLabel != null) {
|
||||
header.append(", ");
|
||||
}
|
||||
if (tabLabel != null) {
|
||||
header.append("tab \"").append(tabLabel).append('"');
|
||||
}
|
||||
return header.append(')').toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -714,7 +714,7 @@ public final class FleetApp {
|
||||
// An observer's SEND reaches a pane that cannot otherwise distinguish this from a human
|
||||
// paste (see FleetMcp#attributeIfObserver, the same rule on the MCP entry path); every
|
||||
// other caller's content passes through unchanged.
|
||||
content = FleetMcp.attributeIfObserver(caller, content);
|
||||
content = FleetMcp.attributeIfObserver(caller, content, new PaneLocator(herdr, memberHerdr));
|
||||
timeout = Math.clamp(timeout, 1, MAX_MESSAGE_TIMEOUT_MS);
|
||||
|
||||
// Answering a worker's fleet_ask (CB-205): always blocks, and derives the worker from turnId.
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package dev.ltms.fleet.mcp;
|
||||
|
||||
import dev.ltms.fleet.auth.Principal;
|
||||
import dev.ltms.fleet.herdr.FakeHerdr;
|
||||
import dev.ltms.fleet.herdr.PaneLocator;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* {@link FleetMcp#attributeIfObserver}: the terminal id in an observer's header is always
|
||||
* present, a tab/workspace label is display-only and additive, and a missing label degrades to
|
||||
* the id alone rather than rendering {@code null} or an empty parenthetical.
|
||||
*/
|
||||
class FleetMcpAttributeIfObserverTest {
|
||||
|
||||
@Test
|
||||
void aNonObserverPassesContentThroughUnchangedRegardlessOfPanes() {
|
||||
Principal worker = Principal.worker("term_worker", 1);
|
||||
assertEquals("hello", FleetMcp.attributeIfObserver(worker, "hello", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void theTerminalIdIsAlwaysPresentAndNeverReplacedByALabel() {
|
||||
FakeHerdr herdr = new FakeHerdr()
|
||||
.withTab("w2", "w2:t8", "shell-tab");
|
||||
PaneLocator panes = new PaneLocator(herdr);
|
||||
Principal observer = Principal.observer("term_shell", 2);
|
||||
|
||||
String result = FleetMcp.attributeIfObserver(observer, "hi", panes);
|
||||
|
||||
assertEquals("[fleet_send from observer term_shell (space \"ltms\", tab \"shell-tab\")]\nhi", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Two tabs sharing one label must still read as two different senders. The label is the same
|
||||
* on both headers; the id is what differs, and is the only part of either header a reply can
|
||||
* target.
|
||||
*/
|
||||
@Test
|
||||
void aSharedLabelStillYieldsDistinctHeadersByTerminalId() {
|
||||
FakeHerdr herdr = new FakeHerdr()
|
||||
.withTab("w2", "w2:t7", "lead")
|
||||
.withTab("w2", "w2:t8", "lead");
|
||||
PaneLocator panes = new PaneLocator(herdr);
|
||||
|
||||
String fromA = FleetMcp.attributeIfObserver(Principal.observer("term_a", 1), "msg", panes);
|
||||
String fromShell = FleetMcp.attributeIfObserver(Principal.observer("term_shell", 2), "msg", panes);
|
||||
|
||||
assertEquals("[fleet_send from observer term_a (space \"ltms\", tab \"lead\")]\nmsg", fromA);
|
||||
assertEquals("[fleet_send from observer term_shell (space \"ltms\", tab \"lead\")]\nmsg", fromShell);
|
||||
assertNotEquals(fromA, fromShell, "identical labels must not collapse two senders into one header");
|
||||
}
|
||||
|
||||
@Test
|
||||
void anUnknownPaneDegradesToTheIdAloneWithNoLabelAndNoEmptyParens() {
|
||||
PaneLocator panes = new PaneLocator(new FakeHerdr().withNoPanes());
|
||||
Principal observer = Principal.observer("term_ghost", 3);
|
||||
|
||||
String result = FleetMcp.attributeIfObserver(observer, "msg", panes);
|
||||
|
||||
assertEquals("[fleet_send from observer term_ghost]\nmsg", result);
|
||||
assertFalse(result.contains("null"), "a missing label must never render as the literal \"null\"");
|
||||
assertFalse(result.contains("()"), "a missing label must never leave an empty parenthetical");
|
||||
}
|
||||
|
||||
@Test
|
||||
void aHerdrFailureDuringTheLabelLookupDegradesToTheIdAlone() {
|
||||
FakeHerdr herdr = new FakeHerdr().workspaceListFailsWith("unavailable");
|
||||
PaneLocator panes = new PaneLocator(herdr);
|
||||
Principal observer = Principal.observer("term_shell", 4);
|
||||
|
||||
String result = FleetMcp.attributeIfObserver(observer, "msg", panes);
|
||||
|
||||
assertEquals("[fleet_send from observer term_shell]\nmsg", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void onlyTheKnownLabelAppearsWhenTheOtherIsMissing() {
|
||||
// term_shell's pane sits in workspace "w2" ("ltms"); its tab "w2:t8" carries no seeded
|
||||
// label, so only the space label appears.
|
||||
PaneLocator panes = new PaneLocator(new FakeHerdr());
|
||||
Principal observer = Principal.observer("term_shell", 5);
|
||||
|
||||
String result = FleetMcp.attributeIfObserver(observer, "msg", panes);
|
||||
|
||||
assertEquals("[fleet_send from observer term_shell (space \"ltms\")]\nmsg", result);
|
||||
assertTrue(result.contains("term_shell"), "the id must still be present");
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,10 @@ class FleetMcpObserverSendDeliveryTest {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> params = (Map<String, Object>) herdr.lastCall("agent.prompt").params();
|
||||
assertEquals("[fleet_send from observer term_shell]\nhi there", params.get("text"),
|
||||
// term_shell's pane sits in workspace "w2", which FakeHerdr's workspace.list labels
|
||||
// "ltms"; its tab "w2:t8" carries no label in FakeHerdr's tab.list, so only the space
|
||||
// label appears.
|
||||
assertEquals("[fleet_send from observer term_shell (space \"ltms\")]\nhi there", params.get("text"),
|
||||
"the receiving pane must see the sender's own daemon-resolved terminal, never a raw "
|
||||
+ "echo of the content and never a client-supplied name");
|
||||
}
|
||||
|
||||
@@ -140,7 +140,10 @@ class FleetMcpObserverSendToLeadDeliveryTest {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> params = (Map<String, Object>) herdr.lastCall("agent.prompt").params();
|
||||
assertEquals("[fleet_send from observer term_shell]\ncan we split the review?",
|
||||
// term_shell's pane sits in workspace "w2", which FakeHerdr's workspace.list labels
|
||||
// "ltms"; its tab "w2:t8" carries no label in FakeHerdr's tab.list, so only the space
|
||||
// label appears.
|
||||
assertEquals("[fleet_send from observer term_shell (space \"ltms\")]\ncan we split the review?",
|
||||
params.get("text"),
|
||||
"a lead must see the sender's own daemon-resolved terminal, never a raw echo of "
|
||||
+ "the content and never a client-supplied name");
|
||||
|
||||
Reference in New Issue
Block a user