Skip to content

feat(harness): complete Agent Map planning workspace - #772

Merged
ynadge merged 17 commits into
mainfrom
yashnadge/sap-3058-agent-studio-complete-the-first-open-planning-slice
Sep 2, 2026
Merged

feat(harness): complete Agent Map planning workspace#772
ynadge merged 17 commits into
mainfrom
yashnadge/sap-3058-agent-studio-complete-the-first-open-planning-slice

Conversation

@ynadge

@ynadge ynadge commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Completes the first visible Agent Map planning slice for stable Studio projects. Opening an Agent Map now starts or resumes the trusted project planner in a dedicated conversation beside the durable map, without exposing a normal terminal.

Map and planner loading remain independent, planner transcripts refresh from the existing durable SessionRecord API, and desktop/mobile layouts retain the established Studio shell behavior.

Changes

  • open Agent Maps into a planner-first workspace with resume-or-create and fresh-session flows
  • render planner-only transcripts, queued-message state, greeting retry, and appropriate rename/end session controls
  • publish content-free SessionRecord invalidations after durable prompt and turn writes, with coalesced trailing reads
  • isolate map and planner failures while reserving whole-workspace replacement for authorization/not-found responses
  • keep the conversation primary on mobile and expose Agent Map through the labeled, focus-restoring sheet
  • add content-free entry/load telemetry, mock coverage, and a harness changeset

Key refinements from code review

  • preserve an explicitly selected planner tab over project-level resume ordering
  • make failed preference restoration a one-shot fallback so later session frames cannot reset selection or close the mobile sheet
  • let dialogs and popovers own Escape before the mobile Agent Map sheet handles it
  • distinguish map and planner load failures in telemetry, prune ended-session transcript revisions, and document additive public event-union variants

Testing

  • pnpm --filter @sapiom/harness typecheck
  • pnpm --filter @sapiom/harness lint
  • focused Vitest suite: 81 tests passed
  • Agent Map Playwright suite: 10 tests passed across desktop and mobile
  • command-palette Escape regression: 1 test passed
  • pnpm --filter '@sapiom/harness...' build
  • pnpm terminology:check
  • pnpm provider-copy:check

Screenshots / demos

The Playwright coverage captures both layouts during the first-open flow:

  • web/e2e/screenshots/agent-map-planning.png
  • web/e2e/screenshots/agent-map-planning-mobile.png

Rollout

No feature flag. The experience applies only to projects with the durable Studio identity introduced by the prerequisite Agent Map work; legacy projects retain System Graph behavior.

Related

Closes: SAP-3058

Checklist

  • Diff reviewed against merged main
  • No prompt, assistant content, local paths, or provider errors added to telemetry
  • Existing design-system primitives and tokens reused
  • No hardcoded secrets

Serialize catalog writers across Studio hosts, exclusively commit lazy workspace initialization, and expose allow-listed project/root association through the authenticated server boundary.

Refs: SAP-3056
Keep browser bootstrap authority out of coding-agent PTYs, serialize dead-owner lock recovery, and leave ambiguous legacy workspace aliases unassigned.

Closes: SAP-3056
…e/sap-3057-ui-open-projects-in-a-pinned-agent-map-workspace
Retain both the scoped planner routes and durable Agent Map workspace-selection routes on the combined mainline tree. Keep preference authorization tied to the live trusted principal, and align the terminology and legacy browser fixtures with the plan-first rail.

Refs: SAP-3057
Keep the on-disk preference parser aligned with the strict HTTP contract by accepting only server-issued UUIDv4 agent identifiers. Add a persisted-state regression for malformed opaque IDs.

Refs: SAP-3057
…cts-in-a-pinned-agent-map-workspace' into yashnadge/sap-3058-agent-studio-complete-the-first-open-planning-slice
…ent-studio-complete-the-first-open-planning-slice

# Conflicts:
#	packages/harness/web/src/App.tsx
#	packages/harness/web/src/lib/api.ts
#	scripts/agent-studio-terminology-allowlist.json
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Review — PR #772 (round 1)

No confidentiality issues: the changeset, mock fixtures, comments and copy are all
generic — no customer/partner names, no internal hosts, no business arrangements.

1. Re-entering an Agent Map overrides the planner tab the user just clicked

App.tsx:2121handleSelectSession sets the clicked planner as active and flips
studioSelection to {kind:"agent-map"}. That flips plannerProjectId from null to
P, so useAgentMapEntry's effect (use-agent-map-entry.ts:236) fires
loadPlanner(P, "resume-or-create"), and onPlannerReady unconditionally calls
setActiveSessionId(response.session.id) (App.tsx:299).

resume-or-create resolves by candidateOrder — live first, then queued, then
lastActiveAt desc (core/planning-session.ts:209) — it has no idea which tab was
clicked. Failure: project P has live planner tabs A (older) and B (newer); the user is on
a legacy project, opens the session list and clicks A. The centre pane flashes
"Opening planning conversation…", then lands on B. Same root cause makes the second
half of the round trip in the E2E test ("return resumes the same planner") pass only
because the fixture has exactly one planner.

Suggested shape: skip the auto resume-or-create when plannerProjectId becomes non-null
because an explicit planner session was already selected (pass a "already have a session"
hint into the hook), or have onPlannerReady no-op when an active planner for that project
already exists.

2. A failing preference read repeatedly stomps selection and closes the mobile sheet

App.tsx:377-386 — the new .catch() now mutates UI state (setStudioSelection,
setFocusedAgentPath, if (isMobile) setRightCollapsed(true)) while still doing the
pre-existing restoredStudioProjectsRef.current.delete(project.projectId). Deleting the
guard means the effect re-runs on the next harness.state change — and harness.state
is replaced on every session.status bus frame.

Failure: getStudioCurrentWorkspace is returning 500 (server restart, offline). On mobile
the user opens the Agent Map sheet; the next session-status event re-fires the effect, the
GET fails again, and setRightCollapsed(true) slams the sheet shut — repeatedly, once per
status event, along with resetting focusedAgentPath. Previously the catch was inert.
Gate the fallback on the first failure per project (keep the id in the ref, or a separate
failedRestore set) so it lands once.

3. Mobile Escape handler steals Escape from every open overlay

App.tsx:751-758 — a window keydown listener that, on mobile with the right pane open,
collapses the pane on any Escape, with no check for an open dialog/popover/palette and
no event.target guard. useDismissable listens on document
(lib/use-dismissable.ts:44), so both handlers run for the same keypress.

Failure: mobile, canvas panel expanded on an ordinary project, user opens the command
palette (or the session menu, or EndSessionConfirm) and presses Escape — the overlay
closes and the panel collapses, and the requestAnimationFrame focus call then yanks
focus to the right-pane trigger, overriding the focus restore the dismissable just did.
Bail out when any overlay is open (paletteOpen, confirming*, templatesOpen,
overviewOpen) or when the event was already defaulted.

4. BusMessage is a public type; the new variant can break a pinned consumer at minor

shared/types.ts:525 adds { type: "session.record.changed" } to BusMessage, and
src/index.ts:6 does export * from "./shared/types.js". A consumer with an exhaustive
switch plus a never default over BusMessage stops compiling on 0.12 → 0.13. Same
class for the UiEventName / AnalyticsEventType additions. Either note it in the
changeset body as a type-level breaking change with the one-line migration, or document
that these unions are open and consumers must keep a default arm.

Nits

  • openFreshPlanner (use-agent-map-entry.ts:290) is the only retry verb that does not
    clear state.unavailable, so a fresh launch from the "Agent Map unavailable" screen
    leaves the blocking pane up even after the launch succeeds.
  • Planner launch failures are not tracked, only workspace reads
    (use-agent-map-entry.ts:172 has no track(...) in its rejection path), so the PR's
    "entry/load telemetry" only covers half the workspace.
  • sessionRecordRevisions (use-harness-state.ts:519) is never pruned and clones the
    whole Map on every prompt/turn of every session, re-rendering App for sessions no
    transcript is watching.

Verdict: request changes — findings 1 and 2 are user-visible correctness regressions
in the primary flow this PR ships.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Review — PR #772 (round 2, follow-up)

Delta reviewed: 9b29731d..d1454cd2 (one commit, fix(harness): preserve Agent Map interaction intent). No confidentiality issues in the delta — the changeset addition,
comments and mock copy are generic.

Earlier findings — all four addressed

  1. Tab override — fixed. handlePlannerReady (App.tsx:301) no-ops when the active
    session is already a live map-planner for the same project and the mode is
    resume-or-create. onPlannerReady is held in a ref (use-agent-map-entry.ts:123), so
    the new harness.state?.sessions dep does not re-trigger the launch effect. E2E covers it.
  2. Preference-failure stomp — fixed. The .catch() no longer deletes the
    restoredStudioProjectsRef guard (App.tsx:402), and neither does the project-open
    fallback (App.tsx:2752), so the fallback lands once per project.
  3. Escape stealing — fixed. The window handler bails on defaultPrevented, on the four
    App-owned overlay flags, and on any live [role=dialog|alertdialog|menu]/aria-modal
    node (App.tsx:777); useDismissable, CommandPalette and OverviewModal now claim the
    key. Checked that every role="menu"/role="dialog" site in web/src/components is
    either behind AnchoredPopover (returns null when closed) or conditionally mounted, so
    the querySelector guard cannot permanently disable the sheet.
  4. Public union additions — documented in the changeset body with the default-arm note.

Nits 1 (fresh launch clearing unavailable) and 2 (planner-pane failure telemetry) are fixed.

Remaining

  • Nit 3 only half fixed: sessionRecordRevisions is now pruned on exit
    (use-harness-state.ts:1293) and on close (:1752), but the Map is still cloned on every
    prompt/turn of every live session, re-rendering App for sessions no transcript watches.

Nit

  • Re-entering an Agent Map still issues a redundant resume-or-create POST and flashes
    "Opening planning conversation…" before landing back on the tab the user picked — the new
    E2E test absorbs it with waitForTimeout(500). Cosmetic now that selection is correct.

Verdict: approve — the round-1 correctness regressions are resolved.

@ynadge
ynadge merged commit fb2486d into main Sep 2, 2026
10 checks passed
@ynadge
ynadge deleted the yashnadge/sap-3058-agent-studio-complete-the-first-open-planning-slice branch September 2, 2026 04:29
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