the lists re-read when the agent changes something

an agent deleted two proxy hosts and both stayed on screen. the person had the
answer in the transcript and a stale list six inches above it — which is the
one thing a chat beside a list has to get right, and the reason it is beside
the list at all.

so the chat panel bumps the refresh channel when a turn ran tools, and every
section re-reads. gated on hadToolCalls: a turn that only talked changed
nothing, and re-reading after every sentence would put six requests behind
every reply.

the channel is renamed with it. it was proxy-manager:connection-changed, named
after its only publisher; it has two now and the second is the bigger one, so
it says what it MEANS — something at NPM changed, read it again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
This commit is contained in:
2026-08-25 19:03:35 +00:00
co-authored by Claude Opus 5
parent 417e091dfe
commit 66383aa9a4
8 changed files with 57 additions and 29 deletions
+14 -9
View File
@@ -2,7 +2,12 @@ import { useCallback, useRef } from 'react';
import { defineChannel } from 'hooks/usePanelChannel';
/**
* "The connection changed — anyone reading NPM should read it again."
* "Something at NPM changed — anyone reading it should read it again."
*
* Two publishers, and naming it after the second one would have been wrong the other way round. The
* connection being saved or removed is one. **A chat turn that ran tools is the other**, and it is the
* bigger one: an agent that deletes a proxy host leaves every list on the screen describing a world that
* no longer exists, and nothing else would ever tell them.
*
* ── Why a channel and not the URL ──
*
@@ -12,11 +17,11 @@ import { defineChannel } from 'hooks/usePanelChannel';
*
* ── Why it is needed at all ──
*
* The panels are siblings in a workspace layout. `ConnectionPanel` cannot call into `CertificatesPanel`
* and should not try — they are mounted independently and neither owns the other. Before this, saving a
* connection left both panels showing whatever they had failed to load a minute earlier, and the only fix
* a person had was a manual refresh. Reported on 2026-08-25 the first time a connection was saved through
* the form.
* The panels are siblings in a workspace layout. None of them can call into another and none should try —
* they are mounted independently and no one owns the rest. Before this, saving a connection left every
* panel showing whatever it had failed to load a minute earlier, and the only fix a person had was a
* manual refresh. Reported on 2026-08-25 the first time a connection was saved through the form; the chat
* publisher was added the same day, after an agent deleted two hosts that stayed on screen.
*
* ── The nonce is a plain counter, and that is not an accident ──
*
@@ -26,12 +31,12 @@ import { defineChannel } from 'hooks/usePanelChannel';
* re-reads. `Date.now()` has the same flaw in miniature, two bumps in one millisecond being one number.
* A module counter never reads React state, so it cannot be wrong however often it is called.
*/
const useConnectionChangedChannel = defineChannel<number>('proxy-manager:connection-changed', 0);
const useNpmChangedChannel = defineChannel<number>('proxy-manager:npm-changed', 0);
let nonce = 0;
export const useConnectionChanged = () => {
const [signal, setSignal] = useConnectionChangedChannel();
export const useNpmChanged = () => {
const [signal, setSignal] = useNpmChangedChannel();
// The setter is a fresh closure every render and this `bump` goes into dependency lists, so it is read
// through a ref — stable identity without freezing the first render's setter.