Files
stack/agents/dewey/work/chat-02/BRIEF-r2-ed177bf6.md
T
jason.woltjeandClaude Opus 5.5 993673865a docs(records): Jason's walkthrough rulings, Sage moved to SetSpark, CHAT-02 go
Jason ruled on seven open items (20:27Z-20:45Z): seat Gitea tokens read in
place, one Discord restart after 6b with row 25 live, row 8 limited to the
dev seats, DYOR in dyor-stack-v4 with Sage moved to SetSpark, skills/aws-*
excluded locally, no second WebUI return defect, and go on CHAT-02 only.

Sage persona files name SetSpark as its business work. Darkwing's SOUL drops
harness names that were wrong for T3. DEFERRED adds the slash-prefix paste
hazard and the board Host/Origin gap, and moves the ledger T3 item to Done.
Dewey's approved CHAT-02 brief (636b0fac) and Filbert's review are recorded.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 15:35:15 -05:00

202 lines
11 KiB
Markdown

# CHAT-02 brief: read-only histories and Age (#1507, row 5)
Author: Dewey, 2026-09-26. R2: Sage's decisions on D1-D5 are recorded in §3
(R1 frozen as `BRIEF-r1-314da8b0.md`). For Filbert's review. No source edits.
Plan row: `docs/plans/2026-09-13_webui-session-chat.md` line 214. Depends on
CHAT-01 (`28d4e98a`) and uses the CHAT-01C companion (`b023841c`).
## 1. Is there a second defect in the return path?
Short answer: no second defect in the return path shows up for repository Pi
seats. The answers reach the transcript, the board reads them, and the Console
shows them. What Jason hit on 09-13 is best explained by the product gap: the
Console offered one "Last assistant text" field, clipped at 240 characters, in
an inspector, with no reply thread and no pending signal. 42c08d52 fixed the
pending display. The clip and the missing thread are CHAT-02's job.
I found one real defect on the send side, not the return side (§1.3).
### 1.1 Evidence so far
1. **Transcripts.** I scanned every `.pi/state/*/sessions` file (darkwing,
dewey, filbert, researcher) for board sends. The script is
`evidence/sends.mjs` and the output is `evidence/board-sends-repo-pi.jsonl`.
There are 30 sends and 29 have a final answer in the same file. Latency is
1.5 s minimum, 14.6 s median and 1019 s maximum. In 16 of the 29 answers
the text exceeds 240 characters, so the Console showed them clipped.
2. **The unanswered send.** Filbert, 2026-09-12T16:33:17Z. The seat started
working on it: it read files until 16:35:44Z, the last entry is an
`aborted` assistant turn, and a new filbert session began at 16:36:26Z. The
seat was relaunched mid-turn. That lost the answer, but the loss is in the
seat, not in the return path.
3. **Jason's report window.** At 2026-09-13T00:49:59Z he asked dewey "What
are the Gate E criteria?". The answer landed at 00:50:07Z with 365
characters, clipped to 240 in the Console. He wrote the report at 00:56:51Z.
The report says the sessions "are all cards within the project dashboard and
not available as independent chat interfaces". That describes the missing
thread, not a missing answer.
4. **The 09-13 Console, replayed.** I extracted `ea00ec66` into a scratch
directory and ran the return-flow test with the pending and Age
assertions removed. It passed: the new answer arrived through the 10 s
poll, once, in the open inspector. That page did not drop answers.
5. **Jason's live send today.** Researcher, user entry at
2026-09-26T20:10:57.429Z, answer "pong" at 20:11:06.173Z (8.7 s). At
20:15:18Z `/api/board` showed the researcher row as `idle` with
`lastAssistantText: "pong"`, `lastActivity` 20:11:06.173Z, the same
`sessionFile`, and a live registration (`evidence/board-researcher-*.json`).
The live WebUI (pid 1266267, port 7330) serves `app.js` byte-identical to
HEAD (`d1a51646…`), so the 42c08d52 pending notice is live. The WebUI
server code has not changed since that process started on 09-13 16:19 CDT.
The board (pid 3977979, port 7331) started today at 15:03 CDT.
### 1.2 What is still unverified
- **What Jason's screen showed.** For today's send I have the transcript and
the board JSON, not the Console DOM or a screenshot. The one missing fact is
whether "pong" appeared in the open inspector without a manual refresh.
- **The 09-13 processes.** The board keeps no reply receipts and the 09-13
board and WebUI processes are gone. I cannot prove what those processes
served at 00:50Z. I can only show that the same code, replayed, works.
- **Fleet seats.** I did not scan `~/.mosaic` fleet transcripts. The rule for
this phase is to leave them alone, and CHAT-02 does not cover fleet
catalogues.
- **Clipping in a live send.** "pong" is 4 characters, so today's send does
not exercise the 240-character clip.
### 1.3 Send-side defect found in passing
Today's user entry starts with `/`: `/[dragon-lin:control-board ->
dragon-lin:researcher] ping`. `agent-send.sh` adds the header, so the `/` was
already sitting in the Pi editor when the paste arrived. `send-message.sh`
pastes into whatever is in the composer (`paste-buffer -p`, then Enter). It
does not clear it first. Here Pi treated the result as plain text. If the
composer had held a real command prefix, a board reply could have become a
slash command. This is the unmediated-ingress hazard that CHAT-01 assigns to
CHAT-03I (B3). `tools/tmux/**` is excluded from the WebUI plan, so I
record it and do not propose a fix here. Sage confirmed it as a safety gap
and is adding it to DEFERRED.md under CHAT-03I/B3. It gets one line on #1507.
There is no tools/tmux change now.
### 1.4 Evidence that settles it
One question for Jason settles the live case: **after today's ping, did
"pong" appear in the Console inspector without pressing Refresh?**
- If yes, there is no second defect. CHAT-02 proceeds as a product-gap fix.
- If no, `/api/board` had the answer, so the defect is in the Console or the
WebUI proxy. Then capture the Console DOM and the network log for
`/api/board` during one send.
- If `/api/board` had lacked it, the defect would be in the scanner (wrong
newest file). If the transcript had lacked it, the defect would be in the
transport or the seat. Neither happened today.
To make the check complete for the clip, one more live send can ask for an
answer over 240 characters. Every step above then repeats, and the Console
should show the text ending in "…". That proves the clip is what cut the
09-13 answer short.
## 2. Scope
CHAT-02 per the plan: both harness catalogues, safe full branch history with
pagination and cursors, separate timestamps, relative Age from last
activity, and fixtures for malformed, truncated, replaced and touched logs,
cross-project and symlink denial, and no log writes. Read-only: opening a
conversation never resumes, forks, launches or controls anything.
### 2.1 Backend: `packages/conversation/**`
Dewey authors it (D4).
- **Catalogue.** Approved source roots only: the board's repository specs
(`<repo>/.pi/state/<seat>/sessions`) and live seat registrations. There is
no global scan and no browser-supplied path. Each conversation gets an
opaque ID mapped server-side to a file. Catalogue creation time, engine
launch time and last activity are separate nullable fields.
- **No-write Pi parser.** Never `SessionManager.open`, which can migrate old
files (CHAT-00 line 67). Open read-only. Record size, mtime and inode
before and after each read, and the fixture asserts they are unchanged.
- **Branches.** Build the `id`/`parentId` tree and select a leaf explicitly.
The default leaf is the last appended entry, which the implementer must
confirm against the pinned Pi session docs. Other leaves are read-only
branches.
Compaction entries render as markers. Pi `get_entries` order is not a
branch transcript (CHAT-00 line 66), so the parser does not use it.
- **Pages.** CHAT-01 limits: at most 100 parts, 8 MiB serialized UTF-8, 64
blocks per part and 262144 characters per string. Oversize content splits
into continuation parts and is never clipped. The cursor binds actor,
purpose, conversation, branch, snapshot, source epoch and expiry. A
replaced file (new inode or a shorter length) is a new source epoch, so old
cursors refuse and the client keeps its view with a reconcile marker. It
never silently switches files.
- **Damaged input.** A malformed line becomes an unavailable part with its
position, and reading continues. A truncated trailing line counts as
incomplete, not as an error.
- **Denials.** A symlink anywhere under the root, a path that resolves
outside the root, a parent-session reference, or a conversation from
another project all refuse, with fixtures.
### 2.2 Console: `packages/webui/**` (Dewey)
This is the smallest piece that answers the 09-13 complaint: a read-only
conversation view per session, opened from the card, table or inspector. It
renders the selected branch in full, unclipped: user text, assistant text,
tool calls and results collapsed, thinking hidden by default, and Markdown as
untrusted text with no active HTML. Age stays as 42c08d52 shipped it. Reply
keeps the existing board path unchanged. The view gets the new answer through
polling, as the inspector does now. Streaming belongs to CHAT-03.
The full chat UI (sidebar, composer, queue, approvals, uploads) stays in
CHAT-05.
### 2.3 Return-flow regression (required by the plan)
Extend `packages/webui/tests/return-flow.test.mjs`, or add a sibling test.
Send from the conversation view. Then the seat appends user, toolCall,
toolResult and a final answer longer than 240 characters. Assert that the
whole answer appears once, unclipped, in the same open view, with no manual
refresh, and that the draft and caret survive. Add a delayed-result variant
where the toolResult lands after a poll. This covers the Pi engine only; see
D2.
## 3. Decisions (Sage, 2026-09-26)
- **D1: moved.** CHAT-01 line 342 had deferred "the actual
execution/writer-claim record" to CHAT-02. CHAT-01C line 217 had deferred
R3-1, reconciling dispatched but unconsumed input, to "CHAT-02 adapter
evidence". A read-only reader needs neither, so both go to CHAT-03, which
owns binding and the single writer. This is noted on #1507.
- **D2: accepted.** Pi ships in CHAT-02. The Claude catalogue refuses
`unsupported-harness` under a fixture until B1 has evidence (Claude's
persisted branch format and leaf selection, CHAT-00 line 66). This narrows
the plan's "both harnesses". Sage records the scope change in the lead
decisions file.
- **D3: accepted.** `packages/conversation` is a library with no server. The
board adds two read-only routes, catalogue and page, in
`packages/control-board/src/serve.mjs` and `scan.mjs`. Darkwing reviews
that change before it lands. The coordination note goes to Darkwing when
the backend reaches review, not before.
- **D4: Dewey authors both.** The backend comes first, then the Console
against its fixtures. Filbert reviews this brief now and the code after.
- **D5: stays with Jason.** Sage set the live reply test as the condition,
and it passed. Sage is asking Jason for the go on CHAT-02. No code goes
under `packages/conversation` until Sage relays his answer.
## 4. Acceptance
- The new `packages/conversation` tests cover every fixture in §2.1, including
a before/after hash, mtime and inode check proving no log writes.
- The return-flow regression from §2.3 passes on the served WebUI.
- These still pass: `node docs/plans/chat-00/check.mjs`, `chat-01/check.mjs`,
`chat-01c/check.mjs`, and the control-board, webui and seat suites.
- Browser evidence: conversation view at 320 and 1440 in both themes, with a
long answer, a tool call, a malformed-line marker and a stale-cursor
reconcile marker. No horizontal overflow at 320.
- Filbert approves the exact candidate hashes. Live check: one board send
with a long answer, visible in full in the view.
## 5. Not in scope
Live adapters, control, streaming, queues, uploads, approvals, fleet seats,
Claude history until B1, any change to `tools/tmux/**`, `roles/**` or
session logs, and the `/` paste hazard (CHAT-03I).