Repository navigation
fix(web): opening the right panel no longer crashes Chat with too many re-renders - #134
Conversation
…y re-renders Since 0.1.2-lazurio.5, opening T3's right panel crashed Chat with React error pingdotgg#301 on every Environment. The panel's open state is persisted per thread, so that thread then crashed on every load. ChatView read the thread's panel through a plain function, buddyRightPanelState(buddySurface, useRightPanelStore(...)). The React Compiler treats a plain function's result as mutable and so stopped memoizing the panel's presence value built from it. usePanelPresence sets state during render whenever that value changes identity, which with a fresh object on every render looped until React gave up. The Buddy seam now runs inside the store's selector, whose result the compiler treats as frozen, for the right panel and, the same way, the terminal drawer. Buddy's surface still reads both as closed. The compiled ChatView memoizes the presence value again, as 0.1.2-lazurio.4 did, and the release contract test pins the new shape of both seams. Co-Authored-By: Claude Opus 5.5 <[email protected]>
agentrozjedemeai
left a comment
There was a problem hiding this comment.
Requesting changes for the missing behavioral regression coverage of #135 / React pingdotgg#301 (one P2 finding below).
The code fix is consistent with the reported cause and applies to ordinary T3 users as well as Buddy: the selectors return the existing thread state outside Buddy and constant closed/empty states inside Buddy; the explicit terminal-drawer exclusion is retained. I independently compiled the exact base and head with the lockfile's oxc-transform-react 0.152.0 and the production compiler options: the base creates a fresh rightPanelPresenceValue, while this head memoizes it (858 -> 895 slots), with no compiler diagnostics on either version.
Verified Lazurio Fork CI run 40540567907 is successful on 027350c: server/web, both archive builds and launcher install. Its logs confirm the Buddy panel/terminal tests and all 46 release-contract tests pass. Greptile is still in progress; Macroscope checks are skipped. The PR body accurately describes the implementation and explicitly acknowledges the absent regression test; update that section when adding the requested coverage.
Local verification: the changed Buddy release-contract test passes and git diff --check is clean. The full local contract run passed 45/46, with the remaining test unable to resolve uninstalled workspace dependencies; vp is unavailable here, so I did not repeat the full web checks or a live browser repro. No source edits, push or merge.
…t Compiler A regression test for #135. It compiles the real ChatView with the React Compiler options the build uses and requires every value ChatView hands usePanelPresence to be bound to a memo slot, never to an object literal built on every render. That object is what made opening the right panel, or loading a thread whose panel was saved open, loop until React error pingdotgg#301. The test fails on f607827 ("rightPanelPresenceValue is built on every render") and passes with the fix. It lives under apps/web/src/lazurio, so the fork CI's overlay test gate runs it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
Thanks, I've addressed this in
Please re-review on |
agentrozjedemeai
left a comment
There was a problem hiding this comment.
Round 2: the new compiler regression test resolves my sole P2 finding from review 5670855112. Approving the code and regression coverage on ccb3eb7.
I ran the byte-identical new test in isolated source fixtures with the lockfile's Vite+ 1.3.0 / Vitest 5.0.5 and oxc-transform-react 0.152.0. With ChatView from base f607827 it fails at line 36 with "rightPanelPresenceValue is built on every render"; with ChatView from this exact head it passes. Independent compilation also confirms the base binds a fresh object, whereas both 027350c and this head bind rightPanelPresenceValue to t81. This directly guards the compiler regression responsible for both opening the panel and loading a thread with it persisted open: usePanelPresence updates state during render when the open panel's value changes identity. A full ChatView rendering harness is not necessary to resolve this finding.
The workflow's fixed apps/web/src/lazurio gate includes the new test, and the web unit project's src/**/*.test.{ts,tsx} pattern selects it. The PR's regression-test section now explains the test, the red base, the green fixed behavior and its scope. The only source change since 027350c is this 38-line test; the application fix and release-contract changes are unchanged. The focused Buddy release-contract test and git diff --check also pass locally.
CI caveat at submission: Lazurio Fork CI run 40543950794 targets this exact SHA, but is NOT yet fully green. Formatting, lint, server/web typecheck and the linux-x64 archive succeeded. The server/web job is still installing Chromium, so the overlay test step has not yet run there; darwin-arm64 is queued without a runner. These outstanding checks must complete successfully before publication. The skipped Macroscope checks are not passing test evidence. I have not performed the separate Ring 0 browser check.
Review only: no source edits, branch push, merge or release.
Root decision 0210, addendum of 2026-10-22, point 1, and its plain-chat addendum (HumanAndMachines/Lazurio#648; plan DEV-6676, row 28): the person sees and hears only what Buddy writes in human words. In Buddy's surface (lazurioBuddy) only: - The timeline keeps the messages and what the person acts on; T3's record of the work stays out. While a turn runs, or work still runs after it, one quiet static line says "Buddy na tom pracuje…" / "Buddy is on it…". A failed turn shows one human line, and the thread's error banner says "Něco se nepovedlo. Zkuste to prosím znovu." instead of T3's text. - None of the turn's work shows at the end of the timeline, and Buddy's queue holds and counts only the person's own messages, never an agent's such as Delegace's wake. - The composer is a message box ("Napište Buddymu…") whose only choice is Hermes's Normal / Fast switch; T3 applies no model or effort to Buddy's sessions (AcpT3Selection) and Buddy's thread keeps full access, which the server guard also holds. - The surface never shows T3's terminal or right panel, their keys stay the browser's, and a link in the conversation opens in a new browser tab. - In the bubble the surface wears the host page's appearance, also over a saved single-appearance theme. Outside it, the composer keeps the corner of Buddy's launcher free (index.html, --lazurio-buddy-clearance). The terminal and right-panel states are read inside the store selectors, so the React Compiler keeps ChatView's panel presence values memoized (chatViewPanelPresence.test.ts guards it, #134). On v0.1.3 upstream moved the work banners and the queue to the end of the timeline and removed the composer's task list (pingdotgg#25401). The work items (childInputNow, goalNow, backgroundWorkNow) are masked on Buddy's surface with inline conditionals in ChatView's body, where hasThreadNow, ThreadNow and the scroll button's summary read them, so no plain function wraps a hook result in render. buddyBackgroundWork is whether backgroundWorkNow exists. useBuddyQueuedRunCount drops an agent's queued message from the footer's count, which upstream's getUserQueuedThreadRuns keeps, and buddyWorkBanners and the task-list seam are gone. useBuddyChatShortcuts takes upstream's `!backgroundThread` condition (pingdotgg#24847), ChatTerminalDrawers lost focusRequestId (pingdotgg#25009), the placeholder falls back to DEFAULT_COMPOSER_PLACEHOLDER, and ReopenClosedViewShortcut stays off on the slimmer root. T3 no longer re-applies its theme from useTheme's effect (pingdotgg#25007, pingdotgg#25461), so the host-appearance stand-in follows every application of T3's saved theme through subscribeRenderedAppearance; the theme tests apply T3's stored appearance as app start does. The ACP adapter keeps the selection gate beside upstream's flavor hook (pingdotgg#24605). Effects carry justified react-effect-budget suppressions and comments are cut to the four-line limit. The error banner asks about the thread's own Environment (ChatBannerLane passes it), so a thread of another Environment keeps T3's own text. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Root decision 0210, addendum of 2026-10-22, point 1, and its plain-chat addendum (HumanAndMachines/Lazurio#648; plan DEV-6676, row 28): the person sees and hears only what Buddy writes in human words. In Buddy's surface (lazurioBuddy) only: - The timeline keeps the messages and what the person acts on; T3's record of the work stays out. While a turn runs, or work still runs after it, one quiet static line says "Buddy na tom pracuje…" / "Buddy is on it…". A failed turn shows one human line, and the thread's error banner says "Něco se nepovedlo. Zkuste to prosím znovu." instead of T3's text. - None of the turn's work shows at the end of the timeline, and Buddy's queue holds and counts only the person's own messages, never an agent's such as Delegace's wake. - The composer is a message box ("Napište Buddymu…") whose only choice is Hermes's Normal / Fast switch, the one option row 23's AcpT3Selection lets through, and Buddy's thread keeps full access, which the server guard also holds. - The surface never shows T3's terminal or right panel, their keys stay the browser's, and a link in the conversation opens in a new browser tab. The terminal and right-panel states are read inside the store selectors, so the React Compiler keeps ChatView's panel presence values memoized (chatViewPanelPresence.test.ts guards it, #134). On v0.1.3 upstream moved the work banners and the queue to the end of the timeline and removed the composer's task list (pingdotgg#25401). The work items (childInputNow, goalNow, backgroundWorkNow) are masked on Buddy's surface with inline conditionals in ChatView's body, where hasThreadNow, ThreadNow and the scroll button's summary read them, so no plain function wraps a hook result in render. buddyBackgroundWork is whether backgroundWorkNow exists. useBuddyQueuedRunCount drops an agent's queued message from the footer's count, which upstream's getUserQueuedThreadRuns keeps, and buddyWorkBanners and the task-list seam are gone. useBuddyChatShortcuts takes upstream's `!backgroundThread` condition (pingdotgg#24847), ChatTerminalDrawers lost focusRequestId (pingdotgg#25009), the placeholder falls back to DEFAULT_COMPOSER_PLACEHOLDER, and ReopenClosedViewShortcut stays off on the slimmer root. Effects carry justified react-effect-budget suppressions and comments are cut to the four-line limit. The error banner asks about the thread's own Environment (ChatBannerLane passes it), so a thread of another Environment keeps T3's own text. On v0.1.4: upstream dropped the resting composer's control strip, so Buddy's composer hook only gives the placeholder and hasControls is gone; Buddy's controls still replace T3's in ComposerControlsBar ahead of upstream's controls ownership. The timeline mask keeps upstream's pending approvals footer, which reads the final rows. useChatShortcuts no longer exports its input type, so the overlay takes it from the hook. The tests follow upstream's props and labels: no activeTurnInProgress or onRollbackCheckpoint, the queue's "1 queued message", background work rows with turn items and "Running …" / "Waiting on …" titles, and a single subagent as a [data-v2-item-type=subagent] row. Effect suppressions use the renamed no-unreasoned-state-sync rule. Row 23's AcpT3Selection, row 25's host appearance and row 7's launcher clearance, which this commit carried on v0.1.3, are back in their rows. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Root decision 0210, addendum of 2026-10-22, point 1, and its plain-chat addendum (HumanAndMachines/Lazurio#648; plan DEV-6676, row 28): the person sees and hears only what Buddy writes in human words. In Buddy's surface (lazurioBuddy) only: - The timeline keeps the messages and what the person acts on; T3's record of the work stays out. While a turn runs, or work still runs after it, one quiet static line says "Buddy na tom pracuje…" / "Buddy is on it…". A failed turn shows one human line, and the thread's error banner says "Něco se nepovedlo. Zkuste to prosím znovu." instead of T3's text. - None of the turn's work shows at the end of the timeline, and Buddy's queue holds and counts only the person's own messages, never an agent's such as Delegace's wake. - The composer is a message box ("Napište Buddymu…") whose only choice is Hermes's Normal / Fast switch, the one option row 23's AcpT3Selection lets through, and Buddy's thread keeps full access, which the server guard also holds. - The surface never shows T3's terminal or right panel, their keys stay the browser's, and a link in the conversation opens in a new browser tab. The terminal and right-panel states are read inside the store selectors, so the React Compiler keeps ChatView's panel presence values memoized (chatViewPanelPresence.test.ts guards it, #134). On v0.1.3 upstream moved the work banners and the queue to the end of the timeline and removed the composer's task list (pingdotgg#25401). The work items (childInputNow, goalNow, backgroundWorkNow) are masked on Buddy's surface with inline conditionals in ChatView's body, where hasThreadNow, ThreadNow and the scroll button's summary read them, so no plain function wraps a hook result in render. buddyBackgroundWork is whether backgroundWorkNow exists. useBuddyQueuedRunCount drops an agent's queued message from the footer's count, which upstream's getUserQueuedThreadRuns keeps, and buddyWorkBanners and the task-list seam are gone. useBuddyChatShortcuts takes upstream's `!backgroundThread` condition (pingdotgg#24847), ChatTerminalDrawers lost focusRequestId (pingdotgg#25009), the placeholder falls back to DEFAULT_COMPOSER_PLACEHOLDER, and ReopenClosedViewShortcut stays off on the slimmer root. Effects carry justified react-effect-budget suppressions and comments are cut to the four-line limit. The error banner asks about the thread's own Environment (ChatBannerLane passes it), so a thread of another Environment keeps T3's own text. On v0.1.4: upstream dropped the resting composer's control strip, so Buddy's composer hook only gives the placeholder and hasControls is gone; Buddy's controls still replace T3's in ComposerControlsBar ahead of upstream's controls ownership. The timeline mask keeps upstream's pending approvals footer, which reads the final rows. useChatShortcuts no longer exports its input type, so the overlay takes it from the hook. The tests follow upstream's props and labels: no activeTurnInProgress or onRollbackCheckpoint, the queue's "1 queued message", background work rows with turn items and "Running …" / "Waiting on …" titles, and a single subagent as a [data-v2-item-type=subagent] row. Effect suppressions use the renamed no-unreasoned-state-sync rule. Row 23's AcpT3Selection, row 25's host appearance and row 7's launcher clearance, which this commit carried on v0.1.3, are back in their rows. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Root decision 0210, addendum of 2026-10-22, point 1, and its plain-chat addendum (HumanAndMachines/Lazurio#648; plan DEV-6676, row 28): the person sees and hears only what Buddy writes in human words. In Buddy's surface (lazurioBuddy) only: - The timeline keeps the messages and what the person acts on; T3's record of the work stays out. While a turn runs, or work still runs after it, one quiet static line says "Buddy na tom pracuje…" / "Buddy is on it…". A failed turn shows one human line, and the thread's error banner says "Něco se nepovedlo. Zkuste to prosím znovu." instead of T3's text. - None of the turn's work shows at the end of the timeline, and Buddy's queue holds and counts only the person's own messages, never an agent's such as Delegace's wake. - The composer is a message box ("Napište Buddymu…") whose only choice is Hermes's Normal / Fast switch, the one option row 23's AcpT3Selection lets through, and Buddy's thread keeps full access, which the server guard also holds. - The surface never shows T3's terminal or right panel, their keys stay the browser's, and a link in the conversation opens in a new browser tab. The terminal and right-panel states are read inside the store selectors, so the React Compiler keeps ChatView's panel presence values memoized (chatViewPanelPresence.test.ts guards it, #134). On v0.1.3 upstream moved the work banners and the queue to the end of the timeline and removed the composer's task list (pingdotgg#25401). The work items (childInputNow, goalNow, backgroundWorkNow) are masked on Buddy's surface with inline conditionals in ChatView's body, where hasThreadNow, ThreadNow and the scroll button's summary read them, so no plain function wraps a hook result in render. buddyBackgroundWork is whether backgroundWorkNow exists. useBuddyQueuedRunCount drops an agent's queued message from the footer's count, which upstream's getUserQueuedThreadRuns keeps, and buddyWorkBanners and the task-list seam are gone. useBuddyChatShortcuts takes upstream's `!backgroundThread` condition (pingdotgg#24847), ChatTerminalDrawers lost focusRequestId (pingdotgg#25009), the placeholder falls back to DEFAULT_COMPOSER_PLACEHOLDER, and ReopenClosedViewShortcut stays off on the slimmer root. Effects carry justified react-effect-budget suppressions and comments are cut to the four-line limit. The error banner asks about the thread's own Environment (ChatBannerLane passes it), so a thread of another Environment keeps T3's own text. On v0.1.4: upstream dropped the resting composer's control strip, so Buddy's composer hook only gives the placeholder and hasControls is gone; Buddy's controls still replace T3's in ComposerControlsBar ahead of upstream's controls ownership. The timeline mask keeps upstream's pending approvals footer, which reads the final rows. useChatShortcuts no longer exports its input type, so the overlay takes it from the hook. The tests follow upstream's props and labels: no activeTurnInProgress or onRollbackCheckpoint, the queue's "1 queued message", background work rows with turn items and "Running …" / "Waiting on …" titles, and a single subagent as a [data-v2-item-type=subagent] row. Effect suppressions use the renamed no-unreasoned-state-sync rule. Row 23's AcpT3Selection, row 25's host appearance and row 7's launcher clearance, which this commit carried on v0.1.3, are back in their rows. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Root decision 0210, addendum of 2026-10-22, point 1, and its plain-chat addendum (HumanAndMachines/Lazurio#648; plan DEV-6676, row 28): the person sees and hears only what Buddy writes in human words. In Buddy's surface (lazurioBuddy) only: - The timeline keeps the messages and what the person acts on; T3's record of the work stays out. While a turn runs, or work still runs after it, one quiet static line says "Buddy na tom pracuje…" / "Buddy is on it…". A failed turn shows one human line, and the thread's error banner says "Něco se nepovedlo. Zkuste to prosím znovu." instead of T3's text. - None of the turn's work shows at the end of the timeline, and Buddy's queue holds and counts only the person's own messages, never an agent's such as Delegace's wake. - The composer is a message box ("Napište Buddymu…") whose only choice is Hermes's Normal / Fast switch, the one option row 23's AcpT3Selection lets through, and Buddy's thread keeps full access, which the server guard also holds. - The surface never shows T3's terminal or right panel, their keys stay the browser's, and a link in the conversation opens in a new browser tab. The terminal and right-panel states are read inside the store selectors, so the React Compiler keeps ChatView's panel presence values memoized (chatViewPanelPresence.test.ts guards it, #134). On v0.1.3 upstream moved the work banners and the queue to the end of the timeline and removed the composer's task list (pingdotgg#25401). The work items (childInputNow, goalNow, backgroundWorkNow) are masked on Buddy's surface with inline conditionals in ChatView's body, where hasThreadNow, ThreadNow and the scroll button's summary read them, so no plain function wraps a hook result in render. buddyBackgroundWork is whether backgroundWorkNow exists. useBuddyQueuedRunCount drops an agent's queued message from the footer's count, which upstream's getUserQueuedThreadRuns keeps, and buddyWorkBanners and the task-list seam are gone. useBuddyChatShortcuts takes upstream's `!backgroundThread` condition (pingdotgg#24847), ChatTerminalDrawers lost focusRequestId (pingdotgg#25009), the placeholder falls back to DEFAULT_COMPOSER_PLACEHOLDER, and ReopenClosedViewShortcut stays off on the slimmer root. Effects carry justified react-effect-budget suppressions and comments are cut to the four-line limit. The error banner asks about the thread's own Environment (ChatBannerLane passes it), so a thread of another Environment keeps T3's own text. On v0.1.4: upstream dropped the resting composer's control strip, so Buddy's composer hook only gives the placeholder and hasControls is gone; Buddy's controls still replace T3's in ComposerControlsBar ahead of upstream's controls ownership. Upstream shows an approval on its own row (pingdotgg#26255); Buddy's mask leaves no work rows, so the approval falls to upstream's footer for unmatched approvals, and a browser test shows a pending approval there with Approve and Decline and no work row or quiet line. The link_request card stays off Buddy's surface, where link requests are refused (row 25). useChatShortcuts no longer exports its input type, so the overlay takes it from the hook. The tests follow upstream's props and labels: no activeTurnInProgress or onRollbackCheckpoint, the queue's "1 queued message", background work rows with turn items and "Running …" / "Waiting on …" titles, and a single subagent as a [data-v2-item-type=subagent] row. Effect suppressions use the renamed no-unreasoned-state-sync rule. Row 23's AcpT3Selection, row 25's host appearance and row 7's launcher clearance, which this commit carried on v0.1.3, are back in their rows. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Root decision 0210, addendum of 2026-10-22, point 1, and its plain-chat addendum (HumanAndMachines/Lazurio#648; plan DEV-6676, row 28): the person sees and hears only what Buddy writes in human words. In Buddy's surface (lazurioBuddy) only: - The timeline keeps the messages and what the person acts on; T3's record of the work stays out. While a turn runs, or work still runs after it, one quiet static line says "Buddy na tom pracuje…" / "Buddy is on it…". A failed turn shows one human line, and the thread's error banner says "Něco se nepovedlo. Zkuste to prosím znovu." instead of T3's text. - None of the turn's work shows at the end of the timeline, and Buddy's queue holds and counts only the person's own messages, never an agent's such as Delegace's wake. - The composer is a message box ("Napište Buddymu…") whose only choice is Hermes's Normal / Fast switch, the one option row 23's AcpT3Selection lets through, and Buddy's thread keeps full access, which the server guard also holds. - The surface never shows T3's terminal or right panel, their keys stay the browser's, and a link in the conversation opens in a new browser tab. The terminal and right-panel states are read inside the store selectors, so the React Compiler keeps ChatView's panel presence values memoized (chatViewPanelPresence.test.ts guards it, #134). On v0.1.3 upstream moved the work banners and the queue to the end of the timeline and removed the composer's task list (pingdotgg#25401). The work items (childInputNow, goalNow, backgroundWorkNow) are masked on Buddy's surface with inline conditionals in ChatView's body, where hasThreadNow, ThreadNow and the scroll button's summary read them, so no plain function wraps a hook result in render. buddyBackgroundWork is whether backgroundWorkNow exists. useBuddyQueuedRunCount drops an agent's queued message from the footer's count, which upstream's getUserQueuedThreadRuns keeps, and buddyWorkBanners and the task-list seam are gone. useBuddyChatShortcuts takes upstream's `!backgroundThread` condition (pingdotgg#24847), ChatTerminalDrawers lost focusRequestId (pingdotgg#25009), the placeholder falls back to DEFAULT_COMPOSER_PLACEHOLDER, and ReopenClosedViewShortcut stays off on the slimmer root. Effects carry justified react-effect-budget suppressions and comments are cut to the four-line limit. The error banner asks about the thread's own Environment (ChatBannerLane passes it), so a thread of another Environment keeps T3's own text. On v0.1.4: upstream dropped the resting composer's control strip, so Buddy's composer hook only gives the placeholder and hasControls is gone; Buddy's controls still replace T3's in ComposerControlsBar ahead of upstream's controls ownership. Upstream shows an approval on its own row (pingdotgg#26255); Buddy's mask leaves no work rows, so the approval falls to upstream's footer for unmatched approvals, and a browser test shows a pending approval there with Approve and Decline and no work row or quiet line. The link_request card stays off Buddy's surface, where link requests are refused (row 25). useChatShortcuts no longer exports its input type, so the overlay takes it from the hook. The tests follow upstream's props and labels: no activeTurnInProgress or onRollbackCheckpoint, the queue's "1 queued message", background work rows with turn items and "Running …" / "Waiting on …" titles, and a single subagent as a [data-v2-item-type=subagent] row. Effect suppressions use the renamed no-unreasoned-state-sync rule. Row 23's AcpT3Selection, row 25's host appearance and row 7's launcher clearance, which this commit carried on v0.1.3, are back in their rows. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Root decision 0210, addendum of 2026-10-22, point 1, and its plain-chat addendum (HumanAndMachines/Lazurio#648; plan DEV-6676, row 28): the person sees and hears only what Buddy writes in human words. In Buddy's surface (lazurioBuddy) only: - The timeline keeps the messages and what the person acts on; T3's record of the work stays out. While a turn runs, or work still runs after it, one quiet static line says "Buddy na tom pracuje…" / "Buddy is on it…". A failed turn shows one human line, and the thread's error banner says "Něco se nepovedlo. Zkuste to prosím znovu." instead of T3's text. - None of the turn's work shows at the end of the timeline, and Buddy's queue holds and counts only the person's own messages, never an agent's such as Delegace's wake. - The composer is a message box ("Napište Buddymu…") whose only choice is Hermes's Normal / Fast switch, the one option row 23's AcpT3Selection lets through, and Buddy's thread keeps full access, which the server guard also holds. - The surface never shows T3's terminal or right panel, their keys stay the browser's, and a link in the conversation opens in a new browser tab. The terminal and right-panel states are read inside the store selectors, so the React Compiler keeps ChatView's panel presence values memoized (chatViewPanelPresence.test.ts guards it, #134). On v0.1.3 upstream moved the work banners and the queue to the end of the timeline and removed the composer's task list (pingdotgg#25401). The work items (childInputNow, goalNow, backgroundWorkNow) are masked on Buddy's surface with inline conditionals in ChatView's body, where hasThreadNow, ThreadNow and the scroll button's summary read them, so no plain function wraps a hook result in render. buddyBackgroundWork is whether backgroundWorkNow exists. useBuddyQueuedRunCount drops an agent's queued message from the footer's count, which upstream's getUserQueuedThreadRuns keeps, and buddyWorkBanners and the task-list seam are gone. useBuddyChatShortcuts takes upstream's `!backgroundThread` condition (pingdotgg#24847), ChatTerminalDrawers lost focusRequestId (pingdotgg#25009), the placeholder falls back to DEFAULT_COMPOSER_PLACEHOLDER, and ReopenClosedViewShortcut stays off on the slimmer root. Effects carry justified react-effect-budget suppressions and comments are cut to the four-line limit. The error banner asks about the thread's own Environment (ChatBannerLane passes it), so a thread of another Environment keeps T3's own text. On v0.1.4: upstream dropped the resting composer's control strip, so Buddy's composer hook only gives the placeholder and hasControls is gone; Buddy's controls still replace T3's in ComposerControlsBar ahead of upstream's controls ownership. Upstream shows an approval on its own row (pingdotgg#26255); Buddy's mask leaves no work rows, so the approval falls to upstream's footer for unmatched approvals, and a browser test shows a pending approval there with Approve and Decline and no work row or quiet line. The link_request card stays off Buddy's surface, where link requests are refused (row 25). useChatShortcuts no longer exports its input type, so the overlay takes it from the hook. The tests follow upstream's props and labels: no activeTurnInProgress or onRollbackCheckpoint, the queue's "1 queued message", background work rows with turn items and "Running …" / "Waiting on …" titles, and a single subagent as a [data-v2-item-type=subagent] row. Effect suppressions use the renamed no-unreasoned-state-sync rule. Row 23's AcpT3Selection, row 25's host appearance and row 7's launcher clearance, which this commit carried on v0.1.3, are back in their rows. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Root decision 0210, addendum of 2026-10-22, point 1, and its plain-chat addendum (HumanAndMachines/Lazurio#648; plan DEV-6676, row 28): the person sees and hears only what Buddy writes in human words. In Buddy's surface (lazurioBuddy) only: - The timeline keeps the messages and what the person acts on; T3's record of the work stays out. While a turn runs, or work still runs after it, one quiet static line says "Buddy na tom pracuje…" / "Buddy is on it…". A failed turn shows one human line, and the thread's error banner says "Něco se nepovedlo. Zkuste to prosím znovu." instead of T3's text. - None of the turn's work shows at the end of the timeline, and Buddy's queue holds and counts only the person's own messages, never an agent's such as Delegace's wake. - The composer is a message box ("Napište Buddymu…") whose only choice is Hermes's Normal / Fast switch, the one option row 23's AcpT3Selection lets through, and Buddy's thread keeps full access, which the server guard also holds. - The surface never shows T3's terminal or right panel, their keys stay the browser's, and a link in the conversation opens in a new browser tab. The terminal and right-panel states are read inside the store selectors, so the React Compiler keeps ChatView's panel presence values memoized (chatViewPanelPresence.test.ts guards it, #134). On v0.1.3 upstream moved the work banners and the queue to the end of the timeline and removed the composer's task list (pingdotgg#25401). The work items (childInputNow, goalNow, backgroundWorkNow) are masked on Buddy's surface with inline conditionals in ChatView's body, where hasThreadNow, ThreadNow and the scroll button's summary read them, so no plain function wraps a hook result in render. buddyBackgroundWork is whether backgroundWorkNow exists. useBuddyQueuedRunCount drops an agent's queued message from the footer's count, which upstream's getUserQueuedThreadRuns keeps, and buddyWorkBanners and the task-list seam are gone. useBuddyChatShortcuts takes upstream's `!backgroundThread` condition (pingdotgg#24847), ChatTerminalDrawers lost focusRequestId (pingdotgg#25009), the placeholder falls back to DEFAULT_COMPOSER_PLACEHOLDER, and ReopenClosedViewShortcut stays off on the slimmer root. Effects carry justified react-effect-budget suppressions and comments are cut to the four-line limit. The error banner asks about the thread's own Environment (ChatBannerLane passes it), so a thread of another Environment keeps T3's own text. On v0.1.4: upstream dropped the resting composer's control strip, so Buddy's composer hook only gives the placeholder and hasControls is gone; Buddy's controls still replace T3's in ComposerControlsBar ahead of upstream's controls ownership. Upstream shows an approval on its own row (pingdotgg#26255); Buddy's mask leaves no work rows, so the approval falls to upstream's footer for unmatched approvals, and a browser test shows a pending approval there with Approve and Decline and no work row or quiet line. The link_request card stays off Buddy's surface, where link requests are refused (row 25). useChatShortcuts no longer exports its input type, so the overlay takes it from the hook. The tests follow upstream's props and labels: no activeTurnInProgress or onRollbackCheckpoint, the queue's "1 queued message", background work rows with turn items and "Running …" / "Waiting on …" titles, and a single subagent as a [data-v2-item-type=subagent] row. Effect suppressions use the renamed no-unreasoned-state-sync rule. Row 23's AcpT3Selection, row 25's host appearance and row 7's launcher clearance, which this commit carried on v0.1.3, are back in their rows. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Root decision 0210, addendum of 2026-10-22, point 1, and its plain-chat addendum (HumanAndMachines/Lazurio#648; plan DEV-6676, row 28): the person sees and hears only what Buddy writes in human words. In Buddy's surface (lazurioBuddy) only: - The timeline keeps the messages and what the person acts on; T3's record of the work stays out. While a turn runs, or work still runs after it, one quiet static line says "Buddy na tom pracuje…" / "Buddy is on it…". A failed turn shows one human line, and the thread's error banner says "Něco se nepovedlo. Zkuste to prosím znovu." instead of T3's text. - None of the turn's work shows at the end of the timeline, and Buddy's queue holds and counts only the person's own messages, never an agent's such as Delegace's wake. - The composer is a message box ("Napište Buddymu…") whose only choice is Hermes's Normal / Fast switch, the one option row 23's AcpT3Selection lets through, and Buddy's thread keeps full access, which the server guard also holds. - The surface never shows T3's terminal or right panel, their keys stay the browser's, and a link in the conversation opens in a new browser tab. The terminal and right-panel states are read inside the store selectors, so the React Compiler keeps ChatView's panel presence values memoized (chatViewPanelPresence.test.ts guards it, #134). On v0.1.3 upstream moved the work banners and the queue to the end of the timeline and removed the composer's task list (pingdotgg#25401). The work items (childInputNow, goalNow, backgroundWorkNow) are masked on Buddy's surface with inline conditionals in ChatView's body, where hasThreadNow, ThreadNow and the scroll button's summary read them, so no plain function wraps a hook result in render. buddyBackgroundWork is whether backgroundWorkNow exists. useBuddyQueuedRunCount drops an agent's queued message from the footer's count, which upstream's getUserQueuedThreadRuns keeps, and buddyWorkBanners and the task-list seam are gone. useBuddyChatShortcuts takes upstream's `!backgroundThread` condition (pingdotgg#24847), ChatTerminalDrawers lost focusRequestId (pingdotgg#25009), the placeholder falls back to DEFAULT_COMPOSER_PLACEHOLDER, and ReopenClosedViewShortcut stays off on the slimmer root. Effects carry justified react-effect-budget suppressions and comments are cut to the four-line limit. The error banner asks about the thread's own Environment (ChatBannerLane passes it), so a thread of another Environment keeps T3's own text. On v0.1.4: upstream dropped the resting composer's control strip, so Buddy's composer hook only gives the placeholder and hasControls is gone; Buddy's controls still replace T3's in ComposerControlsBar ahead of upstream's controls ownership. Upstream shows an approval on its own row (pingdotgg#26255); Buddy's mask leaves no work rows, so the approval falls to upstream's footer for unmatched approvals, and a browser test shows a pending approval there with Approve and Decline and no work row or quiet line. The link_request card stays off Buddy's surface, where link requests are refused (row 25). useChatShortcuts no longer exports its input type, so the overlay takes it from the hook. The tests follow upstream's props and labels: no activeTurnInProgress or onRollbackCheckpoint, the queue's "1 queued message", background work rows with turn items and "Running …" / "Waiting on …" titles, and a single subagent as a [data-v2-item-type=subagent] row. Effect suppressions use the renamed no-unreasoned-state-sync rule. Row 23's AcpT3Selection, row 25's host appearance and row 7's launcher clearance, which this commit carried on v0.1.3, are back in their rows. On v0.1.6 useThreadLifecycle returns attentionNow, which is the run's Stop once it went unanswered (Force stop, pingdotgg#27252) and the child's question otherwise, and workNow, which is the Stop while it is under way and the background work otherwise; backgroundWorkNow is gone. The inline mask hides the child's question, the work and the goal on Buddy's surface, while an unanswered Stop still shows there, so the person can force it; the contract test pins every mask line, including that one, and useThreadLifecycle's return. buddyBackgroundWork is whether workNow exists. Upstream's provider traits menu content (renderProviderTraitsMenuContent, pingdotgg#28057 and pingdotgg#28076) is not needed in ComposerControlsBar, which on Buddy's surface returns BuddyComposerControls before it. A side chat (pingdotgg#26901) renders ChatView embedded, so the thread tabs and terminal drawers are off for it as they are on Buddy's surface. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Root decision 0210, addendum of 2026-10-22, point 1, and its plain-chat addendum (HumanAndMachines/Lazurio#648; plan DEV-6676, row 28): the person sees and hears only what Buddy writes in human words. In Buddy's surface (lazurioBuddy) only: - The timeline keeps the messages and what the person acts on; T3's record of the work stays out. While a turn runs, or work still runs after it, one quiet static line says "Buddy na tom pracuje…" / "Buddy is on it…". A failed turn shows one human line, and the thread's error banner says "Něco se nepovedlo. Zkuste to prosím znovu." instead of T3's text. - None of the turn's work shows at the end of the timeline, and Buddy's queue holds and counts only the person's own messages, never an agent's such as Delegace's wake. - The composer is a message box ("Napište Buddymu…") whose only choice is Hermes's Normal / Fast switch, the one option row 23's AcpT3Selection lets through, and Buddy's thread keeps full access, which the server guard also holds. - The surface never shows T3's terminal or right panel, their keys stay the browser's, and a link in the conversation opens in a new browser tab. The terminal and right-panel states are read inside the store selectors, so the React Compiler keeps ChatView's panel presence values memoized (chatViewPanelPresence.test.ts guards it, #134). On v0.1.3 upstream moved the work banners and the queue to the end of the timeline and removed the composer's task list (pingdotgg#25401). The work items (childInputNow, goalNow, backgroundWorkNow) are masked on Buddy's surface with inline conditionals in ChatView's body, where hasThreadNow, ThreadNow and the scroll button's summary read them, so no plain function wraps a hook result in render. buddyBackgroundWork is whether backgroundWorkNow exists. useBuddyQueuedRunCount drops an agent's queued message from the footer's count, which upstream's getUserQueuedThreadRuns keeps, and buddyWorkBanners and the task-list seam are gone. useBuddyChatShortcuts takes upstream's `!backgroundThread` condition (pingdotgg#24847), ChatTerminalDrawers lost focusRequestId (pingdotgg#25009), the placeholder falls back to DEFAULT_COMPOSER_PLACEHOLDER, and ReopenClosedViewShortcut stays off on the slimmer root. Effects carry justified react-effect-budget suppressions and comments are cut to the four-line limit. The error banner asks about the thread's own Environment (ChatBannerLane passes it), so a thread of another Environment keeps T3's own text. On v0.1.4: upstream dropped the resting composer's control strip, so Buddy's composer hook only gives the placeholder and hasControls is gone; Buddy's controls still replace T3's in ComposerControlsBar ahead of upstream's controls ownership. Upstream shows an approval on its own row (pingdotgg#26255); Buddy's mask leaves no work rows, so the approval falls to upstream's footer for unmatched approvals, and a browser test shows a pending approval there with Approve and Decline and no work row or quiet line. The link_request card stays off Buddy's surface, where link requests are refused (row 25). useChatShortcuts no longer exports its input type, so the overlay takes it from the hook. The tests follow upstream's props and labels: no activeTurnInProgress or onRollbackCheckpoint, the queue's "1 queued message", background work rows with turn items and "Running …" / "Waiting on …" titles, and a single subagent as a [data-v2-item-type=subagent] row. Effect suppressions use the renamed no-unreasoned-state-sync rule. Row 23's AcpT3Selection, row 25's host appearance and row 7's launcher clearance, which this commit carried on v0.1.3, are back in their rows. On v0.1.6 useThreadLifecycle returns attentionNow, which is the run's Stop once it went unanswered (Force stop, pingdotgg#27252) and the child's question otherwise, and workNow, which is the Stop while it is under way and the background work otherwise; backgroundWorkNow is gone. The inline mask hides the child's question, the work and the goal on Buddy's surface, while an unanswered Stop still shows there, so the person can force it; the contract test pins every mask line, including that one, and useThreadLifecycle's return. buddyBackgroundWork is whether workNow exists. Upstream's provider traits menu content (renderProviderTraitsMenuContent, pingdotgg#28057 and pingdotgg#28076) is not needed in ComposerControlsBar, which on Buddy's surface returns BuddyComposerControls before it. A side chat (pingdotgg#26901) renders ChatView embedded, so the thread tabs and terminal drawers are off for it as they are on Buddy's surface. On v0.1.7: upstream wraps the timeline in `WorkspaceMutationIdContext`, so `buddyBackgroundWork` sits inside it after `isWorking`. Upstream now passes `environmentId` to the banner lane itself, so the overlay drops its own copy of that prop and only uses it for Buddy's error banner. The work line outside Buddy reads "Running · <label>" and "Waiting · <label>" on v0.1.7, and the timeline test pins those. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Root decision 0210, addendum of 2026-10-22, point 1, and its plain-chat addendum (HumanAndMachines/Lazurio#648; plan DEV-6676, row 28): the person sees and hears only what Buddy writes in human words. In Buddy's surface (lazurioBuddy) only: - The timeline keeps the messages and what the person acts on; T3's record of the work stays out. While a turn runs, or work still runs after it, one quiet static line says "Buddy na tom pracuje…" / "Buddy is on it…". A failed turn shows one human line, and the thread's error banner says "Něco se nepovedlo. Zkuste to prosím znovu." instead of T3's text. - None of the turn's work shows at the end of the timeline, and Buddy's queue holds and counts only the person's own messages, never an agent's such as Delegace's wake. - The composer is a message box ("Napište Buddymu…") whose only choice is Hermes's Normal / Fast switch, the one option row 23's AcpT3Selection lets through, and Buddy's thread keeps full access, which the server guard also holds. - The surface never shows T3's terminal or right panel, their keys stay the browser's, and a link in the conversation opens in a new browser tab. The terminal and right-panel states are read inside the store selectors, so the React Compiler keeps ChatView's panel presence values memoized (chatViewPanelPresence.test.ts guards it, #134). On v0.1.3 upstream moved the work banners and the queue to the end of the timeline and removed the composer's task list (pingdotgg#25401). The work items (childInputNow, goalNow, backgroundWorkNow) are masked on Buddy's surface with inline conditionals in ChatView's body, where hasThreadNow, ThreadNow and the scroll button's summary read them, so no plain function wraps a hook result in render. buddyBackgroundWork is whether backgroundWorkNow exists. useBuddyQueuedRunCount drops an agent's queued message from the footer's count, which upstream's getUserQueuedThreadRuns keeps, and buddyWorkBanners and the task-list seam are gone. useBuddyChatShortcuts takes upstream's `!backgroundThread` condition (pingdotgg#24847), ChatTerminalDrawers lost focusRequestId (pingdotgg#25009), the placeholder falls back to DEFAULT_COMPOSER_PLACEHOLDER, and ReopenClosedViewShortcut stays off on the slimmer root. Effects carry justified react-effect-budget suppressions and comments are cut to the four-line limit. The error banner asks about the thread's own Environment (ChatBannerLane passes it), so a thread of another Environment keeps T3's own text. On v0.1.4: upstream dropped the resting composer's control strip, so Buddy's composer hook only gives the placeholder and hasControls is gone; Buddy's controls still replace T3's in ComposerControlsBar ahead of upstream's controls ownership. Upstream shows an approval on its own row (pingdotgg#26255); Buddy's mask leaves no work rows, so the approval falls to upstream's footer for unmatched approvals, and a browser test shows a pending approval there with Approve and Decline and no work row or quiet line. The link_request card stays off Buddy's surface, where link requests are refused (row 25). useChatShortcuts no longer exports its input type, so the overlay takes it from the hook. The tests follow upstream's props and labels: no activeTurnInProgress or onRollbackCheckpoint, the queue's "1 queued message", background work rows with turn items and "Running …" / "Waiting on …" titles, and a single subagent as a [data-v2-item-type=subagent] row. Effect suppressions use the renamed no-unreasoned-state-sync rule. Row 23's AcpT3Selection, row 25's host appearance and row 7's launcher clearance, which this commit carried on v0.1.3, are back in their rows. On v0.1.6 useThreadLifecycle returns attentionNow, which is the run's Stop once it went unanswered (Force stop, pingdotgg#27252) and the child's question otherwise, and workNow, which is the Stop while it is under way and the background work otherwise; backgroundWorkNow is gone. The inline mask hides the child's question, the work and the goal on Buddy's surface, while an unanswered Stop still shows there, so the person can force it; the contract test pins every mask line, including that one, and useThreadLifecycle's return. buddyBackgroundWork is whether workNow exists. Upstream's provider traits menu content (renderProviderTraitsMenuContent, pingdotgg#28057 and pingdotgg#28076) is not needed in ComposerControlsBar, which on Buddy's surface returns BuddyComposerControls before it. A side chat (pingdotgg#26901) renders ChatView embedded, so the thread tabs and terminal drawers are off for it as they are on Buddy's surface. On v0.1.7: upstream wraps the timeline in `WorkspaceMutationIdContext`, so `buddyBackgroundWork` sits inside it after `isWorking`. Upstream now passes `environmentId` to the banner lane itself, so the overlay drops its own copy of that prop and only uses it for Buddy's error banner. The work line outside Buddy reads "Running · <label>" and "Waiting · <label>" on v0.1.7, and the timeline test pins those. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Root decision 0210, addendum of 2026-10-22, point 1, and its plain-chat addendum (HumanAndMachines/Lazurio#648; plan DEV-6676, row 28): the person sees and hears only what Buddy writes in human words. In Buddy's surface (lazurioBuddy) only: - The timeline keeps the messages and what the person acts on; T3's record of the work stays out. While a turn runs, or work still runs after it, one quiet static line says "Buddy na tom pracuje…" / "Buddy is on it…". A failed turn shows one human line, and the thread's error banner says "Něco se nepovedlo. Zkuste to prosím znovu." instead of T3's text. - None of the turn's work shows at the end of the timeline, and Buddy's queue holds and counts only the person's own messages, never an agent's such as Delegace's wake. - The composer is a message box ("Napište Buddymu…") whose only choice is Hermes's Normal / Fast switch, the one option row 23's AcpT3Selection lets through, and Buddy's thread keeps full access, which the server guard also holds. - The surface never shows T3's terminal or right panel, their keys stay the browser's, and a link in the conversation opens in a new browser tab. The terminal and right-panel states are read inside the store selectors, so the React Compiler keeps ChatView's panel presence values memoized (chatViewPanelPresence.test.ts guards it, #134). On v0.1.3 upstream moved the work banners and the queue to the end of the timeline and removed the composer's task list (pingdotgg#25401). The work items (childInputNow, goalNow, backgroundWorkNow) are masked on Buddy's surface with inline conditionals in ChatView's body, where hasThreadNow, ThreadNow and the scroll button's summary read them, so no plain function wraps a hook result in render. buddyBackgroundWork is whether backgroundWorkNow exists. useBuddyQueuedRunCount drops an agent's queued message from the footer's count, which upstream's getUserQueuedThreadRuns keeps, and buddyWorkBanners and the task-list seam are gone. useBuddyChatShortcuts takes upstream's `!backgroundThread` condition (pingdotgg#24847), ChatTerminalDrawers lost focusRequestId (pingdotgg#25009), the placeholder falls back to DEFAULT_COMPOSER_PLACEHOLDER, and ReopenClosedViewShortcut stays off on the slimmer root. Effects carry justified react-effect-budget suppressions and comments are cut to the four-line limit. The error banner asks about the thread's own Environment (ChatBannerLane passes it), so a thread of another Environment keeps T3's own text. On v0.1.4: upstream dropped the resting composer's control strip, so Buddy's composer hook only gives the placeholder and hasControls is gone; Buddy's controls still replace T3's in ComposerControlsBar ahead of upstream's controls ownership. Upstream shows an approval on its own row (pingdotgg#26255); Buddy's mask leaves no work rows, so the approval falls to upstream's footer for unmatched approvals, and a browser test shows a pending approval there with Approve and Decline and no work row or quiet line. The link_request card stays off Buddy's surface, where link requests are refused (row 25). useChatShortcuts no longer exports its input type, so the overlay takes it from the hook. The tests follow upstream's props and labels: no activeTurnInProgress or onRollbackCheckpoint, the queue's "1 queued message", background work rows with turn items and "Running …" / "Waiting on …" titles, and a single subagent as a [data-v2-item-type=subagent] row. Effect suppressions use the renamed no-unreasoned-state-sync rule. Row 23's AcpT3Selection, row 25's host appearance and row 7's launcher clearance, which this commit carried on v0.1.3, are back in their rows. On v0.1.6 useThreadLifecycle returns attentionNow, which is the run's Stop once it went unanswered (Force stop, pingdotgg#27252) and the child's question otherwise, and workNow, which is the Stop while it is under way and the background work otherwise; backgroundWorkNow is gone. The inline mask hides the child's question, the work and the goal on Buddy's surface, while an unanswered Stop still shows there, so the person can force it; the contract test pins every mask line, including that one, and useThreadLifecycle's return. buddyBackgroundWork is whether workNow exists. Upstream's provider traits menu content (renderProviderTraitsMenuContent, pingdotgg#28057 and pingdotgg#28076) is not needed in ComposerControlsBar, which on Buddy's surface returns BuddyComposerControls before it. A side chat (pingdotgg#26901) renders ChatView embedded, so the thread tabs and terminal drawers are off for it as they are on Buddy's surface. On v0.1.7: upstream wraps the timeline in `WorkspaceMutationIdContext`, so `buddyBackgroundWork` sits inside it after `isWorking`. Upstream now passes `environmentId` to the banner lane itself, so the overlay drops its own copy of that prop and only uses it for Buddy's error banner. The work line outside Buddy reads "Running · <label>" and "Waiting · <label>" on v0.1.7, and the timeline test pins those. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Root decision 0210, addendum of 2026-10-22, point 1, and its plain-chat addendum (HumanAndMachines/Lazurio#648; plan DEV-6676, row 28): the person sees and hears only what Buddy writes in human words. In Buddy's surface (lazurioBuddy) only: - The timeline keeps the messages and what the person acts on; T3's record of the work stays out. While a turn runs, or work still runs after it, one quiet static line says "Buddy na tom pracuje…" / "Buddy is on it…". A failed turn shows one human line, and the thread's error banner says "Něco se nepovedlo. Zkuste to prosím znovu." instead of T3's text. - None of the turn's work shows at the end of the timeline, and Buddy's queue holds and counts only the person's own messages, never an agent's such as Delegace's wake. - The composer is a message box ("Napište Buddymu…") whose only choice is Hermes's Normal / Fast switch, the one option row 23's AcpT3Selection lets through, and Buddy's thread keeps full access, which the server guard also holds. - The surface never shows T3's terminal or right panel, their keys stay the browser's, and a link in the conversation opens in a new browser tab. The terminal and right-panel states are read inside the store selectors, so the React Compiler keeps ChatView's panel presence values memoized (chatViewPanelPresence.test.ts guards it, #134). On v0.1.3 upstream moved the work banners and the queue to the end of the timeline and removed the composer's task list (pingdotgg#25401). The work items (childInputNow, goalNow, backgroundWorkNow) are masked on Buddy's surface with inline conditionals in ChatView's body, where hasThreadNow, ThreadNow and the scroll button's summary read them, so no plain function wraps a hook result in render. buddyBackgroundWork is whether backgroundWorkNow exists. useBuddyQueuedRunCount drops an agent's queued message from the footer's count, which upstream's getUserQueuedThreadRuns keeps, and buddyWorkBanners and the task-list seam are gone. useBuddyChatShortcuts takes upstream's `!backgroundThread` condition (pingdotgg#24847), ChatTerminalDrawers lost focusRequestId (pingdotgg#25009), the placeholder falls back to DEFAULT_COMPOSER_PLACEHOLDER, and ReopenClosedViewShortcut stays off on the slimmer root. Effects carry justified react-effect-budget suppressions and comments are cut to the four-line limit. The error banner asks about the thread's own Environment (ChatBannerLane passes it), so a thread of another Environment keeps T3's own text. On v0.1.4: upstream dropped the resting composer's control strip, so Buddy's composer hook only gives the placeholder and hasControls is gone; Buddy's controls still replace T3's in ComposerControlsBar ahead of upstream's controls ownership. Upstream shows an approval on its own row (pingdotgg#26255); Buddy's mask leaves no work rows, so the approval falls to upstream's footer for unmatched approvals, and a browser test shows a pending approval there with Approve and Decline and no work row or quiet line. The link_request card stays off Buddy's surface, where link requests are refused (row 25). useChatShortcuts no longer exports its input type, so the overlay takes it from the hook. The tests follow upstream's props and labels: no activeTurnInProgress or onRollbackCheckpoint, the queue's "1 queued message", background work rows with turn items and "Running …" / "Waiting on …" titles, and a single subagent as a [data-v2-item-type=subagent] row. Effect suppressions use the renamed no-unreasoned-state-sync rule. Row 23's AcpT3Selection, row 25's host appearance and row 7's launcher clearance, which this commit carried on v0.1.3, are back in their rows. On v0.1.6 useThreadLifecycle returns attentionNow, which is the run's Stop once it went unanswered (Force stop, pingdotgg#27252) and the child's question otherwise, and workNow, which is the Stop while it is under way and the background work otherwise; backgroundWorkNow is gone. The inline mask hides the child's question, the work and the goal on Buddy's surface, while an unanswered Stop still shows there, so the person can force it; the contract test pins every mask line, including that one, and useThreadLifecycle's return. buddyBackgroundWork is whether workNow exists. Upstream's provider traits menu content (renderProviderTraitsMenuContent, pingdotgg#28057 and pingdotgg#28076) is not needed in ComposerControlsBar, which on Buddy's surface returns BuddyComposerControls before it. A side chat (pingdotgg#26901) renders ChatView embedded, so the thread tabs and terminal drawers are off for it as they are on Buddy's surface. On v0.1.7: upstream wraps the timeline in `WorkspaceMutationIdContext`, so `buddyBackgroundWork` sits inside it after `isWorking`. Upstream now passes `environmentId` to the banner lane itself, so the overlay drops its own copy of that prop and only uses it for Buddy's error banner. The work line outside Buddy reads "Running · <label>" and "Waiting · <label>" on v0.1.7, and the timeline test pins those. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Root decision 0210, addendum of 2026-10-22, point 1, and its plain-chat addendum (HumanAndMachines/Lazurio#648; plan DEV-6676, row 28): the person sees and hears only what Buddy writes in human words. In Buddy's surface (lazurioBuddy) only: - The timeline keeps the messages and what the person acts on; T3's record of the work stays out. While a turn runs, or work still runs after it, one quiet static line says "Buddy na tom pracuje…" / "Buddy is on it…". A failed turn shows one human line, and the thread's error banner says "Něco se nepovedlo. Zkuste to prosím znovu." instead of T3's text. - None of the turn's work shows at the end of the timeline, and Buddy's queue holds and counts only the person's own messages, never an agent's such as Delegace's wake. - The composer is a message box ("Napište Buddymu…") whose only choice is Hermes's Normal / Fast switch, the one option row 23's AcpT3Selection lets through, and Buddy's thread keeps full access, which the server guard also holds. - The surface never shows T3's terminal or right panel, their keys stay the browser's, and a link in the conversation opens in a new browser tab. The terminal and right-panel states are read inside the store selectors, so the React Compiler keeps ChatView's panel presence values memoized (chatViewPanelPresence.test.ts guards it, #134). On v0.1.3 upstream moved the work banners and the queue to the end of the timeline and removed the composer's task list (pingdotgg#25401). The work items (childInputNow, goalNow, backgroundWorkNow) are masked on Buddy's surface with inline conditionals in ChatView's body, where hasThreadNow, ThreadNow and the scroll button's summary read them, so no plain function wraps a hook result in render. buddyBackgroundWork is whether backgroundWorkNow exists. useBuddyQueuedRunCount drops an agent's queued message from the footer's count, which upstream's getUserQueuedThreadRuns keeps, and buddyWorkBanners and the task-list seam are gone. useBuddyChatShortcuts takes upstream's `!backgroundThread` condition (pingdotgg#24847), ChatTerminalDrawers lost focusRequestId (pingdotgg#25009), the placeholder falls back to DEFAULT_COMPOSER_PLACEHOLDER, and ReopenClosedViewShortcut stays off on the slimmer root. Effects carry justified react-effect-budget suppressions and comments are cut to the four-line limit. The error banner asks about the thread's own Environment (ChatBannerLane passes it), so a thread of another Environment keeps T3's own text. On v0.1.4: upstream dropped the resting composer's control strip, so Buddy's composer hook only gives the placeholder and hasControls is gone; Buddy's controls still replace T3's in ComposerControlsBar ahead of upstream's controls ownership. Upstream shows an approval on its own row (pingdotgg#26255); Buddy's mask leaves no work rows, so the approval falls to upstream's footer for unmatched approvals, and a browser test shows a pending approval there with Approve and Decline and no work row or quiet line. The link_request card stays off Buddy's surface, where link requests are refused (row 25). useChatShortcuts no longer exports its input type, so the overlay takes it from the hook. The tests follow upstream's props and labels: no activeTurnInProgress or onRollbackCheckpoint, the queue's "1 queued message", background work rows with turn items and "Running …" / "Waiting on …" titles, and a single subagent as a [data-v2-item-type=subagent] row. Effect suppressions use the renamed no-unreasoned-state-sync rule. Row 23's AcpT3Selection, row 25's host appearance and row 7's launcher clearance, which this commit carried on v0.1.3, are back in their rows. On v0.1.6 useThreadLifecycle returns attentionNow, which is the run's Stop once it went unanswered (Force stop, pingdotgg#27252) and the child's question otherwise, and workNow, which is the Stop while it is under way and the background work otherwise; backgroundWorkNow is gone. The inline mask hides the child's question, the work and the goal on Buddy's surface, while an unanswered Stop still shows there, so the person can force it; the contract test pins every mask line, including that one, and useThreadLifecycle's return. buddyBackgroundWork is whether workNow exists. Upstream's provider traits menu content (renderProviderTraitsMenuContent, pingdotgg#28057 and pingdotgg#28076) is not needed in ComposerControlsBar, which on Buddy's surface returns BuddyComposerControls before it. A side chat (pingdotgg#26901) renders ChatView embedded, so the thread tabs and terminal drawers are off for it as they are on Buddy's surface. On v0.1.7: upstream wraps the timeline in `WorkspaceMutationIdContext`, so `buddyBackgroundWork` sits inside it after `isWorking`. Upstream now passes `environmentId` to the banner lane itself, so the overlay drops its own copy of that prop and only uses it for Buddy's error banner. The work line outside Buddy reads "Running · <label>" and "Waiting · <label>" on v0.1.7, and the timeline test pins those. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Root decision 0210, addendum of 2026-10-22, point 1, and its plain-chat addendum (HumanAndMachines/Lazurio#648; plan DEV-6676, row 28): the person sees and hears only what Buddy writes in human words. In Buddy's surface (lazurioBuddy) only: - The timeline keeps the messages and what the person acts on; T3's record of the work stays out. While a turn runs, or work still runs after it, one quiet static line says "Buddy na tom pracuje…" / "Buddy is on it…". A failed turn shows one human line, and the thread's error banner says "Něco se nepovedlo. Zkuste to prosím znovu." instead of T3's text. - None of the turn's work shows at the end of the timeline, and Buddy's queue holds and counts only the person's own messages, never an agent's such as Delegace's wake. - The composer is a message box ("Napište Buddymu…") whose only choice is Hermes's Normal / Fast switch, the one option row 23's AcpT3Selection lets through, and Buddy's thread keeps full access, which the server guard also holds. - The surface never shows T3's terminal or right panel, their keys stay the browser's, and a link in the conversation opens in a new browser tab. The terminal and right-panel states are read inside the store selectors, so the React Compiler keeps ChatView's panel presence values memoized (chatViewPanelPresence.test.ts guards it, #134). On v0.1.3 upstream moved the work banners and the queue to the end of the timeline and removed the composer's task list (pingdotgg#25401). The work items (childInputNow, goalNow, backgroundWorkNow) are masked on Buddy's surface with inline conditionals in ChatView's body, where hasThreadNow, ThreadNow and the scroll button's summary read them, so no plain function wraps a hook result in render. buddyBackgroundWork is whether backgroundWorkNow exists. useBuddyQueuedRunCount drops an agent's queued message from the footer's count, which upstream's getUserQueuedThreadRuns keeps, and buddyWorkBanners and the task-list seam are gone. useBuddyChatShortcuts takes upstream's `!backgroundThread` condition (pingdotgg#24847), ChatTerminalDrawers lost focusRequestId (pingdotgg#25009), the placeholder falls back to DEFAULT_COMPOSER_PLACEHOLDER, and ReopenClosedViewShortcut stays off on the slimmer root. Effects carry justified react-effect-budget suppressions and comments are cut to the four-line limit. The error banner asks about the thread's own Environment (ChatBannerLane passes it), so a thread of another Environment keeps T3's own text. On v0.1.4: upstream dropped the resting composer's control strip, so Buddy's composer hook only gives the placeholder and hasControls is gone; Buddy's controls still replace T3's in ComposerControlsBar ahead of upstream's controls ownership. Upstream shows an approval on its own row (pingdotgg#26255); Buddy's mask leaves no work rows, so the approval falls to upstream's footer for unmatched approvals, and a browser test shows a pending approval there with Approve and Decline and no work row or quiet line. The link_request card stays off Buddy's surface, where link requests are refused (row 25). useChatShortcuts no longer exports its input type, so the overlay takes it from the hook. The tests follow upstream's props and labels: no activeTurnInProgress or onRollbackCheckpoint, the queue's "1 queued message", background work rows with turn items and "Running …" / "Waiting on …" titles, and a single subagent as a [data-v2-item-type=subagent] row. Effect suppressions use the renamed no-unreasoned-state-sync rule. Row 23's AcpT3Selection, row 25's host appearance and row 7's launcher clearance, which this commit carried on v0.1.3, are back in their rows. On v0.1.6 useThreadLifecycle returns attentionNow, which is the run's Stop once it went unanswered (Force stop, pingdotgg#27252) and the child's question otherwise, and workNow, which is the Stop while it is under way and the background work otherwise; backgroundWorkNow is gone. The inline mask hides the child's question, the work and the goal on Buddy's surface, while an unanswered Stop still shows there, so the person can force it; the contract test pins every mask line, including that one, and useThreadLifecycle's return. buddyBackgroundWork is whether workNow exists. Upstream's provider traits menu content (renderProviderTraitsMenuContent, pingdotgg#28057 and pingdotgg#28076) is not needed in ComposerControlsBar, which on Buddy's surface returns BuddyComposerControls before it. A side chat (pingdotgg#26901) renders ChatView embedded, so the thread tabs and terminal drawers are off for it as they are on Buddy's surface. On v0.1.7: upstream wraps the timeline in `WorkspaceMutationIdContext`, so `buddyBackgroundWork` sits inside it after `isWorking`. Upstream now passes `environmentId` to the banner lane itself, so the overlay drops its own copy of that prop and only uses it for Buddy's error banner. The work line outside Buddy reads "Running · <label>" and "Waiting · <label>" on v0.1.7, and the timeline test pins those. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Root decision 0210, addendum of 2026-10-22, point 1, and its plain-chat addendum (HumanAndMachines/Lazurio#648; plan DEV-6676, row 28): the person sees and hears only what Buddy writes in human words. In Buddy's surface (lazurioBuddy) only: - The timeline keeps the messages and what the person acts on; T3's record of the work stays out. While a turn runs, or work still runs after it, one quiet static line says "Buddy na tom pracuje…" / "Buddy is on it…". A failed turn shows one human line, and the thread's error banner says "Něco se nepovedlo. Zkuste to prosím znovu." instead of T3's text. - None of the turn's work shows at the end of the timeline, and Buddy's queue holds and counts only the person's own messages, never an agent's such as Delegace's wake. - The composer is a message box ("Napište Buddymu…") whose only choice is Hermes's Normal / Fast switch, the one option row 23's AcpT3Selection lets through, and Buddy's thread keeps full access, which the server guard also holds. - The surface never shows T3's terminal or right panel, their keys stay the browser's, and a link in the conversation opens in a new browser tab. The terminal and right-panel states are read inside the store selectors, so the React Compiler keeps ChatView's panel presence values memoized (chatViewPanelPresence.test.ts guards it, #134). On v0.1.3 upstream moved the work banners and the queue to the end of the timeline and removed the composer's task list (pingdotgg#25401). The work items (childInputNow, goalNow, backgroundWorkNow) are masked on Buddy's surface with inline conditionals in ChatView's body, where hasThreadNow, ThreadNow and the scroll button's summary read them, so no plain function wraps a hook result in render. buddyBackgroundWork is whether backgroundWorkNow exists. useBuddyQueuedRunCount drops an agent's queued message from the footer's count, which upstream's getUserQueuedThreadRuns keeps, and buddyWorkBanners and the task-list seam are gone. useBuddyChatShortcuts takes upstream's `!backgroundThread` condition (pingdotgg#24847), ChatTerminalDrawers lost focusRequestId (pingdotgg#25009), the placeholder falls back to DEFAULT_COMPOSER_PLACEHOLDER, and ReopenClosedViewShortcut stays off on the slimmer root. Effects carry justified react-effect-budget suppressions and comments are cut to the four-line limit. The error banner asks about the thread's own Environment (ChatBannerLane passes it), so a thread of another Environment keeps T3's own text. On v0.1.4: upstream dropped the resting composer's control strip, so Buddy's composer hook only gives the placeholder and hasControls is gone; Buddy's controls still replace T3's in ComposerControlsBar ahead of upstream's controls ownership. Upstream shows an approval on its own row (pingdotgg#26255); Buddy's mask leaves no work rows, so the approval falls to upstream's footer for unmatched approvals, and a browser test shows a pending approval there with Approve and Decline and no work row or quiet line. The link_request card stays off Buddy's surface, where link requests are refused (row 25). useChatShortcuts no longer exports its input type, so the overlay takes it from the hook. The tests follow upstream's props and labels: no activeTurnInProgress or onRollbackCheckpoint, the queue's "1 queued message", background work rows with turn items and "Running …" / "Waiting on …" titles, and a single subagent as a [data-v2-item-type=subagent] row. Effect suppressions use the renamed no-unreasoned-state-sync rule. Row 23's AcpT3Selection, row 25's host appearance and row 7's launcher clearance, which this commit carried on v0.1.3, are back in their rows. On v0.1.6 useThreadLifecycle returns attentionNow, which is the run's Stop once it went unanswered (Force stop, pingdotgg#27252) and the child's question otherwise, and workNow, which is the Stop while it is under way and the background work otherwise; backgroundWorkNow is gone. The inline mask hides the child's question, the work and the goal on Buddy's surface, while an unanswered Stop still shows there, so the person can force it; the contract test pins every mask line, including that one, and useThreadLifecycle's return. buddyBackgroundWork is whether workNow exists. Upstream's provider traits menu content (renderProviderTraitsMenuContent, pingdotgg#28057 and pingdotgg#28076) is not needed in ComposerControlsBar, which on Buddy's surface returns BuddyComposerControls before it. A side chat (pingdotgg#26901) renders ChatView embedded, so the thread tabs and terminal drawers are off for it as they are on Buddy's surface. On v0.1.7: upstream wraps the timeline in `WorkspaceMutationIdContext`, so `buddyBackgroundWork` sits inside it after `isWorking`. Upstream now passes `environmentId` to the banner lane itself, so the overlay drops its own copy of that prop and only uses it for Buddy's error banner. The work line outside Buddy reads "Running · <label>" and "Waiting · <label>" on v0.1.7, and the timeline test pins those. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Status: ready for review. Head
ccb3eb7e90adds the regression test Henry asked for. Lazurio Fork CI is running on it (it was green on027350c75c).Fixes #135.
The problem
Since
0.1.2-lazurio.5, clicking Toggle right panel in Chat crashes the web app with "Something went wrong", React error pingdotgg#301 (too many re-renders). The panel's open state is persisted per thread, so that thread then crashes on every load. Other threads keep working. It was reported on a work Environment and reproduced on the Ring 0 reference Environment, both on.5.Cause
f607827395made ChatView read the thread's panel through a plain function:buddyRightPanelState(buddySurface, useRightPanelStore(...)).{ activeSurface, surfaces: rightPanelState.surfaces }built from it.usePanelPresencesets state during render whenever that value changes identity. With a fresh object on every render, an open panel looped until React gave up.The compiled output shows it: in
.4the value sits in a memo slot, in.5it is a bare object literal..4is unaffected.The fix
8bfef1cb5gets the same shape.What does not change
Verification
ChatView.tsxwith the same React Compiler the build uses shows the presence value memoized again. ChatView has 895 memo slots, against 858 in.5and 892 in.4.reactCompilerCoveragefor ChatView passes;buddySurfaceRightPanelandbuddySurfaceTerminalbrowser tests pass (4), andbuddySurfaceControlspasses (5);apps/web/src/lazurio/chatViewPanelPresence.test.ts, run by the fork CI's overlay gate):ChatView.tsxwith the build's React Compiler options and requires every value ChatView handsusePanelPresenceto be a memo slot's temporary, never an object literal built on every render. That object caused both Homebrew Codex Unrecognized pingdotgg/t3code#301 paths: opening the panel, and loading a thread whose panel was saved open.f607827395with "rightPanelPresenceValue is built on every render" and passes with the fix..5today.Release
Ships as
0.1.2-lazurio.6: preview, then a Ring 0 browser check of the right panel, then stable on the Admin's go. Until then,.5goes to no further Environments.🤖 Generated with Claude Code