Per visual feedback the menu was sitting flush against the cursor;
introduce MENTION_MENU_CARET_GAP (10px) so the popover floats roughly
one space-width to the right of the caret.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The board accepted the canonical executionDisposition payload + classifier
but found the visible badges in IssueRow / IssueProperties / Storybook too
noisy on production surfaces. UI-only walkback: keep the data layer, drop
every visible badge.
- Delete IssueDispositionBadge component and its test.
- Drop the badge import, suppression rule, and trailing-slot references in
IssueRow (desktop + mobile). Existing waiting pill, status-icon ring, and
blockerAttention copy remain unchanged.
- Drop the badge IIFE and related imports from IssueProperties so the
Status row only renders the StatusIcon again.
- Prune the badge cases from IssueRow.test (Live, Stalled, dispatchable,
Resuming, explicit-waiting suppression) so the file still type-checks.
- Storybook: remove "Canonical disposition badges" and "IssueRow with
disposition badge inline" sections plus DispositionRowSurface and
dispositionRowFixtures. The rest of Status Language is unchanged.
Kept: executionDisposition on issue payloads, the resolver and its test,
and the PAP-2818 contract doc — future features can consume the field
without re-running the classifier work.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Align CSS max-height with MENTION_MENU_HEIGHT (208) so the rendered
menu fills the layout-reserved space; previously max-h-[200px] clipped
the box 8px short of the JS layout budget, leaving a visible gap above
the caret line on the flipped path.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Drop the dispatchable "Ready" variant from the badge: the only place it ever
rendered was Storybook (IssueRow and IssueProperties already returned null
for it). dispositionCategory now returns null for dispatchable.
- Pull agent_continuable into a discrete "Resuming" category with a muted
indigo treatment so it doesn't share the rose alarm family with Recovery /
Needs attention / Stalled, and surface "Resuming · n/m" on the visible
badge instead of hiding the attempt counter in the tooltip.
- Differentiate human_escalation_required by owner on the visible label:
Needs board / Needs manager / Needs owner / Needs external. Color stays in
the rose family but the actionable bit is no longer hover-only.
- Bump invalid (Stalled) to a solid rose-600 fill so it stands out against
the rest of the rose family at scan distance, satisfying the parent plan's
acceptance criterion that the board can spot a stalled review leaf without
reading the full thread.
- Mirror the IssueRow suppression rule on IssueProperties so the Status row
doesn't double-render the same waiting/recovery signal as the chat-thread
IssueBlockedNotice.
- Add aria-label={tooltip} on the badge so screen readers get the path
detail, not just the category label.
- Storybook: rename and split the disposition matrix to cover all four
escalation owners, both Resuming attempts, and add a populated IssueRow
surface that shows the badge in production layout context (Live, Resuming,
Needs board, Needs manager, Stalled).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Mention menu now sits on the same line as the caret and to the right of
the cursor (tracking each typed character instead of staying glued to
the @ marker). When the menu would overflow below the viewport it flips
above the line so it doesn't cover the line.
Touch handling no longer preventDefaults on touchstart, so the dropdown
scrolls natively on mobile; selection runs only on touchend if the
finger lifted with negligible movement (a tap, not a scroll).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Adds IssueDispositionBadge that maps the canonical disposition to a
small palette of categories (live, ready, waiting, blocked-chain,
recovery, needs attention, stalled). Renders the badge in the inbox
row and on the issue detail status row, suppressing it where the
existing waiting/recovery copy already conveys the same signal so we
don't double up. Storybook gains a section showing every category for
UX review.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The explicit-wait IssueBlockedNotice rendered Target/Owner/Resume as a row
of pills. Drop the Target pill entirely and render the remaining Owner and
Resume entries as a plain dl/dt/dd block — the value matters more than the
chip styling for these long-form details.
The "Jump to questions/confirmation/approval" affordance referenced anchor
ids that did not exist in the DOM (`issue-thread-interaction-{id}` and
`issue-approval-{id}`); the actual anchors are `interaction-{id}` and
`approval-{id}`. Switch to those ids and use a button with
scrollIntoView({behavior:"smooth"}) so the jump actually scrolls to the
target.
Add a small ExplicitWaitingJumpAnchor component that flips the arrow ↑/↓
based on whether the target is above or below the banner, recomputing on
scroll/resize.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The Target/Owner/Resume chips in the explicit-wait IssueBlockedNotice
banner used inline-flex with `truncate max-w-[Nrem]` on inner spans, so
on narrow viewports a single chip (especially Resume's full sentence)
extended past the container edge. Cap each chip at max-w-full and let
the value span wrap on word boundaries; desktop layout is unchanged.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Wraps fenced code blocks in MarkdownBody with a relative container and an
absolute-positioned Copy button (top-right) that copies the block contents
to the clipboard via navigator.clipboard with an execCommand fallback for
non-secure contexts. The button is hidden by default and reveals on hover
or focus, swaps to a check icon and "Copied\!" label for ~1.5s after
clicking, and falls back to "Copy failed" when the clipboard write throws.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Replaces the rose liveness-break banner for issues whose blocker-attention
read model reports `state="covered"` with `reason="explicit_waiting"`.
- IssueBlockedNotice: add a sky-toned banner with Hourglass icon, kind-aware
headline/body copy (plan confirmation, user response, suggested tasks,
board approval, generic fallback), Target/Owner/Resume pill row, a "Jump
to confirmation" anchor, and a sky chip strip for parent-of-leaf chain
waits. Rose banner still wins on tie via the existing precedence.
- StatusIcon: add a sky ring + bottom-right nub for explicit_waiting and
emit `data-blocker-attention-state="explicit_waiting"`. Tooltip is
"Waiting on board/user/agent" or prefixed "Blocked · ..." when blocked.
- IssueRow: add a sky `Waiting · {owner}` pill (collapses to `Waiting` on
mobile) when the row's attention reason is explicit_waiting. Owner label
resolves via new optional agent/user maps wired in by IssuesList & Inbox.
- IssueDetail/IssueChatThread: lift the linkedApprovals query into the chat
tab and pass interactions/approvals/agentMap/userLabelMap/currentUserId
through to the new banner.
- Storybook + tests: extend coveredBlockedMatrix with sky cells, add four
new BlockedNotice fixtures (plan, user response, board approval, chain),
and cover the new states with unit tests on StatusIcon, IssueBlockedNotice,
and IssueRow.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The user reported having to click Jump-to-latest ~10 times before the
viewport actually pinned to the latest comment on long threads (e.g.
PAP-2536). The virtualizer estimates 220px per unmeasured row, so
totalSize is hugely underestimated until rows render and get measured;
a single smooth scroll lands above the real bottom and each subsequent
click walks closer as more rows mount and expand.
- IssueChatThread.handleJumpToLatest now awaits an optional
onRefreshLatestComments before resolving the latest target so any
comment that arrived after the initial load (or was dropped on a
live-update reconnect) is in the loaded set first.
- scrollToLatestCommentWithSettle issues the initial smooth scroll,
then on an 80 ms tick re-issues scrollIntoView({ block: "end" }) on
the latest comment row for up to 4 s, exiting once the row's bottom
matches the scroll container's bottom (within 4 px) and scrollTop +
scrollHeight have been stable for 3 ticks. If the row hasn't been
mounted into the virtualizer buffer, scrollToIndex is used to nudge
the offset until it is.
- IssueDetail destructures `refetch` from the comments useInfiniteQuery
and threads refetchLatestComments through to IssueChatThread.
QA verified on PAP-2720: single-click convergence in 327-407 ms across
5 successive trials, comment-fa353646 lands flush with the viewport
bottom (-0.44 px sub-pixel), stopped-scroll stability holds.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
StatusIcon adds an AlertTriangle overlay on the red-ring blocked variant
when the read model reports recovery_needed; tooltip and aria-label
spell out the liveness break and which leaf is invalid.
IssueBlockedNotice gets a rose container variant with two copy
templates: parent-perspective ("paused at a liveness break in [leaf]")
and leaf-perspective ("productive run that exited without queueing a
continuation"). The sub-row label flips to "Liveness break at" and an
owner pill renders with the next-action verb derived from the
nextActionHint.
IssueRow surfaces "liveness break at {leaf}" in the meta line for
recovery_needed parents so inbox/MyIssues rows show the actionable leaf
without opening the parent.
The status-language Storybook matrix gains the four new state/reason
combinations and parent/leaf-perspective notice variants. Targeted
vitest cases cover both the icon overlay and notice rendering.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The Phase 3 virtualizer (PAP-2645) used useWindowVirtualizer, but on the
real issue page the chat lives inside `<main id="main-content"
overflow-auto>`. Window scroll never moves on desktop, so the virtualizer
only ever mounted the first 7 rows and the chat area went blank past the
first viewport. Detect the actual scroll container at mount via DOM walk
and rekey the inner virtualizer to use element-mode observers/scroll-fn
when an overflow ancestor exists; fall back to window mode for mobile and
the auth-free perf fixture, where the document scrolls.
Adds a regression test that mounts IssueChatThread inside an overflow-auto
ancestor and asserts jump-to-latest drives that element's scrollTo, not
window.scrollTo.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
If the issue reply composer would post without any assignee, the first
Send press now surfaces a warning toast instead of submitting. A second
press confirms the user wants to proceed unassigned, and changing the
assignee resets the confirmation.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add @tanstack/react-virtual and gate IssueChatThread on a 150-row
threshold. Thread shorter than the threshold still uses the direct
render path; longer threads window the merged feed via
useWindowVirtualizer with dynamic measurement and overscan, so the
document scroll continues to represent the full conversation height
without introducing a nested scrollbar.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
UX feedback split the badge by role: on the source issue the linked pill
describes a state ("Under review"), while on the productivity-review issue
itself the static pill identifies the kind of issue ("Productivity review").
Tooltip and aria-label still surface the formal name on hover.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Adds a productivityReview lookup to single-issue and list responses, plus
a ProductivityReviewBadge component that surfaces the open productivity
review on the source issue's header (with trigger/no-comment-streak
detail in the tooltip), and a compact eye glyph next to the StatusIcon
on IssueRow so operators can spot yellow tasks in any list. The review
issue itself shows a static "Productivity review" pill in the header
when its originKind matches.
Also extends the shared IssueOriginKind union with the recovery-side
kinds (harness_liveness_escalation, issue_productivity_review,
stranded_issue_recovery) so UI checks against issue.originKind type-check.
Wires the new helper through GET /issues/:id, GET /companies/:id/issues
(via issueService.list), and GET /issues/:id/heartbeat-context. Adds a
status-language Storybook story matrix for UX review.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Reviewer follow-up — pills sat slightly low after the baseline-to-middle
switch. position: relative + top: -1px raises them to optically center
with the surrounding cap height.
Inline-flex baseline behavior is browser-dependent, which made skill/agent/issue
chips in MarkdownBody and the MDXEditor hang below the surrounding text. Switch
to vertical-align: middle and drop the vertical padding so the pill sits
centered on the line with the prose around it.
Adds a skill-chip line to the MarkdownBody storybook fixture so this regresses
loudly next time.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Wrap the variables editor in a single rounded container so the
"Variables" header and the per-variable cards share one border with
internal dividers, instead of stacking as two visually separate
containers when expanded.
Add a help button to the variable instructions hint that opens a modal
explaining custom {{variable_name}} placeholders and the built-in
{{date}} and {{timestamp}} variables.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip's board UI relies on compact selectors for frequent issue
and agent edits.
> - Inline selectors often live inside larger keyboard-aware surfaces
such as composers and popovers.
> - Arrow, enter, tab, and escape keys handled by the selector should
not leak to parent document shortcuts.
> - Stale company selection should also stay hidden until the company
list confirms it is valid.
> - This pull request tightens inline selector keyboard handling and
adds regression coverage for stale company bootstrap behavior.
> - The benefit is fewer accidental parent interactions and safer
company-scoped UI initialization.
## What Changed
- Added a stable empty `recentOptionIds` default so selector filtering
does not get a new array every render.
- Mirrored highlighted option state into a ref so Enter/Tab commits the
current highlighted option reliably after keyboard navigation.
- Stopped propagation for selector-owned navigation/commit/escape keys.
- Added jsdom regressions for inline selector keyboard handling and
CompanyProvider stale selection behavior.
## Verification
- `pnpm exec vitest run ui/src/components/InlineEntitySelector.test.tsx
ui/src/context/CompanyContext.test.tsx`
- Targeted selector and CompanyProvider tests pass cleanly without React
`act(...)` warnings.
- Screenshots not attached: this is keyboard/state behavior covered by
component tests.
## Risks
- Low risk: changes are scoped to inline selector key handling and
tests. The main behavior shift is intentionally preventing handled
selector keys from reaching parent listeners.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent based on GPT-5, tool-enabled local
repository and shell access, Paperclip heartbeat context.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip operators need fast control over the agents running their
company.
> - The sidebar is the persistent place operators scan agent state while
navigating the board UI.
> - Agent pause and resume already exist as control-plane actions, but
sidebar users had to navigate away to use them.
> - This pull request adds a compact per-agent action menu in the
sidebar.
> - It keeps edit, pause, and resume close to the visible agent list
while preserving existing navigation behavior.
> - The benefit is faster operator intervention when an agent needs to
be paused or restarted.
## What Changed
- Refactored sidebar agent rows into a small item component with a
hover/focus action menu.
- Added edit, pause, and resume actions using existing agent API calls
and cache invalidation keys.
- Added success/error toasts for pause and resume mutations.
- Tracked pause/resume pending state per agent so one active mutation
does not disable every sidebar row.
- Disabled direct sidebar resume for budget-paused agents and labeled
that state clearly.
- Added jsdom coverage for active-agent pause, paused-agent resume,
per-agent pending state, and budget-paused resume protection.
- Added visual review artifacts for the default row and opened action
menu:
- [Sidebar row](docs/pr-screenshots/pr-4616/sidebar-agent-row.png)
- [Sidebar action
menu](docs/pr-screenshots/pr-4616/sidebar-agent-actions.png)
## Verification
- `pnpm exec vitest run ui/src/components/SidebarAgents.test.tsx`
- `pnpm --filter @paperclipai/server prepare:ui-dist`
- Browser screenshot pass against a temporary local trusted instance at
`http://127.0.0.1:3102` using Playwright.
## Risks
- Low risk: UI-only addition using existing agent pause/resume
endpoints. The main risk is layout crowding for very narrow sidebars,
mitigated by the icon-only trigger and existing truncation.
- Budget-paused agents now require a non-sidebar path to resume, which
is intentional to avoid accidentally restarting agents stopped by budget
enforcement.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent based on GPT-5, tool-enabled local
repository and shell/browser access, Paperclip heartbeat context.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>