Add display control state to Display tab #1726

Merged
hongming merged 2 commits from feat/1686-display-control-ui into main 2026-05-23 09:34:20 +00:00
Owner

Implements the Canvas Display tab control-state surface for #1686, on top of backend display-control lock endpoints from #1718.

This PR intentionally does not render the live desktop stream yet. It exposes the control-lock state and takeover affordance for display-configured workspaces only.

Changes

  • Fetches /workspaces/:id/display/control only when the workspace has display configuration.
  • Shows Take control only when the backend reports controller: "none".
  • Shows active controller state with normalized actor labels and no release button until the backend exposes an explicit ownership/can-release field.
  • Fails closed when control status cannot be loaded, so unknown lock state does not render takeover actions.
  • Refetches control status after failed acquire attempts so conflict/current-holder state replaces stale local state.

Comprehensive testing performed

  • npm test -- --run src/components/tabs/__tests__/DisplayTab.test.tsx — 5 passed.
  • npm test — 221 files passed, 3383 passed, 1 skipped. jsdom emitted the existing HTMLCanvasElement.getContext warning during the full suite.
  • npm run build — Next.js production build passed.
  • git diff --check — passed.
  • Static secret scan of the diff using security-auditor patterns — no matches.

Local-postgres E2E run

Not rerun for this Canvas-only UI slice. Backend local Postgres/Redis Stage A was completed in #1718 for the endpoints this UI consumes, including acquire/release responses, DB lock state, structure_events audit rows, and migration up/down. This PR changes only mocked Canvas component behavior and has no server, migration, or binary changes.

Staging-smoke verified or pending

Pending post-merge deploy verification. This PR is a Canvas UI slice and needs the normal Actions/deploy chain to publish before tenant-visible smoke can be checked.

Root-cause not symptom

The prior Display tab stopped at display availability and returned null for display-configured workspaces, so users had no Canvas surface for control-lock state. The root fix is to consume the backend lock status contract and gate mutation affordances on known lock state instead of adding a browser-side/script-control workaround.

Five-Axis review walked

  • Correctness: covered non-display skip, acquire happy path, active-lock observe-only, acquire failure/refetch, and control-status failure/fail-closed.
  • Readability & simplicity: kept the change in DisplayTab with local typed response shapes; no new abstraction or dependency.
  • Architecture: follows existing Canvas api helper and the backend lock API from #1718; leaves live display transport for a later slice.
  • Security: no live display URL, screenshot, credential, or raw actor/error exposure added; unknown control state disables mutation UI.
  • Performance: one additional control GET only for display-configured workspaces; non-display workspaces do not call control status.

No backwards-compat shim / dead code added

No compatibility shim added. Release UI is intentionally omitted until the backend returns an explicit ownership/can-release field.

Memory/saved-feedback consulted

Followed the repo SOP in internal/runbooks/dev-sop.md, including focused tests, full suite/build verification, self-review, and non-author review path.

Implements the Canvas Display tab control-state surface for #1686, on top of backend display-control lock endpoints from #1718. This PR intentionally does not render the live desktop stream yet. It exposes the control-lock state and takeover affordance for display-configured workspaces only. ## Changes - Fetches `/workspaces/:id/display/control` only when the workspace has display configuration. - Shows `Take control` only when the backend reports `controller: "none"`. - Shows active controller state with normalized actor labels and no release button until the backend exposes an explicit ownership/can-release field. - Fails closed when control status cannot be loaded, so unknown lock state does not render takeover actions. - Refetches control status after failed acquire attempts so conflict/current-holder state replaces stale local state. ## Comprehensive testing performed - `npm test -- --run src/components/tabs/__tests__/DisplayTab.test.tsx` — 5 passed. - `npm test` — 221 files passed, 3383 passed, 1 skipped. jsdom emitted the existing `HTMLCanvasElement.getContext` warning during the full suite. - `npm run build` — Next.js production build passed. - `git diff --check` — passed. - Static secret scan of the diff using security-auditor patterns — no matches. ## Local-postgres E2E run Not rerun for this Canvas-only UI slice. Backend local Postgres/Redis Stage A was completed in #1718 for the endpoints this UI consumes, including acquire/release responses, DB lock state, structure_events audit rows, and migration up/down. This PR changes only mocked Canvas component behavior and has no server, migration, or binary changes. ## Staging-smoke verified or pending Pending post-merge deploy verification. This PR is a Canvas UI slice and needs the normal Actions/deploy chain to publish before tenant-visible smoke can be checked. ## Root-cause not symptom The prior Display tab stopped at display availability and returned `null` for display-configured workspaces, so users had no Canvas surface for control-lock state. The root fix is to consume the backend lock status contract and gate mutation affordances on known lock state instead of adding a browser-side/script-control workaround. ## Five-Axis review walked - Correctness: covered non-display skip, acquire happy path, active-lock observe-only, acquire failure/refetch, and control-status failure/fail-closed. - Readability & simplicity: kept the change in `DisplayTab` with local typed response shapes; no new abstraction or dependency. - Architecture: follows existing Canvas `api` helper and the backend lock API from #1718; leaves live display transport for a later slice. - Security: no live display URL, screenshot, credential, or raw actor/error exposure added; unknown control state disables mutation UI. - Performance: one additional control GET only for display-configured workspaces; non-display workspaces do not call control status. ## No backwards-compat shim / dead code added No compatibility shim added. Release UI is intentionally omitted until the backend returns an explicit ownership/can-release field. ## Memory/saved-feedback consulted Followed the repo SOP in `internal/runbooks/dev-sop.md`, including focused tests, full suite/build verification, self-review, and non-author review path.
claude-ceo-assistant was assigned by hongming 2026-05-23 08:41:17 +00:00
hongming added 1 commit 2026-05-23 08:41:17 +00:00
Add display control state to Display tab
Block internal-flavored paths / Block forbidden paths (pull_request) Waiting to run
CI / Detect changes (pull_request) Waiting to run
CI / Platform (Go) (pull_request) Blocked by required conditions
CI / Canvas (Next.js) (pull_request) Blocked by required conditions
CI / Shellcheck (E2E scripts) (pull_request) Blocked by required conditions
CI / Canvas Deploy Reminder (pull_request) Blocked by required conditions
CI / Python Lint & Test (pull_request) Waiting to run
CI / all-required (pull_request) Waiting to run
E2E API Smoke Test / detect-changes (pull_request) Waiting to run
E2E API Smoke Test / E2E API Smoke Test (pull_request) Blocked by required conditions
E2E Chat / detect-changes (pull_request) Waiting to run
E2E Chat / E2E Chat (pull_request) Blocked by required conditions
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Waiting to run
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Blocked by required conditions
Handlers Postgres Integration / detect-changes (pull_request) Waiting to run
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Blocked by required conditions
Harness Replays / detect-changes (pull_request) Waiting to run
Harness Replays / Harness Replays (pull_request) Blocked by required conditions
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Waiting to run
Lint no tenant GITEA or GITHUB token write / Scan for repo-host token write into tenant workspace surface (pull_request) Waiting to run
lint-required-no-paths / lint-required-no-paths (pull_request) Waiting to run
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Waiting to run
Secret scan / Scan diff for credential-shaped strings (pull_request) Waiting to run
gate-check-v3 / gate-check (pull_request) Waiting to run
qa-review / approved (pull_request) Waiting to run
security-review / approved (pull_request) Waiting to run
sop-checklist / all-items-acked (pull_request) Waiting to run
sop-checklist / review-refire (pull_request) Waiting to run
sop-tier-check / tier-check (pull_request) Waiting to run
321d051c9f
hongming added the securitytier:medium labels 2026-05-23 08:41:36 +00:00
app-fe added 1 commit 2026-05-23 08:46:49 +00:00
Harden display control tab state handling
CI / Canvas Deploy Reminder (pull_request) Blocked by required conditions
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Waiting to run
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 10s
CI / Detect changes (pull_request) Successful in 16s
CI / Python Lint & Test (pull_request) Successful in 6s
E2E API Smoke Test / detect-changes (pull_request) Successful in 11s
E2E Chat / detect-changes (pull_request) Successful in 8s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 10s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 9s
Harness Replays / detect-changes (pull_request) Successful in 9s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 4s
Lint no tenant GITEA or GITHUB token write / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 4s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 7s
qa-review / approved (pull_request) Successful in 6s
gate-check-v3 / gate-check (pull_request) Successful in 6s
security-review / approved (pull_request) Successful in 4s
sop-checklist / review-refire (pull_request) Has been skipped
sop-tier-check / tier-check (pull_request) Successful in 5s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m2s
sop-checklist / all-items-acked (pull_request) acked: 7/7
sop-checklist / na-declarations (pull_request) N/A: (none)
CI / Platform (Go) (pull_request) Successful in 4s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 2s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 4s
E2E Chat / E2E Chat (pull_request) Successful in 4s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 4s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 2s
Harness Replays / Harness Replays (pull_request) Successful in 2s
CI / Canvas (Next.js) (pull_request) Successful in 7m23s
CI / all-required (pull_request) Successful in 21m55s
audit-force-merge / audit (pull_request) Successful in 6s
af3d98e478
Author
Owner

Follow-up review fixes pushed in af3d98e4:

  • Added request-generation guard so acquire/refetch responses from a previous workspaceId cannot update the current tab.
  • Reset control busy state on workspace changes.
  • Replaced raw display-status API error rendering with fixed user-facing copy.
  • Added org-token: actor normalization to Automation.
  • Render control-status failure as unavailable instead of an animated loading row.

Updated verification:

  • npm test -- --run src/components/tabs/__tests__/DisplayTab.test.tsx — 8 passed.
  • npm test — 221 files passed, 3386 passed, 1 skipped. Existing jsdom canvas warning observed.
  • npm run build — passed.
  • git diff --check — passed.
  • Static secret scan of diff — no matches.
Follow-up review fixes pushed in `af3d98e4`: - Added request-generation guard so acquire/refetch responses from a previous `workspaceId` cannot update the current tab. - Reset control busy state on workspace changes. - Replaced raw display-status API error rendering with fixed user-facing copy. - Added `org-token:` actor normalization to `Automation`. - Render control-status failure as unavailable instead of an animated loading row. Updated verification: - `npm test -- --run src/components/tabs/__tests__/DisplayTab.test.tsx` — 8 passed. - `npm test` — 221 files passed, 3386 passed, 1 skipped. Existing jsdom canvas warning observed. - `npm run build` — passed. - `git diff --check` — passed. - Static secret scan of diff — no matches.
core-qa approved these changes 2026-05-23 08:47:50 +00:00
core-qa left a comment
Member

[core-qa-agent] APPROVED: reviewed DisplayTab tests and verification for #1686. Focused DisplayTab suite, full Canvas test suite, and production build are recorded green; active locks are observe-only and failed acquire refetches current state.

[core-qa-agent] APPROVED: reviewed DisplayTab tests and verification for #1686. Focused DisplayTab suite, full Canvas test suite, and production build are recorded green; active locks are observe-only and failed acquire refetches current state.
core-lead approved these changes 2026-05-23 08:47:50 +00:00
core-lead left a comment
Member

APPROVED: manager-tier review for tier:medium. Scope is limited to Canvas DisplayTab control-state UI on top of #1718, with review findings addressed in af3d98e4 and rollback via normal revert.

APPROVED: manager-tier review for tier:medium. Scope is limited to Canvas DisplayTab control-state UI on top of #1718, with review findings addressed in af3d98e4 and rollback via normal revert.
core-security approved these changes 2026-05-23 08:48:18 +00:00
core-security left a comment
Member

[core-security-agent] APPROVED: reviewed #1686 display control UI security behavior. Unknown control state fails closed, raw control/display errors are not rendered, active locks expose no release mutation, and actor labels are normalized.

[core-security-agent] APPROVED: reviewed #1686 display control UI security behavior. Unknown control state fails closed, raw control/display errors are not rendered, active locks expose no release mutation, and actor labels are normalized.
Member

/sop-ack comprehensive-testing Focused DisplayTab tests, full Canvas Vitest suite, production build, diff check, and secret scan are recorded.
/sop-ack local-postgres-e2e Canvas-only slice; backend local Postgres Stage A covered by #1718 and this PR has no server or migration change.
/sop-ack staging-smoke Pending post-merge deploy smoke is documented for this Canvas UI slice.
/sop-ack five-axis-review Five-axis review is documented and reviewer findings were addressed in af3d98e4.
/sop-ack memory-consulted SOP and saved feedback were consulted.

/sop-ack comprehensive-testing Focused DisplayTab tests, full Canvas Vitest suite, production build, diff check, and secret scan are recorded. /sop-ack local-postgres-e2e Canvas-only slice; backend local Postgres Stage A covered by #1718 and this PR has no server or migration change. /sop-ack staging-smoke Pending post-merge deploy smoke is documented for this Canvas UI slice. /sop-ack five-axis-review Five-axis review is documented and reviewer findings were addressed in af3d98e4. /sop-ack memory-consulted SOP and saved feedback were consulted.
Member

/sop-ack root-cause Root cause is the missing Canvas consumer of the display-control lock contract, not a browser automation workaround.
/sop-ack no-backwards-compat No shim/dead code added; release is intentionally omitted until backend exposes ownership/can-release.

/sop-ack root-cause Root cause is the missing Canvas consumer of the display-control lock contract, not a browser automation workaround. /sop-ack no-backwards-compat No shim/dead code added; release is intentionally omitted until backend exposes ownership/can-release.
hongming merged commit 3161d43cec into main 2026-05-23 09:34:20 +00:00
Sign in to join this conversation.
5 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#1726