원클릭으로
hgraf-add-drawer-tab
Add a new tab to the span inspector drawer — TabId, TABS registry, body component, uiStore deep-link, tests.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Add a new tab to the span inspector drawer — TabId, TABS registry, body component, uiStore deep-link, tests.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Understand and safely change heartbeat interval, stuck threshold, and timeout constants — client cadence, server detection math, frontend signal.
Add a new AnnotationKind — proto, storage, ingress RPC, optional downstream delivery to agents, frontend authoring UI, tests.
Add a new agent Capability — proto enum, client advertisement, server ingest, frontend gating in the control UI.
Add a new ControlKind — capability advertisement, client handler, server routing, ack semantics, frontend button, and tests — all the way through.
Add a new drift kind end-to-end. Goldfive owns the detector + enum; harmonograf reflects the kind through the intervention aggregator and the UI timeline.
Script a complex multi-turn FakeLlm scenario — function calls, tool chains, drift events, side-effect hooks, partial responses — for deterministic adk test coverage.
| name | hgraf-add-drawer-tab |
| description | Add a new tab to the span inspector drawer — TabId, TABS registry, body component, uiStore deep-link, tests. |
You need a new panel inside the span inspector drawer (the right-hand slide-out). Existing tabs: Summary, Task, Payload, Timeline, Links, Annotations, Control. This is a narrow recipe — see hgraf-update-frontend-component.md in the same directory for the general frontend-component pattern this specializes.
frontend/src/components/shell/Drawer.tsx:29-46 — TabId union, TABS constant.frontend/src/components/shell/Drawer.tsx:141-187 — DrawerTabs rendering and the switch over tab.frontend/src/state/uiStore.ts to find the drawerRequestedTab / openDrawerOnTrajectory pattern — that's how keyboard shortcuts deep-link to a specific tab.TabIdfrontend/src/components/shell/Drawer.tsx:29:
type TabId =
| 'summary'
| 'task'
| 'payload'
| 'timeline'
| 'links'
| 'annotations'
| 'control'
| 'thinking'; // new
TABSDrawer.tsx:38:
const TABS: { id: TabId; label: string; testId: string }[] = [
// ...existing entries...
{ id: 'thinking', label: 'Thinking', testId: 'inspector-tab-thinking' },
];
The testId must be a unique string; playwright/vitest tests reference it via getByTestId.
Below the other XxxTab components in the same file:
function ThinkingTab({ span, sessionId }: { span: Span; sessionId: string | null }) {
// Use useSessionWatch if you need live updates on the span itself.
// Use useUiStore selectors for UI-only state.
return (
<div className="hg-drawer__body" data-testid="inspector-thinking-body">
{/* ... */}
</div>
);
}
For tabs that need live updates on the selected span (status/attribute mutations), use useSessionWatch(sessionId) and re-read the span via store.spans.getById(span.id) inside an effect — see how CurrentTaskSection does it at Drawer.tsx:189.
For tabs that subscribe to collections, use useReducer((x: number) => x + 1, 0) + store.tasks.subscribe(() => bump()) (the same [, bump] = useReducer pattern at Drawer.tsx:200).
DrawerTabsDrawer.tsx:173:
{tab === 'thinking' && <ThinkingTab span={span} sessionId={sessionId} />}
If you want a keyboard shortcut to open the drawer directly on your tab, extend uiStore.ts with a new action like openDrawerOnThinking(spanId) mirroring the existing openDrawerOnTrajectory (grep in uiStore.ts):
openDrawerOnThinking(spanId: string) {
set({ drawerOpen: true, selectedSpanId: spanId, drawerRequestedTab: 'thinking' });
},
Then in frontend/src/lib/shortcuts.ts add a binding following the thinking-trajectory precedent at shortcuts.ts:272:
{
id: 'thinking-tab',
description: 'Open drawer on Thinking tab',
combo: 'shift+t',
handler: () => {
const s = ui();
const spanId = s.selectedSpanId ?? listAllSpans(s.currentSessionId)[0]?.id;
if (spanId) s.openDrawerOnThinking(spanId);
},
},
Update HelpOverlay in frontend/src/components/shell/HelpOverlay.tsx with the new shortcut if it exists in the help table (it likely lists defaultShortcuts() directly — a test will catch you if it doesn't).
drawerRequestedTabThe drawer already reads drawerRequestedTab on mount (Drawer.tsx:145) and calls consumeDrawerRequestedTab() on the next microtask. Your new TabId just works because the consumer stores whatever string the uiStore handed it.
If your tab has sub-state (like TaskTab has drawerRequestedTaskSubtab), add a parallel field on uiStore and a second consume call. Follow TaskTab's precedent.
frontend/src/__tests__/components/Drawer.test.tsx — drawer renders the new tab button, clicking it swaps the body, and openDrawerOnThinking puts the drawer on the right tab at mount.frontend/src/__tests__/lib/shortcuts.test.ts to fire the keybinding and assert uiStore state.cd frontend && pnpm test -- --run Drawer
cd frontend && pnpm typecheck
cd frontend && pnpm dev # manually verify the tab renders and the shortcut opens it
TABS is rendered in array order. If your new tab belongs in the middle (e.g., next to "Task"), insert at the right index rather than always appending.useState<TabId>(requestedTab ?? 'summary') (Drawer.tsx:147) falls back to 'summary'. If you want your new tab to be the default for certain selections, gate that in the requestedTab ?? expression.sessionId can be null when the drawer opens via a deep link before the session has loaded. Guard every getSessionStore(sessionId) call. See how AnnotationsTab does sessionId && at Drawer.tsx:178.span prop is snapshotted at the drawer's last render. For tabs that need live data, route through useSessionWatch and look up store.spans.getById(span.id) inside the tab body.TABS but not the switch: the compiler won't catch you because TabId is exhaustive in the switch only if you write never fallthrough. Add a test that clicks every tab in TABS and asserts its body test-id is visible.