Repository navigation
shell: delegation threads, a line each over the chat an agent asked from - #779
Conversation
Our Hermes image reads an agent's status for its title and its role
(`agentRole`, its fragment's `meta.role`) in one read, and tells it its
role between its name and its owner's home ("Your role is chief of
staff: what your owner made you for, as it is now."), none when it has
none, so an edit reaches its next turn. Its profile.yaml description,
what teammates read it does in Hermes' roster, is its role, else its
job's first line. Its job never copies the role.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Several agents, one UI for any number (Paul, 2026-10-30): each chat's row is two lines, its agent first (its face, a group's stacked; its name; its role's tag; what it is doing there, else when), then the chat's own name. "New chat with [agent]" names the open chat's agent, else the main one; with more than one agent its chip lists them all, the pick for that chat alone. The launcher's start row follows the same rule, with a pick beside it. The launcher's button is a search and its key, beside the brand. The main agent's pill and its menu go: what it is doing is on its chats' rows, its profile and screen in a chat's ⋯, its name and picture in Settings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Settings' Agents adds an agent with a name and a role, made as the first run makes its agent (one shared `makeAgent`): its own chat opens at once with the making's steps, and it says hello first. Its job names no role; its role is its fragment.json's meta.role, which its runtime tells it. Each agent's row has a Role field, written and put live as its name is. Add is offered while the computer runs fewer than 16 agents. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chat rows now say what their agent is doing, so a row whose agent finished its work while the list changed is rightly drawn again; every row that still says what it said stays the same element. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The stub image builds the fragment CLI by our Hermes image's own stage (cached by the same ids; xtask's guard reads both), its context now the repo's root as Hermes' is. Its script runtime's `ask <agent> <words>` runs `fragment ask --return-to <this chat>` as the agent acting for its owner, as Hermes' fragment_ask does, so the local lanes test an ask end to end on the real CLI path: the computers lane's lead asks its second agent, the question in their pair chat, its answer back as the lead's own turn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When one of the person's agents asks another (`fragment ask --return-to`), the chat it asked from shows one line per thread: the asked agent, the first line of what was asked, its state (Asked, Working, Needs you, Done, Stopped, Lost), how long, and Stop (the owner's `stop` in the pair chat). A line opens the pair chat, its transcript; pair chats leave the chat list. A line stays until its answer has come (its `an:<asked turn>` on the asker's tasks, and the asker's turn of it ended in the asking chat), so a thread stopped or lost is said, never dropped; the asking chat's row waits on the asked agent meanwhile. Nothing new is kept: a pair chat's listed records, and its newest records read when its row changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alexlwn123
left a comment
There was a problem hiding this comment.
Fail for now at bde93fd1ba22ced21b3622cc10528a5120870be7, reviewing this layer against #778 at 4194934e61d2df8dda17d6443f76226f3a692cfd.
The separate shell module and existing public APIs fit this feature. However, the cache invalidation assumes more ordering/history than listed records provide.
Blocking:
-
Coalesced work updates can leave a running thread at Asked, without Stop.
cell/shell/threads.js:62includes onlyturn.startandturn.endin the signature. Listed records contain each poster's newest record, and publication coalesces changes over three seconds (docs/api.md, Listed records). If the question is read before its turn starts, and the next published row already containsturn.steporturn.prompt, the signature remains unchanged.refreshskips the work read, the cached thread has no turn, andstateOfkeeps returning Asked even while an approval card waits. Including the latest work state in invalidation must account for starts/ends that were superseded before publication; the list is not an event stream. -
A newly discovered ended thread can inherit another thread's completed answer check.
cell/shell/threads.js:107-123keysanswerChecksonly by the asking chat's signature, not the turns being checked. With two asks returning to the same chat, the cached pair read can contain only the first end. If the asking chat publishes both completed answer turns before the pair publishes its second end, the pair signature still matches its cached read. It checks only the first ended thread and caches that asking-chat signature. When the next pair update discovers the second end, the asking chat is unchanged, so its answer is never checked. The second line stays Done (or Stopped/Lost) until unrelated activity or a page reload. Newly discovered ended turns need their own answer check even when that chat's signature is already cached.
I reproduced both against this head's actual createThreads using mocked public-API responses on the repository's pinned Node: prompt update produced no reread and remained Asked; delayed discovery of the second end left a completed line across repeated refreshes. Please cover these coalesced-update and interleaved-read cases alongside the existing sequential e2e cases.
Validation: the focused reproduction and module syntax check ran locally. CI check and images check pass; e2e shards and real-Hermes Docker are still pending. Full e2e was not run locally. The external engineering-style document named by AGENTS.md is absent on this machine.
The stub's CLI stage builds its dependencies first, from the manifests and the lock alone (our crates empty sources), so a change to the CLI, core or proto rebuilds only our crates; its target is the layers' own, so CI's BuildKit cache keeps the dependencies' build. Unoptimized (the stub's agents make a few calls with it): a third of the build time, a binary of 17 MB. xtask's guard reads its dependency-first form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alexlwn123
left a comment
There was a problem hiding this comment.
Fail for now at d6955c6deecb8f3ee3ed6fc39b64fa009365c1f8, reviewing this layer against #778 at e2e0c7ddf06efa5b1c0135e7d764252380a15601.
Neither blocker from my previous review is fixed. This head merges base-layer Docker/build changes; the shell implementation and its tests are unchanged.
Blocking:
-
Coalesced work updates still leave running threads at Asked without Stop.
cell/shell/threads.js:62ignores work records other thanturn.startandturn.end. If the question is cached before its turn starts and the next listed row already containsturn.promptorturn.step, the signature does not change.refreshskips the read, andstateOfreturns Asked because the cached turn is absent. Listed rows retain each poster's newest record and combine changes over three seconds (docs/api.md, Listed records), so observing every start is not guaranteed. Invalidate from the latest work state even when it supersedes a start/end. -
Newly discovered ended threads still inherit an earlier answer check.
cell/shell/threads.js:107-123caches checks only by return-chat signature. When two asks return to one chat, that chat can publish both completed answer turns while the pair cache contains only the first end. Checking that first thread caches the return-chat signature. When the pair later publishes the second end, the unchanged return-chat signature skips checking its answer, leaving its completed line visible until unrelated activity or reload. Include newly discovered ended turns in the check boundary even when the return-chat signature is unchanged.
Please cover coalesced work updates and interleaved pair/return-chat publication, alongside the sequential e2e cases. The separate module and existing public APIs remain an appropriate scope for this feature; no additional complexity objection.
Validation: rechecked the current-head source and listed-record contract, and confirmed the affected implementation/tests are byte-for-byte unchanged from the earlier review. I did not rerun the earlier mocked reproductions or full e2e. Current CI check, image checks, Docker and e2e shards are pending. The external engineering-style document is unavailable on this machine.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alexlwn123
left a comment
There was a problem hiding this comment.
Fail for now at d075a6a38894401cd51fafa6e254dcf6292b4476, against #778 at 1b14f3cc0c5bbfc46d4eeb0c88b4e8a9a158935d.
Neither previous blocker is fixed. The only tree change since my last verdict is in docs/computers.md; shell code and tests are unchanged.
Blocking:
-
Coalesced work updates leave running threads at Asked without Stop.
cell/shell/threads.js:62filters the invalidation signature toturn.start/turn.end. When the question is cached before its turn starts and the next listed row already containsturn.promptorturn.step, the signature stays unchanged, the work read is skipped, and the missing cached turn keeps the line at Asked. Listed records keep each poster’s newest record and coalesce publication over three seconds (docs/api.md, Listed records). Invalidation must handle work states that supersede starts/ends before publication. -
Newly discovered ended threads inherit an earlier answer check.
cell/shell/threads.js:107-123keys answer checks only by return-chat signature. With two asks returning to one chat, both answer turns can finish and publish while the pair cache contains only the first end. Checking that first thread caches the return-chat signature; discovering the second end later does not invalidate it. The completed second line remains until unrelated activity or reload. Newly discovered ended turns must receive their own check even when the return-chat signature is unchanged.
Please cover coalesced work updates and interleaved pair/return-chat publication. The separate module and public-API approach remain appropriate; no additional complexity objection.
Validation: rechecked both source paths and the listed-record contract at this head, and verified shell implementation/tests are unchanged from the previously reviewed head. I did not rerun the earlier reproductions or full e2e. CI check, image checks, Docker and e2e shards remain pending. The external engineering-style document named by AGENTS.md is unavailable on this machine.
…read's answer Listed records are each member's newest, sent at most once in 3 s, so a turn's start or end may be past before a row says it (#779 review, 1): a pair chat is read again whenever its row changes. An asking chat's answers are looked for again when another of its threads is seen to end, even if its own row is unchanged (#779 review, 2). cargo xtask check runs the repo's *.test.mjs on the pinned Node's own runner; cell/shell-tests/threads.test.mjs covers both cases (each failed before this change). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alexlwn123
left a comment
There was a problem hiding this comment.
Pass for this stack layer at ea17a8fb3dbbef67a5514e88cbc7b1ab94ebe070, against #778 at 1b14f3cc0c5bbfc46d4eeb0c88b4e8a9a158935d.
Both previous blockers are fixed: pair reads now invalidate on coalesced work updates beyond start/end, and answer checks include the ended turns being checked, so a newly discovered end cannot inherit an earlier check. The regression tests exercise both publication orderings.
The separate shell module and existing public APIs remain appropriate for delegation threads. The small Node test integration uses the existing file discovery and pinned runtime without adding dependencies. I see no remaining blocking regression in this layer.
Validation: all four tests in cell/shell-tests/threads.test.mjs pass locally on pinned Node v24.22.1. CI check passes; image checks, real-Hermes Docker and all four e2e shards remain pending. Full check/e2e were not run locally. The external engineering-style document named by AGENTS.md is unavailable on this machine.
Add kept its name and role in memory alone, and made its chat before its agent, so a reload in between left a chat nothing tied to them (#763 review). Add now makes its agent first, its title, job, tier and role written, then its chat: once its chat exists, all Add was asked for is on the platform. A page's start (resumeFirstRun, once no first run is owed) finds one of their chats, <label>-chat, with no message yet, whose agent <label> is not in it, and makes it on from there; in it but quiet, its hello is posted again (the same record). Cut before its chat, Add again with that name makes on the agent it made. cargo xtask check runs the repo's *.test.mjs on the pinned Node's own runner; cell/shell-tests/onboarding.test.mjs cuts each step, reloads, and checks one agent with its role in one chat (all seven reload cases failed before this change). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # xtask/src/js_syntax.rs
|
Thanks, both reproduced, and both fixed in ea17a8f. They reach this layer through 3bff24a, which merges #763's fix up the stack.
Tests:
Both fail on bde93fd's |
alexlwn123
left a comment
There was a problem hiding this comment.
Pass for this stack layer at 3bff24a77475d5cd331708f433a88bb2a3f3c609, against the incorporated #778 commit dce4c235015bd82bf6587593d29171a5b305f38d (that base branch has since advanced).
Both earlier blockers remain fixed: coalesced work updates invalidate pair reads, and newly discovered ended turns receive an answer check. The thread implementation is unchanged from my approved head ea17a8fb3dbbef67a5514e88cbc7b1ab94ebe070. This merge brings in the lower layer's Add-agent recovery and combines the two shell test suites correctly. The separate module and existing public APIs remain appropriate; I found no new blocking regression in this layer.
Validation: all 14 onboarding/thread tests pass locally on pinned Node v24.22.1. CI check, images check, real-Hermes Docker, and all four e2e shards are pending. Full check/e2e were not run locally. The external engineering-style document named by AGENTS.md is unavailable on this machine.
# Conflicts: # cell/shell-tests/onboarding.test.mjs # cell/shell/chat-list.js # cell/shell/onboarding.js # cell/shell/shell.css # cell/shell/shell.js # crates/e2e/src/lanes/shell.rs # xtask/src/js_syntax.rs
alexlwn123
left a comment
There was a problem hiding this comment.
Pass for this stack layer at c58cd3e1f470264104672de9958baa006753af73, against #778 at 6feb34f2b43f494475164bf3f4827dd3eef6b63b.
Both earlier blockers remain fixed: coalesced work updates invalidate pair reads, and newly discovered ended turns receive an answer check. The thread implementation and regression tests are unchanged from the previously approved head. I checked this merge's integration with the updated shell and base-layer changes and found no new blocking regression in this layer. The separate module and existing public APIs remain appropriate; no additional complexity concern.
Validation: all 15 shell onboarding/thread tests pass locally on pinned Node v24.22.1. CI check, images check and all four e2e shards are pending. Full check/e2e were not run locally. The external engineering-style document named by AGENTS.md is unavailable on this machine.
alexlwn123
left a comment
There was a problem hiding this comment.
Pass for this stack layer at 75625fd166e153daae10e1c62da96d073962e608, against #778 at b201eff549c575e6bd883a00bb518cbe50be1210.
Both earlier blockers remain fixed: coalesced work updates invalidate pair reads, and newly discovered ended turns receive an answer check. The shell implementation and regression tests are unchanged from my previously approved head. I checked the merged integration and found no new blocking regression in this layer. The separate module and existing public APIs remain appropriate; no additional complexity concern.
Validation: all 15 shell onboarding/thread tests pass locally on pinned Node v24.22.1; CI check passes. The images check fails in simplex::run::tests::no_unsettled_cancel_is_let_go (43 cancels retained versus 64 expected), in bridge code inherited unchanged from the base; this layer changes neither the image workspace nor its workflow. Real-Hermes Docker and all four e2e shards remain pending. Full check/e2e were not run locally. The external engineering-style document named by AGENTS.md is unavailable on this machine.
alexlwn123
left a comment
There was a problem hiding this comment.
Pass for this stack layer at 6a61bf14166ab7a2402493211ff869c048f3dd47, against #778 at edb1daf8bb72e684fdb836abf8a2a4e61871eebb.
Both earlier blockers remain fixed: coalesced work updates invalidate pair reads, and newly discovered ended turns receive an answer check. The thread implementation and regression tests are unchanged from my previously approved head. I checked the merge integration with the updated shell layout and e2e code and found no new blocking regression in this layer. The separate module and existing public APIs remain appropriate; no additional complexity concern.
Validation: all 15 shell onboarding/thread tests pass locally on pinned Node v24.22.1. CI check, images check, real-Hermes Docker and all four e2e shards remain pending. Full check/e2e were not run locally. The external engineering-style document named by AGENTS.md is unavailable on this machine.
|
Merged master in (019df47) after #763 and #778 merged, since the Session Archive had landed. Two conflicts, both additive, both sides kept:
No threads code changed. |
alexlwn123
left a comment
There was a problem hiding this comment.
Pass at 019df471add4409e7935377647757a5f2936042e, against master at 90fa6b883aee5fd73bc7e3479f62b1626c7d16f5.
Both earlier blockers remain fixed: coalesced work updates invalidate pair reads, and newly discovered ended turns receive an answer check. The thread module and regression tests are unchanged from my previously approved head. I checked the merge integration with the Session Archive, updated chat-row state handling, release assets and e2e code and found no new blocking regression. The separate module and existing public APIs remain appropriate; no additional complexity concern.
Validation: all 15 shell onboarding/thread tests pass locally on pinned Node v24.22.1; CI check passes. Images check, real-Hermes Docker and all four e2e shards remain pending. Full check/e2e were not run locally. The external engineering-style document named by AGENTS.md is unavailable on this machine.
Step 3 of agents and threads (Paul, 2026-10-30): delegation threads in the shell. It stacks on the stub-asks PR, which lets the local lanes test asks end to end.
What the person sees
fragment ask --return-to, as Hermes'fragment_askand the stub'saskdo), the chat it asked from shows one line per thread above the chat: the asked agent's face and name, the first line of what was asked, its state, how long it has run, and Stop while it runs.fragment asklabels a pair. A person's own group chats stay listed.an:<asked turn>) is on the asker'stasks, and the asker's turn of it has ended in the asking chat. So a thread that's stopped or lost is always shown, never dropped.stopcommand in the pair chat (stop:<turn>). The person's own Stop there would belong to its asker, an agent, and be ignored.No new platform state. A thread's state comes from the pair chat's listed records, live on the list socket. The question, its time, its
return_toand its turn's end come from the pair chat's newest records (before=, 100 ofchatand 400 ofwork), read again whenever its row changes at all. A row holds each member's newest record and is sent at most once in 3 s, so a start or end may be past before a row says it. An ended thread's answer is looked for (the asker's newest 100tasksrecords, the asking chat's 400workrecords) when the asking chat's row changes, or another of its threads is newly seen to end.Tests
cell/shell-tests/threads.test.mjs(run bycargo xtask checkon the pinned Node,node --test): the realcreateThreadsagainst rows published only when the test says, each holding a poster's newest record:threads_ui: the first agent asks the Reader through the stub'sask:Complexity ledger
cell/shell/threads.js(~200 lines, its own module), a#threadsstrip (index.html, ~15 lines of CSS), and two hooks in chat-list.js (hide pair chats; "Waiting on").LISTED_FRESH_MS).cell/shell-tests/threads.test.mjs(~130 lines), on Settings: Add agent (a name and a role), and each agent's role #763'snode --teststep.pairLabelmirrors the CLI'spair_label(cli/src/ask.rs); the lane's asks keep the two in agreement end to end.🤖 Generated with Claude Code