Skip to content

test(desktop): enforce entry runtime boundary#408

Merged
DeliciousBuding merged 2 commits into
dev/delicious233from
task/387-desktop-entry-runtime-split
Jun 28, 2026
Merged

test(desktop): enforce entry runtime boundary#408
DeliciousBuding merged 2 commits into
dev/delicious233from
task/387-desktop-entry-runtime-split

Conversation

@DeliciousBuding

@DeliciousBuding DeliciousBuding commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add Desktop data-boundary Playwright coverage for entry-preflight Local Edge health vs Demo workbench runtime requests
  • switch chat-flow request phase to workbench-runtime before the Demo transition starts
  • harden useHealth so in-flight entry health responses do not update state after polling is disabled
  • sync roadmap current SPEC status without adding duplicate rules

Closes #387

Verification

  • corepack.cmd pnpm --dir app/desktop exec vitest run src/__tests__/useHealth.test.ts
  • corepack.cmd pnpm --dir app/desktop test:e2e:data-boundary
  • corepack.cmd pnpm --dir app/desktop test:e2e:chat-flow
  • corepack.cmd pnpm --dir app/desktop typecheck
  • corepack.cmd pnpm --dir app/desktop build
  • pwsh ./scripts/verify/verify-doc-ssot.ps1
  • pwsh ./scripts/verify/verify-project-skills.ps1
  • pwsh ./scripts/verify/verify-real-e2e-contract.ps1
  • git diff --check

Evidence boundary: Desktop Vite Playwright + fixture/unit + build. real_tested=false; no packaged Desktop, sidecar, installer/signing, real login, real CLI/model/API, or production path tested.

Summary by CodeRabbit

  • New Features

    • Added a new desktop end-to-end test command for validating data-boundary behavior.
  • Tests

    • Introduced a “Desktop data boundary” Playwright spec and added a useHealth test covering behavior after disabling polling.
  • Bug Fixes

    • Prevent health status updates from stale in-flight responses after polling is turned off.
    • Refined the desktop demo flow so the runtime-handling behavior only triggers when the demo continue button is shown.
  • Documentation

    • Updated roadmap and progress tracking for the latest phase status.

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a1dffbbc-0f3c-490d-a7cf-582b2cb0314a

📥 Commits

Reviewing files that changed from the base of the PR and between cbf2981 and 759d2da.

📒 Files selected for processing (1)
  • docs/progress/MASTER.md
✅ Files skipped from review due to trivial changes (1)
  • docs/progress/MASTER.md

📝 Walkthrough

Walkthrough

Adds enabledRef gating to useHealth so in-flight fetch results are ignored after polling is disabled. Adds a Desktop Playwright spec for entry-preflight versus workbench-runtime request boundaries, fixes demo callback timing, and updates the matching npm script and progress docs.

Changes

Desktop boundary checks and phase tracking

Layer / File(s) Summary
useHealth enabledRef gating
app/desktop/src/hooks/useHealth.ts, app/desktop/src/__tests__/useHealth.test.ts
Introduces enabledRef synced on render and in useEffect, and gates state updates after fetchHealth() resolves or rejects; the new test covers disabling polling while a request is pending.
Desktop data boundary E2E
app/desktop/src/__e2e__/desktop-data-boundary.spec.ts, app/desktop/src/__e2e__/chat-flow-ui.spec.ts, app/desktop/package.json
Adds request interception and phase tracking for Desktop data-boundary assertions, including boundary classification and request formatting helpers; moves onWorkbenchRuntime?.() into the Demo-button branch and adds the new Chromium test script.
Phase tracking updates
docs/roadmap.md, docs/progress/MASTER.md
Updates the Phase 3 roadmap/progress tables, marks T3.2 ready via #408, revises next steps, and adds a new session log entry.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐇 Hop hop, the boundary line is bright,
Preflight hums, then pauses just right.
Demo doors open, but runtime stays low,
My whiskers approve this phase-aware flow.
Tiny tests dance; the roadmap sings—
A neat little patch with orderly springs.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the Desktop entry-runtime boundary enforcement work.
Linked Issues check ✅ Passed The PR adds E2E boundary assertions and hardens useHealth so entry-preflight probes and workbench-runtime blocking behave as required by #387.
Out of Scope Changes check ✅ Passed The roadmap and progress updates support the same boundary-split effort and do not introduce unrelated code changes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task/387-desktop-entry-runtime-split

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/desktop/src/__e2e__/desktop-data-boundary.spec.ts`:
- Around line 51-58: The boundary check in the desktop E2E spec is using a fixed
1s timeout, which can be too short to cover the next health poll. Update the
wait in desktop-data-boundary.spec around the runtime backends assertion to use
the full HEALTH_POLL_MS delay before checking backendRequests.requests, so the
no-runtime-backend assertion and assertE2EDataModeScenario run after the
expected poll window.

In `@app/desktop/src/hooks/useHealth.ts`:
- Around line 25-37: The `useHealth` poll callback only guards updates with
`mountedRef` and `enabledRef`, so an older `fetchHealth()` response can still
overwrite newer state after polling is toggled off and back on. In `useHealth`,
add a poll-generation or request token inside `poll` before awaiting
`fetchHealth()`, then re-check that token before every `setHealth`, `setOnline`,
and `setLastError` update so only the latest session can commit state.

In `@docs/roadmap.md`:
- Line 3: The roadmap’s last-updated metadata is future-dated and needs to be
corrected. Update the “最后更新” value in the roadmap metadata to the actual edit
date, keeping the change limited to the existing last-updated entry so the
document remains accurate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5da5b8db-a4fd-4624-9892-98da2dcb01f6

📥 Commits

Reviewing files that changed from the base of the PR and between 8b057f1 and cbf2981.

📒 Files selected for processing (6)
  • app/desktop/package.json
  • app/desktop/src/__e2e__/chat-flow-ui.spec.ts
  • app/desktop/src/__e2e__/desktop-data-boundary.spec.ts
  • app/desktop/src/__tests__/useHealth.test.ts
  • app/desktop/src/hooks/useHealth.ts
  • docs/roadmap.md

Comment on lines +51 to +58
await page.waitForTimeout(1_000);

const runtimeBackends = backendRequests.requests.filter((request) =>
request.phase === 'workbench-runtime' &&
isBackendBoundary(classifyE2ERequest(request.url, DESKTOP_DEMO_BOUNDARY_SCENARIO))
);
expect(runtimeBackends, formatRequests(runtimeBackends)).toEqual([]);
assertE2EDataModeScenario(DESKTOP_DEMO_BOUNDARY_SCENARIO, backendRequests.requests);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

USE_HEALTH_FILE="$(fd 'useHealth.ts' app/desktop/src | head -n1)"
USE_HEALTH_TEST_FILE="$(fd 'useHealth.test.ts' app/desktop/src | head -n1)"

echo "useHealth file: ${USE_HEALTH_FILE}"
ast-grep outline "${USE_HEALTH_FILE}" --view expanded
rg -n -C3 'setInterval|setTimeout|poll|interval|health' "${USE_HEALTH_FILE}"

if [[ -n "${USE_HEALTH_TEST_FILE}" ]]; then
  echo
  echo "useHealth test file: ${USE_HEALTH_TEST_FILE}"
  rg -n -C3 'advanceTimersByTime|runOnlyPendingTimers|poll|interval|health' "${USE_HEALTH_TEST_FILE}"
fi

Repository: TokenDanceLab/AgentHub

Length of output: 6318


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "useHealth.ts"
cat -n app/desktop/src/hooks/useHealth.ts | sed -n '1,220p'

echo
echo "desktop-data-boundary.spec.ts"
cat -n app/desktop/src/__e2e__/desktop-data-boundary.spec.ts | sed -n '1,220p'

echo
echo "Search HEALTH_POLL_MS"
rg -n 'HEALTH_POLL_MS|useHealth\(' app/desktop/src

Repository: TokenDanceLab/AgentHub

Length of output: 11683


Wait a full HEALTH_POLL_MS before asserting no runtime backend requests. The current 1s sleep can miss a later /v1/health poll and let a boundary regression slip through. app/desktop/src/__e2e__/desktop-data-boundary.spec.ts:51-58

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/desktop/src/__e2e__/desktop-data-boundary.spec.ts` around lines 51 - 58,
The boundary check in the desktop E2E spec is using a fixed 1s timeout, which
can be too short to cover the next health poll. Update the wait in
desktop-data-boundary.spec around the runtime backends assertion to use the full
HEALTH_POLL_MS delay before checking backendRequests.requests, so the
no-runtime-backend assertion and assertE2EDataModeScenario run after the
expected poll window.

Comment on lines +25 to +37
const enabledRef = useRef(enabled);
enabledRef.current = enabled;

const poll = useCallback(async () => {
if (!enabled) return;
try {
const h = await fetchHealth();
if (!mountedRef.current) return;
if (!mountedRef.current || !enabledRef.current) return;
setHealth(h);
setOnline(true);
setLastError(null);
} catch (error) {
if (!mountedRef.current) return;
if (!mountedRef.current || !enabledRef.current) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Guard stale responses by poll generation, not only enabled.

If polling is disabled and then re-enabled before an earlier fetchHealth() resolves, that older response will still pass the current mountedRef.current && enabledRef.current check and overwrite the new session’s state. Capture a request/session token before await fetchHealth() and compare it again before each state update.

Possible fix
   const mountedRef = useRef(true);
   const enabledRef = useRef(enabled);
+  const pollEpochRef = useRef(0);
   enabledRef.current = enabled;

   const poll = useCallback(async () => {
     if (!enabled) return;
+    const pollEpoch = pollEpochRef.current;
     try {
       const h = await fetchHealth();
-      if (!mountedRef.current || !enabledRef.current) return;
+      if (!mountedRef.current || !enabledRef.current || pollEpoch !== pollEpochRef.current) return;
       setHealth(h);
       setOnline(true);
       setLastError(null);
     } catch (error) {
-      if (!mountedRef.current || !enabledRef.current) return;
+      if (!mountedRef.current || !enabledRef.current || pollEpoch !== pollEpochRef.current) return;
       setOnline(false);
       setHealth(null);
       setLastError(error instanceof Error ? error.message : 'Local Edge health check failed');
     }
   }, [enabled]);

   useEffect(() => {
     mountedRef.current = true;
     enabledRef.current = enabled;
+    pollEpochRef.current += 1;
     if (!enabled) {
       setOnline(false);
       setHealth(null);
       setLastError(null);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const enabledRef = useRef(enabled);
enabledRef.current = enabled;
const poll = useCallback(async () => {
if (!enabled) return;
try {
const h = await fetchHealth();
if (!mountedRef.current) return;
if (!mountedRef.current || !enabledRef.current) return;
setHealth(h);
setOnline(true);
setLastError(null);
} catch (error) {
if (!mountedRef.current) return;
if (!mountedRef.current || !enabledRef.current) return;
const mountedRef = useRef(true);
const enabledRef = useRef(enabled);
const pollEpochRef = useRef(0);
enabledRef.current = enabled;
const poll = useCallback(async () => {
if (!enabled) return;
const pollEpoch = pollEpochRef.current;
try {
const h = await fetchHealth();
if (!mountedRef.current || !enabledRef.current || pollEpoch !== pollEpochRef.current) return;
setHealth(h);
setOnline(true);
setLastError(null);
} catch (error) {
if (!mountedRef.current || !enabledRef.current || pollEpoch !== pollEpochRef.current) return;
setOnline(false);
setHealth(null);
setLastError(error instanceof Error ? error.message : 'Local Edge health check failed');
}
}, [enabled]);
useEffect(() => {
mountedRef.current = true;
enabledRef.current = enabled;
pollEpochRef.current += 1;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/desktop/src/hooks/useHealth.ts` around lines 25 - 37, The `useHealth`
poll callback only guards updates with `mountedRef` and `enabledRef`, so an
older `fetchHealth()` response can still overwrite newer state after polling is
toggled off and back on. In `useHealth`, add a poll-generation or request token
inside `poll` before awaiting `fetchHealth()`, then re-check that token before
every `setHealth`, `setOnline`, and `setLastError` update so only the latest
session can commit state.

Comment thread docs/roadmap.md
# AgentHub Roadmap

最后更新:2026-06-28
最后更新:2026-06-29

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the last-updated date.

2026-06-29 is future-dated here, which makes the roadmap metadata inaccurate. Please set it to the actual edit date.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/roadmap.md` at line 3, The roadmap’s last-updated metadata is
future-dated and needs to be corrected. Update the “最后更新” value in the roadmap
metadata to the actual edit date, keeping the change limited to the existing
last-updated entry so the document remains accurate.

@DeliciousBuding DeliciousBuding merged commit 899cea0 into dev/delicious233 Jun 28, 2026
21 checks passed
@DeliciousBuding DeliciousBuding deleted the task/387-desktop-entry-runtime-split branch June 28, 2026 18:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant