Skip to content

Conversation

@gus-bn
Copy link

@gus-bn gus-bn commented Dec 4, 2025

Problem

Between version 0.0.54 and the current version, stack trace output was significantly simplified to reduce noise. While this simplification had good intentions, it removed critical HTML element hierarchy information that AI coding agents need to effectively understand and modify DOM structures.

What Changed Between Versions

Version 0.0.54 output:

<span class="flex items-center">
  Status
  <div ...>
</span>

## Code Location:
  at span in /app/src/components/DataView.tsx
  at th in /app/src/components/DataView.tsx
  at tr in /app/src/components/DataView.tsx
  at thead in /app/src/components/DataView.tsx
  at table in /app/src/components/DataView.tsx
  at div in /app/src/components/DataView.tsx
  at DataView in /app/src/App.tsx
  at Layout in /app/src/App.tsx
  at BrowserRouter in /app/src/App.tsx
  at AuthProvider in /app/src/state/auth.tsx
  at ThemeProvider in /app/src/state/theme.tsx
  at QueryClientProvider in /app/src/App.tsx
  at App in /app/src/main.tsx

Current version output:

<span class="flex items-center">
  Status
  <div ...>
</span>
  in DataView (at //localhost:5173/src/components/DataView.tsx)
  in AuthProvider (at //localhost:5173/src/state/auth.tsx)
  in ThemeProvider (at //localhost:5173/src/state/theme.tsx)

Why This Is Problematic for AI Agents

The current version only shows 3 levels and jumps straight to React component names, completely skipping the HTML element hierarchy. This causes several issues:

  1. Lost DOM Context: AI agents can't see that the span is inside a th (table header cell), which is in a tr (table row), which is in a thead (table header), which is in a table. This structural information is critical for understanding how to modify the element correctly.

  2. Insufficient Depth: Only 3 levels means the agent might not even see the immediate parent component, let alone the layout structure.

  3. Can't Distinguish Element Types: Without seeing th vs td, agents can't tell if they're modifying a header cell or data cell. Without seeing button vs a, they can't tell if it's a button or link. This leads to incorrect modifications.

Real-World Impact

When an AI agent is asked to "make this bold" or "change the styling" on a table header, it needs to know:

  • ✅ It's a span inside a th (table header) - style accordingly
  • ❌ Current version: Just sees "DataView component" - might apply wrong styling

When asked to "fix this form input":

  • ✅ Needs to see: inputlabeldivformFormComponent
  • ❌ Current version: Jumps straight to component, misses the form context

Solution

This PR restores the HTML element hierarchy from v0.0.54 while keeping the improvements from recent versions. It provides a middle ground between the overwhelming 20+ level stack traces and the current oversimplified 3-level output.

Changes Made

1. Restored Manual Fiber Traversal (src/context.ts)

Replaced getOwnerStack() from bippy (which only returns React component owners) with manual fiber tree traversal that includes both HTML elements and React components:

// Changed from:
const stack = await getOwnerStack(fiber);

// To:
traverseFiber(
  fiber,
  (currentFiber) => {
    const displayName = isHostFiber(currentFiber)
      ? typeof currentFiber.type === "string"
        ? currentFiber.type  // HTML elements: 'span', 'div', 'table', 'button', etc.
        : null
      : getDisplayName(currentFiber);  // React components: 'DataView', 'Layout', etc.

    if (displayName && !checkIsInternalComponentName(displayName)) {
      unresolvedStack.push({
        name: displayName,
        sourcePromise: getSource(currentFiber),
      });
    }
  },
  true,
);

Key insight: isHostFiber() identifies HTML elements (DOM nodes) vs isCompositeFiber() which identifies React components. The old getOwnerStack() only returned composite fibers, losing the HTML structure.

2. Updated Stack Frame Structure

interface StackFrame {
  name: string;           // Element name (HTML tag or React component)
  source: FiberSource | null;  // File location information
}

This preserves both the element/component name AND its source location.

3. Restored Classic Stack Format

Changed from the confusing nested format back to the clearer v0.0.54 style:

// Before: in ComponentName (at file)
`\n  in ${frame.functionName} (at ${normalizeFileName(frame.fileName)})`

// After: at elementName in file
`\n  at ${frame.name} in ${normalizeFileName(frame.source.fileName)}`

The at X in file.tsx format is:

  • More concise
  • Easier to parse for AI agents
  • Matches familiar error stack trace format
  • Shows clear hierarchy reading top-to-bottom

4. Increased Default Stack Depth

Changed maxLines default from 3 to 10, because:

  • 3 levels was too shallow to provide adequate context
  • 10 levels captures typical application structures without overwhelming
  • Still much better than v0.0.54's unlimited output (often 20+ levels)
  • Remains configurable via options if users want different depth

5. Updated Dependent Code (src/core.tsx)

Fixed references to use new StackFrame structure:

// Updated property access
if (frame.source && isSourceFile(frame.source.fileName)) {
  setSelectionFilePath(normalizeFileName(frame.source.fileName));
  setSelectionLineNumber(frame.source.lineNumber);
}

Result

New Output Format (This PR)

Now provides both HTML structure AND React component hierarchy:

<span class="flex items-center">
  Status
  <div ...>
</span>
  at span in /app/src/components/DataView.tsx
  at th in /app/src/components/DataView.tsx
  at tr in /app/src/components/DataView.tsx
  at thead in /app/src/components/DataView.tsx
  at table in /app/src/components/DataView.tsx
  at div in /app/src/components/DataView.tsx
  at DataView in /app/src/App.tsx
  at Layout in /app/src/components/Layout.tsx
  at BrowserRouter in /app/src/App.tsx
  at AuthProvider in /app/src/state/auth.tsx

Benefits

AI agents see complete HTML structure - Understand element relationships (table header, form input, button, etc.)

React component hierarchy preserved - Still shows component ownership chain

Optimal depth - 10 levels provides enough context without overwhelming (vs 3 too shallow, unlimited too verbose)

Filters noise - Still removes internal React/Next.js components (LoadableComponent, ErrorBoundary, etc.)

Better format - Clearer "at element in file" syntax

Configurable - Users can override maxLines if needed

Comparison Table

Version HTML Elements React Components Default Depth Format AI Agent Effectiveness
v0.0.54 ✅ All ✅ All Unlimited (~20+) at X in file ⚠️ Too much info
Current ❌ None ✅ Only components 3 in X (at file) ❌ Not enough context
This PR ✅ All ✅ All 10 at X in file ✅ Perfect balance

Technical Implementation

Why Manual Traversal?

getOwnerStack() from bippy only returns React component "owners" - components that created other components. It specifically excludes host fibers (HTML elements) because from React's perspective, DOM nodes aren't "owners."

Manual traverseFiber() captures both:

  • Host fibers: HTML elements detected via isHostFiber(fiber)
  • Composite fibers: React components detected via isCompositeFiber(fiber)

Why Filter Internal Components?

Still filters out React/Next.js internal components:

  • InnerLayoutRouter, RedirectBoundary, LoadingBoundary
  • LoadableComponent, ErrorBoundary
  • body, html (Next.js server components)
  • Components starting with _
  • Provider/Context internal wrappers

These add noise without providing useful context for modifications.

Performance

  • Similar performance to old version (both traverse fiber tree)
  • Source resolution happens in parallel via Promise.all()
  • No additional re-renders or side effects
  • Filtering reduces unnecessary work

Testing

✅ Built successfully with TypeScript
✅ Tested with @react-grab/claude-code integration
✅ Tested with @react-grab/cursor integration
✅ Tested with complex nested structures (tables, forms, multi-level layouts)
✅ Tested with both Vite and Next.js projects
✅ All package builds pass

Breaking Changes

None. This is fully backward compatible:

  • Function signatures unchanged
  • Return types compatible
  • Options like maxLines still work
  • Existing integrations require no updates
  • Only visible change is improved output format

Users can still override maxLines if they prefer fewer/more levels:

const snippet = await generateSnippet([element], { maxLines: 5 });

Files Changed

  • packages/react-grab/src/context.ts - Restored fiber traversal, updated stack formatting
  • packages/react-grab/src/core.tsx - Updated StackFrame property access

Migration Guide

No migration needed - this is a drop-in improvement. If you're using react-grab, you'll automatically get better stack traces after updating.

Related Context

This addresses feedback from users using react-grab with AI coding agents (Claude Code, Cursor, etc.) who found that the simplified stack traces didn't provide enough context for the agents to make accurate code modifications, particularly when working with:

  • Complex table structures
  • Nested form layouts
  • Multi-level navigation components
  • Conditional rendering with multiple wrapper elements

The lack of HTML element visibility meant agents couldn't distinguish between different types of containers, leading to incorrect or suboptimal code changes.


Summary: This PR restores the best aspects of v0.0.54's detailed stack traces while maintaining the improved signal-to-noise ratio of recent versions. It provides the "goldilocks" amount of information - not too much, not too little - specifically optimized for AI agent workflows.

- Migrate from getOwnerStack to traverseFiber and getSource APIs
- Update StackFrame to use FiberSource instead of direct properties
- Increase default maxContextLines from 3 to 10
- Add tmp directory to .gitignore
- Fix property access in core.tsx for new frame structure
@vercel
Copy link

vercel bot commented Dec 4, 2025

@gus-bn is attempting to deploy a commit to the Million Team on Vercel.

A member of the Team first needs to authorize it.

- Add formatFileName function to generate clickable dev server URLs
- Improve developer experience by making file links more accessible in error contexts
- Add @types/node to devDependencies for better TypeScript support
- Update tsconfig.json to include Node.js types
- Improve developer experience with enhanced type checking
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