Stop announcing the shared conversation

2c15c6ed208d · AtlantisPleb · · parent 0b42ec5004d8

Stop announcing the shared conversation

I asked for this note when the coder's turns were landing in the account's one
conversation and nothing on screen said so. It answered the wrong question. The
conversation is shared because a thread does not exist yet, and a banner at the
top of every session made a temporary limitation look like the design.

The thread is the unit of agent work — `docs/taxonomy.md` says so and
`docs/2026-08-23-thread-primitive-audit.md` records what building it takes.
Advertising the constraint we are removing teaches the wrong model to everyone
who reads it, and it is the first thing a new user sees.

The `scopeNotice` seam goes with it, in the interface and in the piped path.
When a thread exists there will be nothing to announce, because the session
will be private to the thread that owns it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTmy4SEXrHXouw5sZbs3f4
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>

Deploy story

What this commit did to the running system — joined from the forge receipt chain, the part a commit page elsewhere cannot show.

Not deployed through the forge lane

No push, promotion, build, or deploy receipt references this commit (receipts are scanned over a bounded recent window). Changes shipped by full node replacement carry their proof in the release gate receipt instead.

Changed files

  • modified packages/openagents-cli/src/coder-chat-api.ts
  • modified packages/openagents-cli/src/coder-plain.ts
  • modified packages/openagents-cli/src/coder-session.ts
  • modified packages/openagents-cli/src/coder-ui.ts
  • modified packages/openagents-cli/test/coder-chat-api.test.ts
  • modified packages/openagents-cli/test/coder-session.test.ts
  • modified packages/openagents-cli/test/coder-ui.test.ts

Diff

7 files changed, +0 -69

packages/openagents-cli/src/coder-chat-api.ts modified -13

@@ -86,19 +86,6 @@ export class ChatApiUnavailable extends Error {

86 86
export class ChatApiReplySource {
87 87
  private backend: CoderBackend;
88 88
89
  /**
90
   * The server records one conversation per account, so a turn submitted here
91
   * lands in the same conversation `/chat` writes to and every earlier
92
   * `openagents coder` run wrote to. That is why the model remembers a
93
   * question this terminal never asked, and there is nothing else on screen
94
   * that would tell a reader so. The Thread work replaces this; until a thread
95
   * exists the interface must not imply the session is private to this
96
   * terminal.
97
   */
98
  readonly scopeNotice =
99
    "This conversation is the account's one conversation, shared with /chat " +
100
    "and with earlier coder runs, so the model remembers turns from all of them.";
101
102 89
  constructor(private readonly options: ChatApiOptions) {
103 90
    this.backend = options.backend ?? defaultBackend();
104 91
  }
packages/openagents-cli/src/coder-plain.ts modified -6

@@ -65,12 +65,6 @@ export async function runCoderPlain(

65 65
    }
66 66
  };
67 67
68
  // Where the turns are recorded is a property of the session, not of the
69
  // interface, so the piped path says it too rather than leaving it to the
70
  // one reader who happens to be on a TTY.
71
  const scope = session.snapshot().scope;
72
  if (scope !== undefined) stdout.write(`${scope}\n`);
73
74 68
  const unsubscribe = session.onChange(flush);
75 69
  flush();
76 70
packages/openagents-cli/src/coder-session.ts modified -7

@@ -70,11 +70,6 @@ export interface CoderSnapshot {

70 70
   * this process never saw.
71 71
   */
72 72
  readonly turns: number;
73
  /**
74
   * What a reader needs to know about where this source records its turns, or
75
   * undefined when there is nothing to say. Shown once, at the start.
76
   */
77
  readonly scope: string | undefined;
78 73
}
79 74
80 75
/** Where reply chunks come from. One implementation today; ACP is the next. */

@@ -87,7 +82,6 @@ export interface ReplySource {

87 82
   * leaves it unset; a source that writes into a conversation shared with
88 83
   * another surface has to say so, because nothing else on screen would.
89 84
   */
90
  readonly scopeNotice?: string;
91 85
  /**
92 86
   * Move to the next backend and return its new label.
93 87
   *

@@ -225,7 +219,6 @@ export class CoderSession {

225 219
      branch: this.branch,
226 220
      model: this.source.model,
227 221
      turns: this.turnCount,
228
      scope: this.source.scopeNotice,
229 222
    };
230 223
  }
231 224
packages/openagents-cli/src/coder-ui.ts modified -2

@@ -609,8 +609,6 @@ export function runCoderUi(session: CoderSession, options: CoderUiOptions): Prom

609 609
      "openagents coder — development build. Type a message and press enter. " +
610 610
        "Ctrl+D quits, Esc interrupts a reply.",
611 611
    );
612
    const scope = session.snapshot().scope;
613
    if (scope !== undefined) session.notice(scope);
614 612
    render();
615 613
  });
616 614
}
packages/openagents-cli/test/coder-chat-api.test.ts modified -7

@@ -336,11 +336,4 @@ describe("ChatApiReplySource", () => {

336 336
    expect(new Set(labels).size).toBe(CODER_BACKENDS.length);
337 337
    expect(cycling.model).toBe(defaultBackend().label);
338 338
  });
339
340
  it("says that its turns land in the account's one shared conversation", () => {
341
    // The server records one conversation per account, so this source cannot
342
    // let the interface imply the session is private to one terminal.
343
    expect(source().scopeNotice).toContain("/chat");
344
    expect(source().scopeNotice).toContain("remembers");
345
  });
346 339
});
packages/openagents-cli/test/coder-session.test.ts modified -12

@@ -223,18 +223,6 @@ describe("CoderSession", () => {

223 223
    expect(session.snapshot().turns).toBe(1);
224 224
  });
225 225
226
  it("carries the source's scope notice, and nothing when the source has none", () => {
227
    const local = new CoderSession(scripted([]), "repo", "main");
228
    expect(local.snapshot().scope).toBeUndefined();
229
230
    const shared = new CoderSession(
231
      { ...scripted([]), scopeNotice: "shared with /chat" },
232
      "repo",
233
      "main",
234
    );
235
    expect(shared.snapshot().scope).toBe("shared with /chat");
236
  });
237
238 226
  it("carries workspace and model into the snapshot for the status line", () => {
239 227
    const session = new CoderSession(new DummyReplySource(), "openagents", "main");
240 228
    const snapshot = session.snapshot();
packages/openagents-cli/test/coder-ui.test.ts modified -22

@@ -204,28 +204,6 @@ describe("runCoderUi", () => {

204 204
    expect(rows.at(-1)).toContain("1 reply this run");
205 205
  });
206 206
207
  it("says once where the turns are recorded when the source is not private", async () => {
208
    const stdin = new FakeIn();
209
    const stdout = new FakeOut();
210
    const session = new CoderSession(
211
      { ...source([{ type: "text", value: "hi" }]), scopeNotice: "shared with /chat" },
212
      "repo",
213
      "main",
214
    );
215
    const running = runCoderUi(session, {
216
      stdin: stdin as unknown as NodeJS.ReadStream,
217
      stdout: stdout as unknown as NodeJS.WriteStream,
218
    });
219
    await session.submit("go");
220
    const rows = screen(stdout.written);
221
    stdin.emit("data", "\x04");
222
    await running;
223
224
    const notes = rows.filter((row) => row.includes("shared with /chat"));
225
    expect(notes).toHaveLength(1);
226
    expect(notes[0]).toContain("note");
227
  });
228
229 207
  it("says nothing about scope when the source keeps its turns to itself", async () => {
230 208
    const { rows } = await drive([{ type: "text", value: "hello" }]);
231 209
    expect(rows.join("\n")).not.toContain("shared with");

This page updates live while a promote is in flight · changelog