Repository navigation
fix(attention): asks and waiting sessions name the exact tmux target - #388
Merged
Merged
Conversation
An ask, a waiting session and their notifications named a mirrored window
by its host's codename ("mango:1 @ ws", which is no tmux name) or by its tmux
session alone with no window ("build @ ws"), so the operator could not find
it. Every attention surface that names a mirrored window now takes its words
from one helper, mirror.Where: "tmux <session>:<index> @ <host>", led by the
codename when the host has one ("mango:1 · tmux scratch3:5 @ ws").
- chiefnudge.Session carries the window's Index (filled by stallInputs), and
nudgeAskQuestion builds its "where" from mirror.Where; agents keep
"(agent <name>)".
- A waiting session / held question / Hermes item's source is e.Where().
- A mirrored session's ask resolves the window running its Claude session
NOW (claudeSessionEntry, the rule claudeSessionWindow already used) for its
source on the queue, its page header, its session page's asks block and
its notification; when no window runs it, its stored name is followed by
"(not shown in any mirrored window now)" instead of an invented target.
- Docs: attention.md "How a window is named", chief-nudges.md ask text.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… for the in-flight PRs Test-merged with the two open PRs that touch these functions: one changes nudgeAskQuestion's parameters, the other's test calls askItem(a). So: - askItem takes its hosts variadically: askItem(a) still compiles (a mirrored session's ask is then named as no window showing it); every production caller passes the owner's windows. The test call sites go back to main's. - The nudge ask's "where" is its own function, nudgeAskWhere, placed away from nudgeAskQuestion; nudgeAskQuestion's only change is calling it. The unit tests pin nudgeAskWhere whole; the live-ask PG test now pins the whole question built through the real path. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…geAskQuestion's head untouched Test-merged with the open PR that changes nudgeAskQuestion's parameters: a helper replacing the function's first lines conflicted with it textually. Only the mirror/agent branch now changes (it calls nudgeAskPlace), as in the first commit, which merged cleanly. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…x target #384 (now on main) pinned the generic ask's head as "A session (Gold:1 @ ws)"; with this branch the place is mirror.Where's "(Gold:1 · tmux main:1 @ ws)". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Owner
Author
|
Audit round 0 (requirements & deletion) at Verdict: requirement questioned. The operator asked for the exact tmux target always, and the PR applies that more narrowly than asked. What already works: both cases that were observed now render as full tmux targets. The pane index is not needed, because the mirror tracks each window's active pane. |
Owner
Author
|
The operator answered the round-0 premise. Relayed word for word: |
… exact tmux target
The operator's "always" is wider than the open-ask surfaces: an answered
ask's card and its delivery line ("Queued for X …", "Waiting for X to read
it …") send them to the session too, and the chief only ever saw a session
as its codename and host, so an ask the chief wrote could not name the
window.
D1, delivery: answerDeliveryText takes its asker's name, and every caller
passes askSource's — the Answered list (answeredAsksNow reads the owner's
windows once, when an answer is a mirrored session's) and an answered ask's
own page (its item's Source). A window no longer shown reads "(not shown in
any mirrored window now)", never an invented target.
D2, the chief: every session listing it reads carries the same text, as
`where` (additive; every existing field kept):
- fleet_list rows, sessions_overview rows (and the session hints built from
the same live sessions), fleet_report rows on the chief's door only (the
mail door names no host, so it never carries it);
- a session question's block: "where: <askSource>" inside the fence;
- route candidates and an online PR author's window line.
The supervisor prompt's shared live-reads paragraph says to give that
`where` exactly when pointing the owner at a session (its digest
re-recorded after reading the diff).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…mary's one hub read - attentionItemsIn (#385) takes the caller's ONE read of the hub; the ask loop had a second, lazy read of its own for mirrored sessions' asks. It now names them from that same read, so a summary reads the hub once with a mirrored ask open too (new seam test; a second read is caught as "read the hub 2 times; want 1"). - #385's summary tests pin the new titles and whys: "tmux main:1 @ ws", "Waiting: tmux main:1 @ ws"; the unit fixture's ask is built with its window, "Gold:1 · tmux main:4 @ ws"; the member-leak list also refuses the operator's "tmux main:1", with a control that the operator's own summary carries it. - An answered ask's page passes its source through textplain.Title before it reaches the delivery line (internal/textplain's title-sink ledger caught the raw read, introduced in the previous commit). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…own-stripped
The title rule (textplain.Title) plains markdown out of every page and
notification title; on a where it also plained the tmux target itself, so a
session named "_scratch_" read "scratch", "*dev*" read "dev" and "a`b`c"
read "abc" — places the operator cannot find.
- textplain.TitleKeeping(s, keep): Title over s, keep kept verbatim. keep is
mirror.WhereTarget ("tmux <session>:<index> @ <host>", Where's machine
part), carried on attentionItem (target) and RouterNotification
(TitleKeep). Every surface that plains a Source uses it: the push body and
the push title sink (goNotify), the queue card, the summary title/why, the
ask page header and delivery line, the Answered list. The codename around
the target is still plained; per-surface escaping is unchanged.
- The title-sink ledger records the exemption, counts TitleKeeping as the
rule, tracks Where / askSource / askSourceParts reads, and refuses a plain
Title over any value that can name a window ("where kept verbatim", with a
positive control).
- A where from a host the hub still lists but has dropped ends
" (host offline)" (an ask's source, the chief's fleet_report).
- The session page's asks block reads the hub only for a mirrored ask (its
comment said so; the code did not).
- docs/attention.md: the summary's why is "Waiting: <tool> · <where>"; the
target is verbatim; "(host offline)".
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Owner
Author
|
Audit round 1 (full, at
The round also checked that |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Every attention text that names a mirrored tmux window now gives its exact tmux target,
<session>:<index>on its host, led by the host's codename when it has one:Fix CI (tmux build:4 @ laptop) stopped and is waiting on you: …Fix CI (mango:1 · tmux scratch3:5 @ laptop) stopped and is waiting on you: …One helper,
mirror.Where(host, session, window, index, label)(andSessionView.Where()), ininternal/mirror/order.go, built on the same<session>:<index>rule asSessionView.Address(nowtmuxAddress, shared byAddressandTitle).Why
The operator could not find the window an ask was about. The ask text used
c.Label, the host's codename (mango:1), which is not a tmux name: one ask saidmango:1while the window was tmux sessionscratch…, a different index. With no codename it fell back to the tmux session alone, with no window (build @ wsfor windowbuild:4). Neither one is a target you can type into tmux.How
chiefnudge.SessioncarriesIndex(filled installInputsfromw.Index).nudgeAskQuestion: only the mirror/agent branch changed. It now callsnudgeAskPlace(c), which returns" (" + mirror.Where(...) + ")"or" (agent <name>)". The function's signature, its first lines and its generic branch are untouched, so it merges cleanly with fix(chief-nudges): the generic ask shows the chief's read #384 (since merged; see below).attention.Asker{MirrorHost, MirrorSession}) resolves the window running its Claude session now (claudeSessionEntry, which is the ruleclaudeSessionWindowalready used and is now built on) to get its source. If no window runs it, the source is its stored name plus(not shown in any mirrored window now). It never invents a target.askItem(a, hosts...)takes the owner's windows variadically. Every production caller passes them:askItemNow, the queue, the asker block.askItem(a)still compiles, so feat(attention): GET /api/attention/summary + summary-only token scope #385's test keeps building.The index is a snapshot. tmux renumbers windows, so the text says where the window was when it was written. View in session (from #383) resolves by Claude session id at click time and stays the source of truth. The docs say this. I did not add "as of when it was asked" to every ask, because it would be noise on every line.
Site ledger
From a
git grepforLabel,TmuxSession,MirrorSession," @ ",DisplayTitle() +andaskItem(over non-test Go:chief_nudge.gonudgeAskQuestion/ newnudgeAskPlacemirror.Wherechief_nudge.go:344stallInputschiefnudge.SessionIndexattention.gowaitingSessionsSourceAskUserQuestion), Hermes waiting item; queue card and push title/bodye.Where()(wasDisplayTitle() + " @ " + host)attention.goaskItem→ newaskSourceattention_ask_page.gorenderAskPageaskItemNowattention_asker.goaskerAskshostViews()attention_push_target.goopenTargetaskItemNowattention_suggest.goaskFiled,repushAskaskItemNowcards_attention.goclaudeSessionWindowclaudeSessionEntry(the same rule, now with Index/Label)attention.goansweredAsksHTML/answerDeliveryText(Answered card source; "Queued for X…", "Delivering to X", "Waiting for X to read it…"),attention_ask_page.goanswered ask's delivery lineaskSource(answeredAsksNowreads the windows once when an answer is a mirrored session's); window gone → "(not shown …)".askerLabelis now onlyaskSource's fallbackattention.goaskerNamemirror_mcp.go:274Asker.Labelchief_ask_tool.gorenderChiefQuestionwhere: <askSource>; the preface (host, Claude session id) is unchangedfocus_tools.gofleet_listwherefieldsessions_overview.gosessions_overviewrows +liveSessionswherefieldsessions_overview.go→internal/plugins/hints.gosession hints— where: …on each hint's head linepr_routes.go→hints.gorenderAuthorwindow: online, where: …when a window shows itfleet_report.gowhereon listed windows. Mail door: never (it names no host; its goldens are unchanged)plugin_message_route_tools.go:290route candidatesplugin_message_routerefusalwherefieldinternal/agentspec/instructions.gochiefLiveReads(shared by both chief runtimes)whereexactly. Digest re-recorded after reading the diff; worded without tool names (the two runtimes name tools differently) and without "tmux" (an existing prose test bans it)agent_destroy_tool.go,chief_restart_tool.go,connector_decide_tools.go,route_reply_tool.go,plugin_message_route_tools.go(MirrorHost + "/" + MirrorSession)frominternal/ui/send_queue.go/composer.go"Already answered X's question"Before this change, an unlabelled mirrored session's ask was already near-exact: its stored label was
DisplayTitle() @ host=build:7 @ ws. It was a snapshot with notmuxmarker and no codename handling. It now resolves live.Test matrix
Whole normalised strings are pinned (
internal/api/attention_where_test.go,internal/mirror/where_test.go). The fixture's fields are pairwise distinct: sessionscratch3, id@37, index 5, codenamemango:1(the codename's index is not the tmux index), plus an unlabelledbuild/@4/7.Red runs at
origin/mainf73c492f. Green runs at HEAD. The counts are the runner's own--- PASS/FAILlines.TestANudgeAskNamesItsWindowsExactTmuxTarget(codename / no codename subtests)TestAnAgentNudgeAskKeepsItsAgentNameTestAWaitingSessionAndItsPushNameTheExactTmuxTarget(session item and push body)TestAMirroredSessionsAskNamesTheWindowRunningItNow(queue source and push title, incl. window gone → "(not shown …)")TestTheAskPageHeaderNamesTheExactTmuxTarget(incl. window gone)TestASessionPagesAsksBlockNamesTheExactTmuxTargetTestAFiledAsksNotificationNamesTheExactTmuxTarget(stored name stalemain:9, push saysmain:2)TestChiefNudgeLiveAskIsAnsweredAndRelayedVerbatim(PG; now pins the whole ask question throughactAsk)TestAWaitingSessionShowsWhatItWaitsOn(PG; existing, expectation updated)TestChiefNudgeTheGenericAsk*×3 (PG; #384's,genericAskHeadupdated)TestWhereIsTheExactTmuxTarget(helper unit test)To run the two nudge tests at
main, which has nonudgeAskPlace, I used a test-only shim that read main's own place out of main's ownnudgeAskQuestion. It was never committed.Mutation battery (one exact edit per mutant, tree checksum restored): 10/10 killed.
addr := session)IndexLabel @ hostclaudeSessionEntrydropsIndexaskFiledwithout hostsgo build ./...andgo vet ./...are clean.go test -racepasses oninternal/api,internal/mirror,internal/chiefnudgeandinternal/uiunderscripts/with-test-pg.sh. Fullgo test ./...with PG: 77 packages ok, rc 0 (run before the last two refactor commits; the-raceset above was re-run at HEAD).Round 1 (answered lines + the chief's reads)
The tests pin whole strings:
attention_where_delivery_test.go,attention_where_chief_test.go,internal/plugins/hints_where_test.go, plus a whole-line pin inchief_ask_pg_test.go. Red runs at the previous head4ed52f11, green at2311ca58. Counts are the runner's own result lines.4ed52f11TestAnAnswersDeliveryLineNamesTheExactTmuxTarget(Answered list: card source and delivery line whole, for Queued+detail with a codename, Queued with window gone, Pull, Unconfirmed)TestAnAnsweredAskPagesDeliveryLineNamesTheExactTmuxTarget(the same lines on the ask's own page)TestTheChiefsFleetListCarriesEachWindowsWhere(where, withname/hostkept)TestChiefAskPutsTheQuestionToTheChiefWithItsSessionAndRoute(PG; pins\n| where: tmux main:1 @ ws\nin the chief's real turn)TestASessionQuestionToTheChiefSaysWhereTheWindowIs(incl. window gone)TestTheOverviewAndTheRouteCandidatesCarryEachWindowsWhereTestASessionHintAndAnOnlineAuthorCarryTheirWhereTestFleetReportGolden(PG; 3*.tool.jsongoldens regenerated; all mail goldens unchanged)Mutation battery, round 1: 11 of 11 mutants killed, with the round-0 "drop the index" mutant as a positive control (also killed).
askerLabelaskerLabelfleet_listdropswhereliveSessionsdropswherewherewherewherelinewherewherewherego build ./...andgo vet ./...are clean.-racewith PG passes oninternal/api,internal/mirror,internal/chiefnudge,internal/ui,internal/plugins,internal/agentspec,internal/agentprovisionandcmd/muster-server.Round 1 has two things a reviewer should know:
nameand once forwhere: therichgolden's gap reads "2 field(s) withheld", where it used to read 1. Both fields really are withheld.pr_routes.gowiring (a.Where = e.Where()) has no test of its own. Only the renderer is pinned.Round 2 (final): the target is shown verbatim
🟡 Markdown stripping. Every display surface plains a title's markdown with
textplain.Title. That stripped the target's own characters, so tmux session_scratch_showed asscratch,*dev*asdev, anda`b`casabc.textplain.TitleKeeping(s, keep). It plainssexceptkeep, which it keeps verbatim.keepis the window's target, a newmirror.WhereTarget(tmux <session>:<index> @ <host>, the machine part ofWhere).attentionItemcarries that target.goNotify, through a newRouterNotification.TitleKeep), the queue card, the summarytitle/why, the ask page header and its delivery line, and the Answered list.*mango*:1shows asmango:1). Surface escaping is unchanged: HTML-escaped on pages, one line on pushes.internal/textplain/sinks_test.go): it records the exemption and countsTitleKeepingas the title rule. It now also tracksWhere,askSourceandaskSourcePartsreads. A new subtest,where kept verbatim, fails on anytextplain.Titleover a value that can name a window (aSource, aWhere, an ask's source). It has a positive control requiring at least 6TitleKeepingsites.🟢 Offline host. A where from a host that has just dropped (the hub keeps listing its windows for a grace period) now ends in
(host offline). This applies to an ask's source and to the chief'sfleet_report. Theclashsaved output for the fleet report was regenerated.🟢 Asks block comment. The asks block (
attention_asker.go) now reads hosts only for a mirrored ask, so its comment is true again.Docs:
attention.md's summary table now showsWaiting: <tool> · <where>. "How a window is named" now says the target is verbatim and describes(host offline).Tests, red at
3427d6d4and green at HEAD:3427d6d4TestAWindowsTargetIsShownVerbatimOnThePushTheSummaryAndTheCard(sessions_scratch_with codename*mango*:1,*dev*,a`b`c; push body, summary title and why, card, all whole)tmux scratch:2,tmux dev:3,tmux abc:4)TestAnAsksPushTitleKeepsItsTargetVerbatim(throughgoNotify)tmux dev:3 @ ws is asking)TestAWhereOnAnOfflineHostSaysSoTestTitleKeepingKeepsOnlyItsPartVerbatim(textplain)Full
go test -race ./...with Postgres on the merged final tree (c73ddd4e, which includes main's #386 and #389): 77ok, 0FAIL, 20 with no test files, 0 timeouts, 0 skips, rc 0.go build ./...andgo vet ./...are clean.This round has two rows that did not change after the offline marker, recorded here:
fleet_listandsessions_overviewlist only online windows, so they never show an offlinewhere.Mutations: 8 of 8 killed, plus a positive control (also killed).
Title: killed by both the behavioural test and the ledger'swhere kept verbatim.TitleKeep, a waiting item with no target, an ask item with no target,TitleKeepingkeeping nothing, no offline marker: each killed.Merge notes (test-merged)
main. It is merged into this branch with no textual conflict. Its newchief_nudge_generic_read_pg_test.gopinned"A session (Gold:1 @ ws)", and this branch updates it to"(Gold:1 · tmux main:1 @ ws)".mainand merged into this branch with no textual conflict. What changed after that merge:One semantic fix. feat(attention): GET /api/attention/summary + summary-only token scope #385 made
attentionItemsIntake the summary's ONE hub read (hs). This branch's ask loop had made a second, lazy read for mirrored asks. It now names asks fromhs, so a summary reads the hub once even when a mirrored session's ask is open. New seam testTestTheSummaryNamesAMirroredAsksWindowFromItsOneHubRead: it passes at HEAD. With the second read put back, it fails with "read the hub 2 times; want 1".feat(attention): GET /api/attention/summary + summary-only token scope #385's expectations updated.
attention_summary_pg_test.go: the mirrored ask's title is nowtmux main:1 @ ws, the session's title istmux main:1 @ wsand its why isWaiting: tmux main:1 @ ws. The member-leak list also checks fortmux main:1, with a control that the operator's own summary containstmux main:1 @ ws.attention_summary_test.go: the mirrored ask is built with its fixture's window (Gold:1, tmuxmain:4), so its title is pinned asGold:1 · tmux main:4 @ ws, not the "not shown" fallback.One more fix, after the full suite on the merged tree. The
internal/textplaintitle-sink ledger went red, because the round-1 commit read the answered ask page'sit.Sourceraw into its delivery line. It now goes throughtextplain.Title. That commit's own-racerun did not includeinternal/textplain. Only the full./...run caught it.Full
go test -race ./...with Postgres at3427d6d4: 77ok, 0FAIL, 20 packages with no test files, 0 timeouts, 0 skips, rc 0.go build ./...andgo vet ./...are clean.What the summary (and so the bar) shows now
GET /api/attention/summarybuilds each item'stitlefrom the item's source and itswhyfrom the first line of its push body. Both now carry the window's exact target:titleistmux build:4 @ laptop, ormango:1 · tmux scratch3:5 @ laptopwhen the window has a codename.whyisWaiting: <tool> · <the same>, orWaiting: <the same>when no tool is named.titleis the window running it now. When no window runs it, the title is its stored name followed by(not shown in any mirrored window now).whyis the question.your chief), a reply draft: unchanged. A nudge ask'swhyis the first line of its question, which opens with<focus title> (<where>) ….Not verified
attention.spec.tsassertstoContainText('@ ' + host), which the new format still contains.🤖 Generated with Claude Code