Skip to Content

← All archived runs

Run: agent-shell-layout-polish

run.md

Run: agent-shell-layout-polish

  • branch: claude/awesome-bell-lwwiqy
  • pr: #905

02_define/output/spec.md

Spec: Agent app shell and layout polish

  • slug: agent-shell-layout-polish
  • personas: Admin, CSM, SDM, Expert, Vendor, Customer
  • touches: apps/agent, turbo.json
  • complexity: standard

Problem

The agent app's frame is still the scaffold cut: a header showing a plain name and a Sign out button where every other Sustentus app shows the Clerk UserButton, no way back to the platform, no surfaced theme toggle although next-themes is fully wired, an h-screen layout that fights mobile browser chrome and the on-screen keyboard, and a max-w-3xl column floating unframed on large screens. It reads generic rather than Sustentus, and stub 2's conversation polish would be styling messages inside a frame that undermines them. Jamie confirmed the irritations and ruled the pass visual/UX only (2026-08-27). Initiative: Scale the bridge / Q2-2026 Objective 3 — Validate Technical Infrastructure & Payout Flow, inherited from agentic-app-foundation, the batch that built this app — the tie is as indirect for polish as it was for the scaffold (flagged, not dressed up).

Proposed change

A visual/UX pass over the agent app's shell, per the brand-guidelines skill, changing no behaviour, no routes, and no data access. agentic-tenant-context has shipped and landed chrome in this header (the tenant name and the console caller's "Switch tenant" affordance) — the pass polishes that chrome in place; it does not revert it.

  • Header. Replace the name + Sign out pair with the standard Clerk UserButton (sign-out moves into its menu). Keep the tenant name and "Switch tenant" affordance, aligned into the polished header. Add a visible, labelled link back to the platform app and a compact theme toggle surfacing the existing next-themes wiring (light / dark / system — Clerk components already follow the resolved theme via ThemeAwareClerkProvider).
  • Platform link source. The link's target is the platform's origin from configuration — a new PLATFORM_APP_URL variable mirroring CONSOLE_APP_URL's semantics (a bare host is accepted and completed to https, http for localhost), resolved by a small helper local to apps/agent (lib/). Unset, it fails safe: the link simply doesn't render and nothing else changes. The variable joins turbo.jsonglobalEnv in this same PR (strict env mode; turbo/no-undeclared-env-vars enforces it).
  • Mobile viewport. Replace the h-screen shell with dynamic-viewport sizing so the layout tracks mobile browser chrome and the on-screen keyboard: the input stays visible and usable with the keyboard open, sending never jumps the scroll position, and the page itself never scrolls — only the conversation column does. This may adjust container and scroll classes in components/assistant-chat.tsx — containers only; the messages, banners and empty state inside them are stub 2's.
  • Large-screen framing. Frame the conversation column deliberately on large screens — width, background and header alignment chosen in the brand pass, so the column reads as designed rather than floating.
  • Brand foundation. Typography and colour per brand-guidelines become the baseline stub 2 builds on, applied through the existing @sustentus/ui tokens already imported by app/globals.css — no new palette definitions.

Files: apps/agent/app/page.tsx, apps/agent/app/layout.tsx, apps/agent/app/globals.css, apps/agent/components/providers.tsx, apps/agent/components/assistant-chat.tsx (containers only), new small components under apps/agent/components/ and a helper under apps/agent/lib/, turbo.json. The auth gate stays "authenticated user and nothing finer"; /sign-in remains the only public route; proxy.ts is untouched.

Acceptance criteria

  • The header shows the standard Clerk UserButton (sign-out lives in its menu; the old name + Sign out pair is gone), a labelled platform link, and a theme toggle — and the tenant name and console caller's "Switch tenant" affordance survive the polish.
  • Light and dark themes both render correctly across every shell state — header, conversation, tenant picker, no-workspace — with the toggle's choice (light / dark / system) handled by the existing next-themes wiring.
  • On a phone, the input is visible and usable with the on-screen keyboard open, sending does not jump the scroll position, and only the conversation column scrolls — never the page.
  • On a large screen the conversation column reads as deliberately framed — width, background and header alignment per the brand pass.
  • The platform link's target comes from PLATFORM_APP_URL, catalogued in turbo.jsonglobalEnv in this PR; with the variable unset the link is absent and everything else behaves unchanged.
  • proxy.ts and the route surface are untouched; no new route is public; no new data access is added.

Out of scope

  • Everything inside the conversation itself — welcome copy, starter-prompt chips, message styling, the "Thinking…" shimmer, the error and degraded banners. All stub 2 (agent-chat-experience-polish).
  • Conversation persistence, history, navigation, or any second page.
  • The reverse direction — a platform → assistant entry point in apps/web — which belongs to the later redirection batch.
  • Any change to /api/chat or /api/ai/availability behaviour, the availability probe's semantics, or the error-sanitisation policy (raw provider errors stay out of the DOM).
  • Promoting a shared ThemeToggle to packages/uiapps/web and apps/marketing keep their own theme controls; the agent toggle stays local until a third consumer earns the extraction.
  • proxy.ts, the "authenticated user" gate, and Clerk configuration.

Open questions

  • none — Jamie's rulings (2026-08-27, quoted in the stub) settled the scope; the platform-link source and the interplay with agentic-tenant-context's header chrome are resolved above.

03_build/output/notes.md

Build notes: agent-shell-layout-polish

  • commits: feat config+helper · feat shell pass · docs build notes · style prettier fix
  • ci: GREEN on 09e5eff (first run RED — prettier class order in page.tsx; Husky had not run in this container, fixed by installing deps and running the repo formatter on touched files)

What changed

  • turbo.json: PLATFORM_APP_URL joins globalEnv (strict env mode), documented beside CONSOLE_APP_URL whose parsing rules it shares.
  • apps/agent/lib/platform-origin.ts (new): platformOrigin() — delegates to the shared origin parser (resolveConsoleOrigin, @sustentus/services/shared) rather than duplicating it, per the "grep before writing a helper" rule; unset/unusable input → null → the header omits the link. No new test file: the parsing logic already lives (and is exercised) in packages/services; the local helper is a one-line delegation.
  • apps/agent/components/theme-toggle.tsx (new): compact dropdown (light / dark / system) over the existing next-themes wiring. Trigger icon swaps via CSS dark: classes (the shared theme's class-strategy @custom-variant dark), so hydration needs no theme knowledge.
  • apps/agent/app/page.tsx: header now shows the standard Clerk UserButton (name + Sign out pair removed — also drops the currentUser() call), the platform link (rendered only when PLATFORM_APP_URL resolves; text hidden below sm, aria-label carries the name), the theme toggle, and keeps agentic-tenant-context's chrome (tenant name, "Switch tenant"). Shell is now h-dvh + overflow-hidden (page never scrolls); the conversation column is a bg-background card — full-bleed on mobile, rounded-xl border shadow-sm on sm+ floating on the bg-muted/30 canvas, header content aligned to the same max-w-3xl column. Picker and no-workspace states sit in their own internal scroll wrappers.
  • apps/agent/app/layout.tsx: viewport.interactiveWidget = "resizes-content" — the on-screen keyboard resizes the layout so the h-dvh shell shrinks and the input stays visible while typing.
  • apps/agent/app/globals.css: overscroll-behavior-y: none on html/body — no pull-to-refresh wiping the ephemeral conversation, no page-level bounce.
  • apps/agent/components/assistant-chat.tsx: containers only — scroll container gains overscroll-contain + its own horizontal padding (moved off main), input strip loses its now-redundant bg-background and gains matching padding. Messages, banners, empty state untouched (stub 2).

Acceptance criteria status

  • Header: UserButton (sign-out in its menu), labelled platform link, theme toggle; tenant name + "Switch tenant" survive — all in page.tsx.
  • Both themes render from semantic tokens only (bg-background, bg-muted/30, borders); Clerk components follow via the existing ThemeAwareClerkProvider; toggle drives light / dark / system through next-themes.
  • Phone: implemented (h-dvh, interactiveWidget: resizes-content, overscroll-contain, fixed shell — only the column scrolls) but the assertion is device-empirical; left for the Verify smoke on the preview rather than ticked from code reading.
  • Large screens: conversation column framed as a rounded, bordered card on the muted canvas; header content aligned to the same max-w-3xl column.
  • PLATFORM_APP_URL in globalEnv this PR; unset → link absent, nothing else changes.
  • proxy.ts untouched, no route added or made public, no new data access (the one new services import is the pure /shared origin parser).

Notes for Verify

  • Smoke the phone criterion first on the preview: keyboard open → input visible; send → no scroll jump; page itself never scrolls (all three states: chat, picker, no-workspace).
  • Check both themes across the four shell states, including the Clerk UserButton popover and the theme dropdown itself.
  • PLATFORM_APP_URL is a new Vercel env var for the agentic-interface project — the preview will render no platform link until it is set there. Unset is a spec-conformant state (link absent), so the link's presence can only be smoked once the variable is configured; everything else is verifiable without it.

04_verify/output/verify.md

Verify: agent-shell-layout-polish

  • ci: GREEN on 80b5f60 (last code head; settled via ci-status.sh after the review-fix push). The verify.md commit itself is docs-only; ci-status.sh is re-run after its push and the final verdict reported at the gate.
  • previews smoked: the whole agentic-interface preview sits behind Vercel deployment protection (SSO) — every route, /sign-in included, 302s to vercel.com before the app is reached — so no preview surface is agent-reachable; deploy status verified instead (branch preview READY for the code head), all visual smoke lines are the operator's. Earlier .icm-only commits correctly skipped the app build (turbo-ignore); the final code head rebuilt it.
  • production-readiness: run — no blockers in the diff. Env lockstep verified (PLATFORM_APP_URL read + globalEnv entry land in the same PR, same name; fail-safe parser is protocol-allowlisted). Release-process items: the variable is not yet set on the agentic-interface Vercel project (owner action before announcing — unset is spec-conformant, the link is simply absent); docs-sync (deployment env catalogue) and changelog belong to Ship; rollback is a plain squash-revert (no migration).
  • code-review: medium (spec: standard); CI Claude review disabled (workflow runs all skipped), so the /code-review skill ran — single-pass inline, no subagent verify fan-out (Agent tool unavailable in the fork). Correctness clean; 2 cleanup findings, both fixed on branch in 80b5f60: theme menu now uses DropdownMenuRadioGroup/RadioItem (real aria-checked semantics, manual check-icon logic deleted); header's dead bg-background/80 backdrop-blur-sm reduced to bg-background (nothing can pass under a non-overlay header in an overflow-hidden shell).
  • security-review: not required — no auth/payments/PII/route-policy logic in the diff: proxy.ts untouched, no route added or made public, SignOutButtonUserButton is a like-for-like Clerk surface swap, and the env var feeds a server-side origin parser that only ever emits http(s) origins or null.
  • playwright: TODO — manual DoD smoke performed instead.

DoD smoke (on the preview — each line says who verified it)

  • Header shows UserButton + labelled platform link + theme toggle; tenant name and "Switch tenant" survive — code paths traced in the diff: all six elements render from apps/agent/app/page.tsx's header block, tenant chrome preserved conditionally as before (agent). Visual pass on the preview: operator.
  • Both themes render across header / conversation / picker / no-workspace — every new colour is a semantic token, the dark: icon swap rides the shared class-strategy variant, Clerk follows via the existing ThemeAwareClerkProvider (agent, from code + theme config). Visual pass in both themes incl. the UserButton popover: operator.
  • Phone: input visible with keyboard open, send does not jump scroll, only the column scrolls — implemented (h-dvh + interactiveWidget: resizes-content + overscroll-contain, page overflow-hidden) but device-empirical: operator, on iOS Safari first (it ignores interactive-widget; Android proves little for it).
  • Large screens: column reads deliberately framed (rounded bordered card on the muted canvas, header aligned to the same max-w-3xl column) — operator visual pass.
  • PLATFORM_APP_URL catalogued in globalEnv this PR; unset → link absent, nothing else changes — lockstep and fail-safe verified in code (agent). With the var unset in Vercel today, "link absent" is the expected preview state; the rendered-link half is demonstrable only after the owner sets the variable: operator, post-config.
  • proxy.ts and the route surface untouched; no new public route; no new data access — diff-verified: no proxy/route/API changes, the one new services import is the pure /shared origin parser (agent).
  • auth: personas still sign in and reach the assistant; sign-out works from the UserButton menu (operator — the preview's Vercel SSO wall makes every signed-in check theirs).
  • payments: not touched.
  • notifications: none expected from this diff.

Findings & cleanup

  • Theme menu missing aria-checked semantics — fixed on branch (80b5f60).
  • Dead header translucency/backdrop-blur — fixed on branch (80b5f60).
  • PLATFORM_APP_URL absent from the agentic-interface Vercel project — needs owner action before the platform link exists anywhere deployed (set for Production + Preview; runtime read, so globalEnv alone does not deliver it).
  • Walkthrough Slack clip to the feature thread in #buildoperator records it during the preview pass (visibility, not a gate).
  • Non-blocking, for intake: CLAUDE.md's monorepo map omits apps/agent and apps/console (pre-existing) — a /pipeline tweak candidate so orientation stops lagging two apps.

05_ship/output/changelog.md

Changelog entry (run copy)

Live page: apps/help/app/changelog/2026-08-28-agent-shell-layout-polish/page.mdx (https://help.sustentus.com/changelog/2026-08-28-agent-shell-layout-polish)


title: The assistant now fits your phone and follows your theme date: 2026-08-28T09:00:00Z personas: [customer, expert, csm, sdm, admin, vendor] slug: agent-shell-layout-polish pr: https://github.com/sustentus/sustentus/pull/905


The assistant now fits your phone and follows your theme

The first two releases were about what the assistant can do. This one is about the room it does it in — the frame around the conversation now behaves like the rest of Sustentus.

On a phone, the conversation cooperates. The message box stays visible while you type instead of hiding behind the keyboard, sending a message no longer jumps the page around, and only the conversation scrolls — the page itself stays put. A stray downward swipe no longer reloads the page either, which matters here: conversations are not saved, so an accidental refresh used to throw yours away.

Light, dark, or follow your device. A new control in the header sets the theme, and everything follows it — the conversation, the account menu, all of it.

The header now works like every other Sustentus app. Your account lives behind your picture at the top right, with sign out in its menu, and a link takes you back to the main Sustentus platform when you are done here. For staff and partners, the workspace name and the way you switch between workspaces stay where they were.

On a big screen the conversation holds its shape — a framed column rather than text floating in space.

05_ship/output/investor-update.md

The assistant now looks and works like Sustentus

Who it's for: Everyone — all six personas What shipped: The assistant's frame caught up: the standard account menu, a link back to the platform, light/dark/system theme, and a layout that works properly on phones. Why it matters: A credible surface for the assistant as it grows — Scale the Bridge, supporting Q2-2026 Objective 3 (Validate Technical Infrastructure & Payout Flow).

Dig deeper: https://github.com/sustentus/sustentus/pull/905 · https://help.sustentus.com/changelog/2026-08-28-agent-shell-layout-polish

05_ship/output/release.md

Ship: agent-shell-layout-polish

  • pr: #905 (https://github.com/sustentus/sustentus/pull/905) · merge: authorised — Ready to merge ticked by Jamie (read 2026-08-28, with his /pipeline ship); this commit rides the squash
  • CI: gated — the merge proceeds only on a settled GREEN from ci-status.sh on the head carrying this commit, established after this stage's push; Verify's settled verdict was GREEN on 8a0d7f7
  • technical docs: technical/deployment (PLATFORM_APP_URL joins the agent env catalogue, with the runtime-read caveat and the fail-safe) and technical/applications (the agent header's platform link) — updated in this PR
  • business docs: no business docs impact — no business page describes the assistant yet (pre-existing gap, flagged at Verify as an intake candidate alongside the CLAUDE.md map)
  • release notes: both — ship note + changelog entry (2026-08-28-agent-shell-layout-polish)
  • sent: the ship note rides this merge — .github/workflows/ship-note.yaml reads it from the squash and emails the #product-update channel; nothing sends before the authorised merge
  • close-out: archive the run to apps/docs/archive/pipeline-runs/; the agentic-ui-polish batch stays live in intake — stub 2 (agent-chat-experience-polish) is not yet spun out

Acceptance check (vs spec)

  • Header: UserButton + labelled platform link + theme toggle; tenant name and "Switch tenant" survive — diff-traced (agent) + operator preview pass (verify.md)
  • Both themes across header / conversation / picker / no-workspace — semantic tokens and the shared class-strategy variant (agent); visual pass the operator's (verify.md)
  • Phone: input visible with the keyboard open, send doesn't jump, only the column scrolls — implemented (h-dvh · interactiveWidget: resizes-content · overscroll-contain); the device smoke is the operator's line in verify.md, and the merge is authorised past it by Jamie's gate
  • Large screens: framed card on the muted canvas, header aligned to the same column — operator visual pass (verify.md)
  • PLATFORM_APP_URL catalogued in globalEnv this PR; unset → link absent, nothing else changes — code-verified (agent); setting it on the agentic-interface Vercel project is the recorded owner action
  • proxy.ts and the route surface untouched; no new public route; no new data access — diff-verified (agent)