Skip to content

[Bug]: the required dashboard-browser lane is red on main — a steward view switch drops the live turn's elapsed receipt #7384

Open

Description

@JasonBuildAI

Preflight

  • I searched existing issues and discussions for this behavior.
  • This report is not an unpatched security vulnerability.
  • The report and attachments contain no credentials or private/internal material.

Issue origin

Reproduced in a real environment (native Windows source checkout) and confirmed against the hosted required lane.

LoopX version or commit

93bb1ed2794685aec8325142d4b9a31860e93428 (current main, LoopX 2.1.0). Regression range starts at 6275d180366bcd0963073d7e6b0b7005b0b6d79e (merge of #7320, 7a48d6709).

Host or runtime surface

Dashboard personal workspace (browser acceptance). Not host-specific; reproduced on native Windows 11 / CPython 3.14.3 / Node 26.11.1 with the Playwright Chromium headless shell, and observed on the hosted Ubuntu runner.

LoopX area

Presentation (dashboard personal workspace chat / overview receipt).

Problem

The required dashboard-browser job of Python Tests is deterministically red on main, so dashboard-acceptance fails, checks fails and merge-gate fails for every pull request that triggers the frontend qualification. The failing acceptance scenario is conversation-activity, whose stated guarantee is "view switch keeps request elapsed time".

Two separate facts combine:

  1. Switching the steward view to 总览 changes the React parent of PersonalGoalHome: dashboard-page.tsx renders home directly, or inside AppStewardEntry when defaultStewardEntry is true (the entry is selected by search.view === "conversation" with no binding; introduced by feat(chat): open the selected original steward from App Conversation #7320, 7a48d6709). Flipping the view therefore remounts PersonalGoalHome and drops the live message state.
  2. On remount the history recovery path (dashboard-page.tsx, appendManagerAssistantMessage call around lines 1842-1855) re-creates the pending assistant receipt and derives startedAt only from activeSnapshot?.active_turn?.created_at. The browser fixture's session snapshot returns active_turn without created_at (examples/personal-workspace-browser/fixture.mjs:1849), while the production snapshot returns the stored turn record, which carries created_at (loopx/chat_store.py:1976-1994). With no start time, MessageActivity renders no .personal-message-elapsed (message-activity.tsx:27-30,45), so the overview tray never shows the live turn's elapsed time.

The current failure is therefore a state-loss regression from the App chat entry, not a slow-runner flake: the same scenario passes before #7320 and fails at every commit since.

Who is harmed and how much. Every pull request whose change plan includes frontend qualification inherits a failing required check; the merged #7377 shows dashboard-browser (1) and (2), checks, pytest and merge-gate failing while the PR was merged with admin bypass, so the normal merge path is blocked. The maintainer must manually override the gate for each merge instead of reading it as a signal. For users, switching from the steward chat to the overview during an in-flight turn re-creates the receipt, loses the elapsed/quiet feedback and makes the two views disagree about the same request.

Minimal reproduction

From a source checkout of 93bb1ed27:

npm ci --ignore-scripts
npm ci --ignore-scripts --prefix apps/presentation/dashboard
(cd apps/presentation/dashboard && npx playwright install chromium --only-shell)
LOOPX_PERSONAL_WORKSPACE_SCENARIO=conversation-activity \
LOOPX_PLAYWRIGHT_PACKAGE="$PWD/apps/presentation/dashboard/node_modules/playwright" \
node examples/personal-workspace-browser-smoke.mjs

Observed at 93bb1ed27: 3/3 runs fail after about 33 s.

scenario-start=conversation-activity
scenario-end=conversation-activity duration_ms=32974
locator.textContent: Timeout 30000ms exceeded.
Call log:
  - waiting for locator('.personal-manager-conversation-tray').locator('.personal-message-elapsed')
    at Object.run (.../examples/personal-workspace-browser/conversation-activity.mjs:55:68)

Debug state of the same message in the two views (instrumented locally, reverted):

chat view:  {id: "2", pending: "true", started: "1793009124521", elapsed: "已用时 25秒"}
overview:   {id: "2", pending: "true", started: "undefined",   elapsed: null}

The tray message is created by the recovery path: forcing that path's startedAt to a sentinel made the tray render the sentinel.

Before/after range (same command, same host):

06f51b7e4 (parent of the #7320 merge): ok, duration_ms=14337
6275d1803 (#7320 merge):               fails at conversation-activity.mjs:43 waiting for the composer
93bb1ed27 (current main):              fails at conversation-activity.mjs:55 waiting for the tray elapsed element

Hosted evidence:

Expected behavior

  • The required dashboard-browser job is green on main for an unchanged checkout.
  • Switching from the steward chat to the overview keeps the same pending receipt and its elapsed/quiet reading, as the scenario asserts: the elapsed text in the overview tray equals the chat view's.

Actual behavior

  • The required lane fails deterministically, and checks/merge-gate fail with it.
  • The overview re-creates the pending receipt without a start time, so .personal-message-elapsed never appears and the tray shows no elapsed reading.

Sanitized diagnostics

$ LOOPX_PERSONAL_WORKSPACE_SCENARIO=conversation-activity \
  LOOPX_PLAYWRIGHT_PACKAGE=<dashboard>/node_modules/playwright \
  node examples/personal-workspace-browser-smoke.mjs
scenario-end=conversation-activity duration_ms=32974
locator.textContent: Timeout 30000ms exceeded.
  - waiting for locator('.personal-manager-conversation-tray').locator('.personal-message-elapsed')
    at Object.run (<repo>/examples/personal-workspace-browser/conversation-activity.mjs:55:68)
# same scenario at the parent of the #7320 merge
$ git checkout 06f51b7e4 && node examples/personal-workspace-browser-smoke.mjs
personal-workspace-browser-smoke (development): ok
scenarios=conversation-activity

Additional context


AI disclosure: this report was prepared with AI assistance at the account owner's direction. The reproduction commands, local runs and before/after checks above were executed in this environment; the owner set the contribution constraints and will review any follow-up change.

Activity

  1. JasonBuildAI commented on Oct 26, 2026

    @JasonBuildAI
    ContributorAuthor

    Second required shard, same regression range, reproduced locally.

    dashboard-browser (2) fails in composer-session-admission (examples/personal-workspace-browser/composer-session-admission.mjs:136):

    Error: Another conversation's pending instruction blocks this composer's Send
        at Object.run (.../examples/personal-workspace-browser/composer-session-admission.mjs:136:48)
    

    Hosted evidence: run 40297894327, job 121489050029, 2026-10-26T09:17:36Z (after composer-session-admission ended at 8176 ms). Local, single scenario:

    06f51b7e4 (parent of #7320): ok, duration_ms=15935
    93bb1ed27 (current main):    Error: Another conversation's pending instruction blocks this composer's Send
    

    The scenario message is only the diagnostic. The composer is disabled because the new App chat entry sets workspaceConversations.entryGate (dashboard-page.tsx:2972, from appEntryBlocked), and composerBlocked includes it (personal-workspace-page.tsx:1380). The scenario navigates to the manager conversation after an unconfirmed Goal instruction and expects Send to be enabled; after #7320 that path goes through the steward entry gate, so the composer is disabled before any pending-state question is reached.

    So both required dashboard-browser shards fail from the #7320 App chat entry change:

    • shard 1: the entry/view switch remounts PersonalGoalHome and the overview re-creates the receipt without a start time;
    • shard 2: the entry gate disables the manager composer, and the acceptance scenario does not traverse the new entry.

    Either failure alone keeps checks (and therefore merge-gate) red. A test-only patch for shard 2 would hide the shard 1 state loss rather than repair it, and changing the entry gate or its acceptance flow is the owner's call, which is why this report does not include a code change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions